I'm trying to
- generate sign/verification keys (RSA)
- sign a value (using those keys) on a Java web application (lets call server-side)
- in order to a web client to verify - public-key imported as
RSASSA-PKCS1-v1_5
+SHA-256
, (in a browser, using WebCrypto API / client-side)
I'm having problems verifying the signed value (signed in the Java server-side) even though the public sign/verify key is successfully imported as a JWK in the client side.
I was wondering if there is any algorithm compatibility issue in any of the steps (OpenSSL, Java or Javascript) that I may be encountering.
The OpenSSL commands used to generate the keys
openssl genrsa -out privatekey.pem 2048
openssl rsa -in privatekey.pem -pubout > publickey.pub
openssl pkcs8 -topk8 -inform PEM -outform DER -in privatekey.pem -out privatekey-pkcs8.pem
Importing keys with Java (server-side)
public static KeyPair generateSignKeyPair() throws ... {
byte[] privBytes = b64ToByteArray(PRIVATE_KEY_PEM_VALUE);
byte[] pubBytes = b64ToByteArray(PUBLIC_KEY_PEM_VALUE);
// private key
KeySpec keySpec = new PKCS8EncodedKeySpec(privBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
// public key (javaPubSignKey)
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(pubBytes);
PublicKey publicKey = keyFactory.generatePublic(X509publicKey);
return new KeyPair(publicKey, privateKey);
}
Signing a value with Java (server-side)
public static byte[] generateSignature(PrivateKey signPrivateKey, byte[] data) throws ... {
Signature dsa = Signature.getInstance("SHA256withRSA");
dsa.initSign(signPrivateKey);
dsa.update(data);
return dsa.sign();
}
Send them to a web-app for the WebCrypto API to verify as a client/browser (the client is aware of the publicKey generated in the first step).
// Import public sign/verify key (javaPubSignVerifyKey)
var signatureAlgorithm = {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: {
name: 'SHA-256'
}
};
// JWK format (1)
crypto.subtle.importKey(
'jwk', javaPubSignVerifyKey, signatureAlgorithm, false, ['verify']
).then(success, error);
function success(key) {
signatureVerifyPublicKey = key;
}
Note (1): on the Java side, I'm using com.nimbusds.jose.jwk.JWK
to export the publicKey to JWK format.
The sign key is successfully imported by WebCrypto. But when it comes to the verification, it fails (the verification boolean is false
).
crypto.subtle.verify(
signatureAlgorithm,
signatureVerifyPublicKey,
signature, // bytes in Int8Array format (2)
data // bytes in Int8Array format
).then(
function (valid) {
// valid === false
}
)
Note (2): also note that every example I found on WebCrypto used Uint8Array
to represent byte arrays, but since Java generates signed byte-arrays I need to use Int8Array
so that the signature values are not contaminated (maybe this is an issue aswell).
EDIT: for reference, it turned out to be another unrelated issue - I was converting the expected data from base64 twice in Javascript without noticing it; naturally the verification failed.