What I'm trying to do is generate a keypair within javascript, and use these with encrypting in PHP and then decrypt with JS.
I'm having two problems within the code attached
It wont reload the private key from armored text block
And it wont decrypt what PHP has encrypted
Both throw the error DOMException, instead of a useful error.
So this is my code...
PHP/JAVASCRIPT
<?php
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Crypt\RSA;
if ($_POST) {
$public=$_POST['public'];
$data='some text to encrypt';
$key = PublicKeyLoader::load($public);
$key = $key->withPadding(RSA::ENCRYPTION_OAEP);
$encoded=base64_encode($key->encrypt($data));
header('Content-Type: application/json');
echo json_encode(array('encrypted'=>$encoded));
exit;
}
?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
<script>
jQuery(document).ready(function($) {
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
function str2ab(str) {
var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
var bufView = new Uint16Array(buf);
for (var i=0, strLen=str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function importPrivateKey(pem) {
// fetch the part of the PEM string between header and footer
const pemHeader = "-----BEGIN PRIVATE KEY-----\n";
const pemFooter = "\n-----END PRIVATE KEY-----";
const pemContents = pem.substring(pemHeader.length, pem.length - pemFooter.length);
// base64 decode the string to get the binary data
const binaryDerString = window.atob(pemContents);
// convert from a binary string to an ArrayBuffer
const binaryDer = str2ab(binaryDerString);
return window.crypto.subtle.importKey(
"pkcs8",
binaryDer,
{
name: "RSA-OAEP",
modulusLength: 1024,
publicExponent: new Uint8Array([1, 0, 1]),
hash: {name: "SHA-256"}
},
true,
["decrypt"]
);
}
(async() => {
let keyPair = await window.crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 1024,
publicExponent: new Uint8Array([1, 0, 1]),
hash: {name: "SHA-256"}
},
true,
["encrypt", "decrypt"]
);
var exported=await window.crypto.subtle.exportKey("pkcs8",keyPair.privateKey);
var exportedAsString = ab2str(exported);
var exportedAsBase64 = window.btoa(exportedAsString);
var private = `-----BEGIN PRIVATE KEY-----\n${exportedAsBase64}\n-----END PRIVATE KEY-----`;
var exported = await window.crypto.subtle.exportKey(
"spki",
keyPair.publicKey
);
var exportedAsString = ab2str(exported);
var exportedAsBase64 = window.btoa(exportedAsString);
var public = `-----BEGIN PUBLIC KEY-----\n${exportedAsBase64}\n-----END PUBLIC KEY-----`;
console.log(public);
console.log(private);
$.ajax({
url:window.location,
type:'POST',
data:{
public:public
},
success:function(data) {
(async() => {
console.log('*ENCRYPTED BY PHP*',data.encrypted);
// HELP!!! NEED TO BE ABLE TO RELOAD THE KEY FROM ARMORED STRING
var key=await importPrivateKey(private); // Error - Uncaught (in promise) DOMException
var buffer=str2ab(window.atob(data.encrypted));
// HELP!!! WONT DECRYPT WHAT PHP ENCODED USING THE PUBLIC KEY
var decrypted=await window.crypto.subtle.decrypt({name:"RSA-OAEP"},key,buffer);
console.log('DECRYPTED',decrypted);
})();
}
});
})();
});
</script>
The bug is in the str2ab() function, which uses a Uint16Array instead of a Uint8Array.
If this is fixed, the private key can be imported and the ciphertext generated with the PHP code can be decrypted:
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
function str2ab(str) {
// Fix: Don't double the size
var buf = new ArrayBuffer(str.length);
// Fix: Apply a Uint8Array!
var bufView = new Uint8Array(buf);
for (var i=0, strLen=str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function importPrivateKey(pem) {
const pemHeader = "-----BEGIN PRIVATE KEY-----\n";
const pemFooter = "\n-----END PRIVATE KEY-----";
const pemContents = pem.substring(pemHeader.length, pem.length - pemFooter.length);
const binaryDerString = window.atob(pemContents);
const binaryDer = str2ab(binaryDerString);
return window.crypto.subtle.importKey(
"pkcs8",
binaryDer,
{
name: "RSA-OAEP",
modulusLength: 1024,
publicExponent: new Uint8Array([1, 0, 1]),
hash: {name: "SHA-256"}
},
true,
["decrypt"]
);
}
(async function() {
// Apply the private key from key pair generated with the posted JavaScript code
var privateKey =
`-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKeQUdBu3zTX6QyfGfRWYxWWOnxd2xssTOIu6XczDByQEMfBbpQO9iM3u/Mn84zZFPFNvOKUNxcnftmrPiqUO9fBI2aAh77d2m65FBGsm4k/oUPzMNORGaDdBY4gg8FPMKo60kqBaMXAwzF8I4EUS/ot2fkBzSL0BGXT9o1NaO8bAgMBAAECgYAO2OPW8ywF86ervaFAHDN1YzVVdb+HXdqGJB/9tuE42q8R9BrHNbgrkLGvrveOoGGRrBCzhuyGubIsuVat0SqoI6qEnB9uahaIBfF5FZ7+bNW5OfkgerUUYP1S1MGFxUqINnUY1YHITmo6pUKHsiJtP7sihnCT6uEx8LqVNf1quQJBANs+VCZVUDq6eMy3E/u03HiAB8cyqLVMVQ4cLyoiWmFlnEFzZwMd20ZMjtcxICiizW3dlDvyxWYKH93irL0JyM0CQQDDp/VFsh83vKICVvM9IZHwE/Z8vZA3eTkGbWmgnr6qaxqge3FU02kUvIHHlvLmXYIt30lTq0Rn+Lz+TGV/jDeHAkBHYSaSiGojhLx5og1+gKbbEIv3vbWRuTVj76cnZ6HXXfaelIzwRdMzMw+6XgMjV8XcRCzTy7ma/Cbd3cPxk/LtAkEAwkehMVexz/KrHI+icG1JMI9iDnNdJPhmO4+hdzCqOyanBfwNiSF0Encslze4ci8f+NTjRwWlo2hGomzRzFk7OQJAPPd/o0azkg9nF+JxLiz7hF+/6MLVZgIfw04u05ANtOSVVQP4UTmJ/tNAe3OBUQVlRQAJ1m3jzUlir0ACPypC1Q==
-----END PRIVATE KEY-----`;
// Use the ciphertext generated with the PHP code
var ciphertext = 'a8gEZ6/DymB8dTGPytQPNS8QiYFuUULK+/c0vtie1l722isC0Z/jSeC2ytA6MjVUuTdq7sPuNW850gEZ2XvKujLQzl9sjJ8pcsxznBzMK8v03YJCTBr2lbfHpEEtuSLaAR2UbovXDoCyIIvOnMjqlIS3Ug2PG4hALThn/aAUwE0=';
var key = await importPrivateKey(privateKey);
var decryptedBuffer = str2ab(window.atob(ciphertext));
var decrypted = await window.crypto.subtle.decrypt(
{name:"RSA-OAEP"},
key,
decryptedBuffer
);
console.log(ab2str(decrypted)); // some text to encrypt
})();
Here, the posted JavaScript code was applied to generate an RSA key pair. The public key:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnkFHQbt801+kMnxn0VmMVljp8XdsbLEziLul3MwwckBDHwW6UDvYjN7vzJ/OM2RTxTbzilDcXJ37Zqz4qlDvXwSNmgIe+3dpuuRQRrJuJP6FD8zDTkRmg3QWOIIPBTzCqOtJKgWjFwMMxfCOBFEv6Ldn5Ac0i9ARl0/aNTWjvGwIDAQAB
-----END PUBLIC KEY-----
was used to perform the encryption with the posted PHP code and the private key is applied in the code above for decryption.
Related
This is the algorithm for signing the data in C# using a private key from a certificate that is used from both me and the client in order to define an unique key to identify the user:
X509Certificate2 keyStore = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + "Certifikatat\\" + certPath, certPass, X509KeyStorageFlags.Exportable);
RSA privateKey = keyStore.GetRSAPrivateKey();
byte[] iicSignature = privateKey.SignData(Encoding.ASCII.GetBytes("K31418036C|2022-5-16 13:30:41|406|st271ir481|al492py609|zz463gy579|340"), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
byte[] iic = ((HashAlgorithm)CryptoConfig,CreateFromName("MD5")).ComputeHash(iicSignature);
I then pass the private key to my Javascript using Bouncy Castle:
X509Certificate2 keyStore = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + "Certifikatat\\" + certPath, certPass, X509KeyStorageFlags.Exportable);
RSA privateKey = keyStore.GetRSAPrivateKey();
var eky = DotNetUtilities.GetRsaKeyPair(privateKey);
Pkcs8Generator pkcs8Gen = new Pkcs8Generator(eky.Private);
Org.BouncyCastle.Utilities.IO.Pem.PemObject pkcs8 = pkcs8Gen.Generate();
PemWriter pemWriter = new PemWriter(new StringWriter());
pemWriter.WriteObject(pkcs8);
pemWriter.Writer.Flush();
return pemWriter.Writer.ToString();
This one is the algorithm used in Javascript:
window.crypto.subtle.importKey(
"pkcs8",
pemToArrayBuffer(pkcs8Pem), {
name: "RSASSA-PKCS1-v1_5",
hash: {
name: "SHA-256"
},
},
false, ["sign"]
)
.then(function(privateKey) {
console.log(privateKey);
// Sign: RSA with SHA256 and PKCS#1 v1.5 padding
window.crypto.subtle.sign({
name: "RSASSA-PKCS1-v1_5",
},
privateKey,
new TextEncoder().encode("K31418036C|2022-5-16 13:30:41|406|st271ir481|al492py609|zz463gy579|340")
)
.then(function(signature) {
var iic = md5(signature);
console.log(ab2b64(signature));
})
.catch(function(err) {
console.error(err);
});
})
.catch(function(err) {
console.error(err);
});
function ab2b64(arrayBuffer) {
return window.btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));
}
function removeLines(str) {
str = str.replace("\r", "");
return str.replace("\n", "");
}
function base64ToArrayBuffer(b64) {
var byteString = atob(b64);
var byteArray = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
byteArray[i] = byteString.charCodeAt(i);
}
return byteArray;
}
function pemToArrayBuffer(pem) {
var b64Lines = removeLines(pem);
var b64Prefix = b64Lines.replace('-----BEGIN PRIVATE KEY-----', '');
var b64Final = b64Prefix.replace('-----END PRIVATE KEY-----', '');
return base64ToArrayBuffer(b64Final);
}
The signatures returned are different for some reason. I need them to be the same or else it's all pointless because the client won't be authenticated.
The results are as follow:
C#:
57CF663ACBEDE6305309682BA7261412
Javascript:
c099d176dcd95c59d748d6066dcd462e
I had to convert my signature to base64 and then encode it with atob() after that i needed this md5 library to hash the data and then use .toUpperCase() to reproduce the correct result.
The complete code looks like this:
md5(atob(ab2b64(signature))).toUpperCase();
Now i get the same result from both C# and JS.
I want to build a function that sign a SHA512 HMAC with a Private key with a password to open that file. I have done that with Javascript but I'm not able to do it in JAVA. here is my code in Javascript:-
const fs = require('fs')
const crypto = require('crypto');
const getSignatureByInput = (input, crt) => {
let privatePem = fs.readFileSync(crt);
let key = privatePem.toString('ascii');
let sign = crypto.createSign('RSA-SHA512');
sign.write(input)
let signature = sign.sign({ key, passphrase: 'Password' }, 'hex')
return signature;
}
// Usage
let hamc = crypto.createHmac("sha512", '12345678-73a8-414c-9832-fcd4b2d9b362').update(
JSON.stringify({
"body": {
"Id": "123"
}
})
).digest("hex");
console.log(hamc)
let signatureSignedByPrivateKey = getSignatureByInput(hamc,'./private.key');
console.log(signatureSignedByPrivateKey)
and the below is the example of my private key:-
-----BEGIN ENCRYPTED PRIVATE KEY-----
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
XfJTekUHHwFnpKVang==
-----END ENCRYPTED PRIVATE KEY-----
Appreciate your support!
Thanks in advance...
I found a way to solve this thanks #pra-a for answering a question posted earlier Link here.
I will share my solution below.
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.*;
import java.security.spec.*;
public class Main {
public static final String PRIVATE_KEY="./private.der";
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
// write your code here
//get the private key
File file = new File(PRIVATE_KEY);
FileInputStream fis = new FileInputStream(file);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) file.length()];
dis.readFully(keyBytes);
dis.close();
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPrivateKey privKey = (RSAPrivateKey) kf.generatePrivate(spec);
Mac sha512Hmac;
String result = "";
final String key = "'12345678-73a8-414c-9832-fcd4b2d9b362'";
String signature = "";
String body = "{\n" +
" \"body\": {\n" +
" \"Id\": \"123\"\n" +
" }\n" +
"}";
try {
final String HMAC_SHA512 = "HmacSHA512";
final byte[] byteKey = key.getBytes(StandardCharsets.UTF_8);
sha512Hmac = Mac.getInstance(HMAC_SHA512);
SecretKeySpec keySpec = new SecretKeySpec(byteKey, HMAC_SHA512);
sha512Hmac.init(keySpec);
byte[] macData = sha512Hmac.doFinal(body.getBytes(StandardCharsets.UTF_8));
// Can either base64 encode or put it right into hex
//result = Base64.getEncoder().encodeToString(macData);
result = bytesToHex(macData);
signature = sign(result, privKey);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
// Put any cleanup here
System.out.println("Done");
}
}
private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
public static PrivateKey readPrivateKeyII(String filename) throws Exception{
InputStream in = new FileInputStream(filename);
byte[] privateKeydata = new byte[in.available()];
in.read(privateKeydata);
in.close();
KeyFactory privateKeyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(privateKeydata);
PrivateKey privateKey = privateKeyFactory.generatePrivate(encodedKeySpec);
return privateKey;
}
public static String sign(String plainText, PrivateKey privateKey) throws Exception {
Signature privateSignature = Signature.getInstance("SHA512withRSA");
privateSignature.initSign(privateKey);
privateSignature.update(plainText.getBytes(StandardCharsets.UTF_8));
byte[] signature = privateSignature.sign();
return bytesToHex(signature);
}
}
Trying to use the SubtleCrypto Web API to generate a public/private key pair, then use that keypair for encryption from a user inputting the public key, but importKey() keeps giving result as undefine. For public/private key generation, I have the following:
window.crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048, // can be 1024, 2048 or 4096
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: {name: "SHA-256"} // or SHA-512
},
true,
["encrypt", "decrypt"]
).then(function(keyPair) {
console.log(keyPair);
window.crypto.subtle.exportKey(
"pkcs8",
keyPair.privateKey
).then(function(exportedPrivateKey) {
// converting exported private key to PEM format
var pem = toPem(exportedPrivateKey);
console.log(pem);
}).catch(function(err) {
console.log(err);
});
window.crypto.subtle.exportKey(
"spki",
keyPair.publicKey
).then(function(exportedPublicKey) {
// converting exported private key to PEM format
var pem = toPemP(exportedPublicKey);
console.log(pem);
let resultingKey = importPublicKey(pem)
console.log('keyPairPublic: ', keyPair.publicKey)
console.log('resultingKey: ', resultingKey)
}).catch(function(err) {
console.log(err);
});
});
the toPem() and toPemP() functions are as follows:
function toPem(privateKey) {
var b64 = addNewLines(arrayBufferToBase64(privateKey));
var pem = "-----BEGIN PRIVATE KEY-----\n" + b64 + "-----END PRIVATE KEY-----";
return pem;
}
function toPemP(publicKey) {
var b64 = addNewLines(arrayBufferToBase64(publicKey));
var pem = "-----BEGIN PUBLIC KEY-----\n" + b64 + "-----END PUBLIC KEY-----";
return pem;
}
function arrayBufferToBase64(arrayBuffer) {
console.log('arrayBuffer', arrayBuffer);
var byteArray = new Uint8Array(arrayBuffer);
var byteString = '';
for(var i=0; i < byteArray.byteLength; i++) {
byteString += String.fromCharCode(byteArray[i]);
}
var b64 = window.btoa(byteString);
return b64;
}
function addNewLines(str) {
var finalString = '';
while(str.length > 0) {
finalString += str.substring(0, 64) + '\n';
str = str.substring(64);
}
return finalString;
}
The importKey() function is as follows:
function importPublicKey(pem) {
// base64 decode the string to get the binary data
// fetch the part of the PEM string between header and footer
const pemHeader = "-----BEGIN PUBLIC KEY-----";
const pemFooter = "-----END PUBLIC KEY-----";
let pemContents = pem.substring(pemHeader.length, pem.length - pemFooter.length)
pemContents = pemContents.replace(/\s/g, '');
console.log('pemContents:', pemContents)
let binaryDerString = window.atob(pemContents);
// convert from a binary string to an ArrayBuffer
const binaryDer = str2ab(binaryDerString);
console.log('binaryDer', binaryDer)
window.crypto.subtle.importKey(
"spki",
binaryDer,
{
name: "RSA-OAEP",
hash: { name: "SHA-256" }
},
true,
["encrypt"]
).then(function(result) {
return result;
}).catch(function(err) {
console.log('err: ', err);
})
}
Where str2ab() is as follows:
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
All of the above functions were from the SubtleCrypto documentation. When I run the above, I get the following:
I'm not sure why it comes out as undefined, but I can notice that arrayBuffer and binaryDer don't have the exact same size. Don't know if it means anything and not sure how to fix this, any help is appreciated!
I am currently implementing the RSA-OAEP encryption on Javascript and decryption at Java.
My javascript code has the following
function stringToArrayBuffer(str){
var buf = new ArrayBuffer(str.length);
var bufView = new Uint8Array(buf);
for (var i=0, strLen=str.length; i<strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function arrayBufferToString(str){
var byteArray = new Uint8Array(str);
var byteString = '';
for(var i=0; i < byteArray.byteLength; i++) {
byteString += String.fromCodePoint(byteArray[i]);
}
return byteString;
}
function encryptDataWithPublicKey(data, key) {
data = stringToArrayBuffer(data);
return window.crypto.subtle.encrypt(
{
name: "RSA-OAEP",
//label: Uint8Array([...]) //optional
},
key, //from generateKey or importKey above
data //ArrayBuffer of data you want to encrypt
);
}
var pem = Config.encryption.publicKey;
// fetch the part of the PEM string between header and footer
const pemHeader = "-----BEGIN PUBLIC KEY-----";
const pemFooter = "-----END PUBLIC KEY-----";
const pemContents = pem.substring(pemHeader.length, pem.length - pemFooter.length);
// base64 decode the string to get the binary data
const binaryDerString = window.atob(pemContents);
// convert from a binary string to an ArrayBuffer
const binaryDer = stringToArrayBuffer(binaryDerString);
window.crypto.subtle.importKey(
"spki",
binaryDer,
{
name: "RSA-OAEP",
hash: { name: "SHA-256" }
},
true,
["encrypt"]
).then(function (publicKey) {
encryptDataWithPublicKey(text, publicKey).then((result) => {
var rdata = arrayBufferToString(result);
resolve(rdata);
});
}).catch(function (err) {
console.log(err);
reject(err);
});
I also have a Java function to decrypt the text. Assume "rsaOaepCipherText" is a string text.
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(), oaepParams);
return new String(cipher.doFinal(Base64.decodeBase64(rsaOaepCipherText)), "UTF-8");
However I keep getting decryption error on Java, and currently stuck at this portion, is there any error I have done on my encryption on Javascript?
Ok found it. I forgot to include btoa on the string before send to backend.
it should be
encryptDataWithPublicKey(text, publicKey).then((result) => {
var rdata = arrayBufferToString(result);
var rResult = window.btoa(rdata);
resolve(rResult);
});
I am trying to use Java to sign a message on the server side, while verify the signature on the client side, but failed! My steps are as follows:
Generate RSA key pair:
openssl genrsa -out privatekey.pem 2048
openssl pkcs8 -topk8 -inform PEM -outform DER -in privatekey.pem -out private_key.der -nocrypt
openssl rsa -in privatekey.pem -pubout -outform DER -out pubic_key.der
Thus I got pubic_key.der and private_key.der two files.
Using java to load these two files and generate signature.
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import org.apache.commons.codec.binary.Base64;
import com.nimbusds.jose.jwk.*;
public class SignatureTest {
public static KeyPair generateSignKeyPair() throws Exception {
byte[] privBytes = Files.readAllBytes(Paths.get("testFiles/private_key.der"));
byte[] pubBytes = Files.readAllBytes(Paths.get("testFiles/public_key.der"));
// private key
KeySpec keySpec = new PKCS8EncodedKeySpec(privBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
//public key
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(pubBytes);
PublicKey publicKey = keyFactory.generatePublic(X509publicKey);
return new KeyPair(publicKey, privateKey);
}
public static byte[] generateSignature(PrivateKey signPrivateKey, byte[] data) throws Exception {
Signature dsa = Signature.getInstance("SHA256withRSA");
dsa.initSign(signPrivateKey);
dsa.update(data);
return dsa.sign();
}
public static boolean verifySignature(PublicKey publicKey, byte[] data, byte[] sig) throws Exception {
Signature dsa = Signature.getInstance("SHA256withRSA");
dsa.initVerify(publicKey);
dsa.update(data);
return dsa.verify(sig);
}
private static String getPEMPrivateKeyFromDER(PrivateKey privateKey) {
Base64 base64 = new Base64();
String begin = "-----BEGIN PRIVATE KEY-----";
String end = "-----END PRIVATE KEY-----";
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
String key = new String(base64.encode(pkcs8EncodedKeySpec.getEncoded()));
return begin + "\n" + key + "\n" + end;
}
private static String getPEMPublicKeyFromDER(PublicKey publicKey) {
Base64 base64 = new Base64();
String begin = "-----BEGIN PUBLIC KEY-----";
String end = "-----END PUBLIC KEY-----";
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(publicKey.getEncoded());
String key = new String(base64.encode(pkcs8EncodedKeySpec.getEncoded()));
return begin + "\n" + key + "\n" + end;
}
public static void main(String[] args) {
Base64 base64 = new Base64();
try{
KeyPair keyPair = generateSignKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
// print private key
String privKey = getPEMPrivateKeyFromDER(privateKey);
System.out.println("privKey:\n " + privKey);
// print public key
String pubKey = getPEMPublicKeyFromDER(publicKey);
System.out.println("pubKey:\n " + pubKey);
System.out.println("---------------Convert to JWK-----------------");
RSAKey jwk = new RSAKey.Builder((RSAPublicKey) publicKey).privateKey(privateKey).build();
System.out.println("jwk:\n" + jwk.toPublicJWK());
String data = new String(base64.encode("hello".getBytes()));
// sign "hello"
byte[] sig = generateSignature(keyPair.getPrivate(), "hello".getBytes());
System.out.println("signature base64: " + new String(Base64.encodeBase64(sig)));
// start verify
boolean result = verifySignature(publicKey, "hello".getBytes(), sig);
System.out.println("Verification: " + result);
}catch (Exception e){
e.printStackTrace();
}
}
}
The output of the public key, private key, signature and jwk are as follows:
public key:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0pGjaaEsFCYj3sjfYzTarJPgYgSGtuwMeCMgfzAEFy0va+R83tyRNy7asYtGFB8mpwaED9OvzVJ3JvJKCE81c6Q6yWoZ+xQEzMtvQ3FibH/MH+gBSkgRYczP+dsT9j3n+F6sIRz9qdHfzHCyDPZ0tSf/wmmsX54eayMuH1p417bHv9yysDgXYxWKWiOoYOP6ynYSqMLEIxirkgUYlhsXVTD4RlZOAVBCUIbN2YE41UnQDExEnw6s8u1KHDeiNc15G1zZmm4SX/BNOWEeGjEVB4VJAwTUynaAcGgcBuNxzadpklLqFNYyIbu3WkK8L1Im3vVAuttMYIFF7sWiWLiFDQIDAQAB
-----END PUBLIC KEY-----
private key:
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDSkaNpoSwUJiPeyN9jNNqsk+BiBIa27Ax4IyB/MAQXLS9r5Hze3JE3Ltqxi0YUHyanBoQP06/NUncm8koITzVzpDrJahn7FATMy29DcWJsf8wf6AFKSBFhzM/52xP2Pef4XqwhHP2p0d/McLIM9nS1J//Caaxfnh5rIy4fWnjXtse/3LKwOBdjFYpaI6hg4/rKdhKowsQjGKuSBRiWGxdVMPhGVk4BUEJQhs3ZgTjVSdAMTESfDqzy7UocN6I1zXkbXNmabhJf8E05YR4aMRUHhUkDBNTKdoBwaBwG43HNp2mSUuoU1jIhu7daQrwvUibe9UC620xggUXuxaJYuIUNAgMBAAECggEBAMuzFflucDzBtLulaO0IyW5f05YeuM+mRF3H5X0Ez0A4dwKR0/aoO1a93dQZXl7Cz9qOZB0HdK9U0m17wACP166aNkB4gs+wSwBu4KDGxCcf7jEDeJQKab5JqvwTYhk5Q/myZCPYbs/kdvLIJoaBuN8y5amGHrmVNeoA63JrTDGoH7EVSPuJSDLzYd6Wtj5agAR7t4YRvWgqGsYRwhekXONOkMcoPh+o4WGocfLvI39XTzSTiItX+LfcWIKNL7KhPfjALfmCSzhCBEI0DqjMG4UFusfhGun5RM3Ii+ADfPwR1RkuqfVRvvdCh+B9C2RvflIXbJ8g6VfYDCnSmFyPXsUCgYEA8ju5im9oS+8z4C341IUdWpLthJyKGdBd/fvfTlRQFOroz5fKM8nK52BMf8Z0am641UY7gmNaEVID+z4KSIEIvgK19ymZmIUmMHR9nyFUDG7U05GeR9ocp+E12C5mo6KVkmfydMP0o+JeVcRxIksY5O7KcD1GgajJ2oHuOpJODlMCgYEA3ok5pvfCuZ3BpAblHAtKheCtydqt1vqYdPt1/IwzscJSpBkPNV08LQVjHBduSbZ0CqnSP0m+RkJysl7lJbsUejDZvDLpVTuHbI+/lVKVNsDcjbqFCaQ/YNbCS3/RQR8aHyAOjGvFUT0TyiGCyA0RUtOPQ0lN3jKSlvmB4KIk8x8CgYEA1frmcyDpDDMZK07RAcM5Y/po8P1rjVQgw61M3R/jwialVEStLUR0BiJuUW1ZB3AZvOUWyp7DQIun0rhCUzwRIfC5F8Za1X3Bd4pgMkM5wOXjAaYgIS7Oo4pxiy+qIaEM6dEDQByeLmtYlAE9Q2L9cbZzg+TNNNqKvYiLjpgRddsCgYBk5yvw/V6Z82IeEdJIyQXddmbWpAU2+SGvyWVSQfAepdH6/jHbLI9gipTJ5AXp/oTXYPEoKTPRf8eqAUU9unoqfYP8xcjvELLzEqr39h4aWZCn4u731DW4nygyO56SPJwHiO2P1j39/AfBGzjFpMc6GxtWkyJvoda+BhxR0rOWWQKBgQCXKepBOKEcE+tBpptYNBI3fWZp21W0WggTNcsBTvLHT1few5voRxmVlzoP2h3VB30v9i7JyGlP76nZFN+PEQeTUStmRk5kyi0UMx001+qgAjc2uEYUPxf62luQiJdozaQ+tXQ7KTKiQixqfUC4jQszUIlwFLDZgS7gZ7ynzOIGBQ==
-----END PRIVATE KEY-----
signature:
signature base64: sor4eqf2iBiqJnhMogu2GSx0JlT804l9J4thEkZ6stPZLari3sFa6w1/kxtwR+ogHq+LJM2g25eygTPr6RqndOiVjWnB7l0btnPV+wN4AWzofBSi+vAXL+BA+ANm8QF14JG0yW2acF+Wyn+xZjMid6tCc1IVw/3EOdVGWVATVYigM6HCVxdXE9HLBuj0VK7suWgseAEfxrn5hbzaJT+OxZ4Ozx3YwwyUjeDy62cnirswOMb49bZ7eLEreffCZLnah1URrerXTikFyDSFslZjg4X19q4MArZ+xqm8ON+tLC4NoQw7ST9f7KcYznbw58oEgpvE2mxdiEbsTqWwJ+/BOQ==
jwk (JSON Web Key):
{"kty":"RSA","e":"AQAB","n":"0pGjaaEsFCYj3sjfYzTarJPgYgSGtuwMeCMgfzAEFy0va-R83tyRNy7asYtGFB8mpwaED9OvzVJ3JvJKCE81c6Q6yWoZ-xQEzMtvQ3FibH_MH-gBSkgRYczP-dsT9j3n-F6sIRz9qdHfzHCyDPZ0tSf_wmmsX54eayMuH1p417bHv9yysDgXYxWKWiOoYOP6ynYSqMLEIxirkgUYlhsXVTD4RlZOAVBCUIbN2YE41UnQDExEnw6s8u1KHDeiNc15G1zZmm4SX_BNOWEeGjEVB4VJAwTUynaAcGgcBuNxzadpklLqFNYyIbu3WkK8L1Im3vVAuttMYIFF7sWiWLiFDQ"}
In java, the verification is true!!!
In the client side, I use Webcrypto to verify:
// npm install #trust/webcrypto --save
const crypto = require('#trust/webcrypto');
// Utility function
function str2ab(str) {
var arrBuff = new ArrayBuffer(str.length);
var bytes = new Uint8Array(arrBuff);
for (var iii = 0; iii < str.length; iii++) {
bytes[iii] = str.charCodeAt(iii);
}
return bytes;
}
// The publicKeyB64 is in the form of jwk that output in the step 2
var publicKeyB64 = {"kty":"RSA","e":"AQAB","n":"0pGjaaEsFCYj3sjfYzTarJPgYgSGtuwMeCMgfzAEFy0va-R83tyRNy7asYtGFB8mpwaED9OvzVJ3JvJKCE81c6Q6yWoZ-xQEzMtvQ3FibH_MH-gBSkgRYczP-dsT9j3n-F6sIRz9qdHfzHCyDPZ0tSf_wmmsX54eayMuH1p417bHv9yysDgXYxWKWiOoYOP6ynYSqMLEIxirkgUYlhsXVTD4RlZOAVBCUIbN2YE41UnQDExEnw6s8u1KHDeiNc15G1zZmm4SX_BNOWEeGjEVB4VJAwTUynaAcGgcBuNxzadpklLqFNYyIbu3WkK8L1Im3vVAuttMYIFF7sWiWLiFDQ"};
// the Base64 output of "hello"
var dataB64 = "aGVsbG8=";
// The signature output from step 2
var signatureB64 = "sor4eqf2iBiqJnhMogu2GSx0JlT804l9J4thEkZ6stPZLari3sFa6w1/kxtwR+ogHq+LJM2g25eygTPr6RqndOiVjWnB7l0btnPV+wN4AWzofBSi+vAXL+BA+ANm8QF14JG0yW2acF+Wyn+xZjMid6tCc1IVw/3EOdVGWVATVYigM6HCVxdXE9HLBuj0VK7suWgseAEfxrn5hbzaJT+OxZ4Ozx3YwwyUjeDy62cnirswOMb49bZ7eLEreffCZLnah1URrerXTikFyDSFslZjg4X19q4MArZ+xqm8ON+tLC4NoQw7ST9f7KcYznbw58oEgpvE2mxdiEbsTqWwJ+/BOQ==";
var signatureAlgorithm = {name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: {name: 'SHA-256'}};
var data = str2ab(Buffer.from(dataB64, 'base64').toString());
var signature = str2ab(Buffer.from(signatureB64, 'base64').toString());
crypto.subtle.importKey("jwk", publicKeyB64, signatureAlgorithm, false, ["verify"]).then(function(pubkey){
console.log("pubkey: " + pubkey);
return crypto.subtle.verify(signatureAlgorithm, pubkey, signature, data);
}).then( function (valid) {
console.log("Signature valid: ----- " + valid + " -----");
}).catch(function(err) {
console.log("Verification failed: \n" + err );
});
The result shows:
Signature valid: ----- false -----
I am wondering I made mistake in any step and could anyone help with this issue? Thanks a lot.