I want to do a project with JSON Web Signature (JWS) and i want to send the public key of the certificate used for the signature so that the message can be validated once received with this public key. I am using the Ninbus JOS JWT library. I can sign the JSON object and I can see the public key, but i can not validate it correctly. This is the code:
// Create RSA-signer with the private key
JWSSigner signer = new RSASSASigner(_signatureKey_); // PrivateKey
com.nimbusds.jose.util.Base64 b64 = new com.nimbusds.jose.util.Base64(_x509certificate.toString()); // X509Certificate
ArrayList<com.nimbusds.jose.util.Base64> certificados = new ArrayList<com.nimbusds.jose.util.Base64>();
certificados.add(b64);
RSAPublicKey _rsaPublicKey = (RSAPublicKey)_x509certificate.getPublicKey(); // Get the public key of the X509Certificate
RSAKey jwk = new com.nimbusds.jose.jwk.RSAKey.Builder( new Base64URL( _rsaPublicKey.getModulus().toString()), new Base64URL( _rsaPublicKey.getPublicExponent().toString()))
.x509CertChain(certificados)
.build();
JWSHeader _jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256).
x509CertChain(certificados).
jwk(jwk).
build();
// Prepare JWS object with simple string as payload
JWSObject jwsObject = new JWSObject(_jwsHeader, new Payload(_jsonObject));
// Compute the RSA signature
jwsObject.sign(signer);
// Validation OK : This validation works
JWSVerifier verifier = new RSASSAVerifier(_rsaPublicKey);
boolean signatureValid = jwsObject.verify(verifier); // ---> True, OK
// Now I want to validate the JWSObject getting the public key from the same JWSObject. This validation Fails
JWK _jwk = jwsObject.getHeader().getJWK();
RSAKey _rsakey = (RSAKey)_jwk;
RSAPublicKey _rsaPublicKey2 = _rsakey.toRSAPublicKey();
JWSVerifier verifier2 = new RSASSAVerifier(_rsakey.toRSAPublicKey());
boolean verificado2 = jwsObject.verify(verifier2); // False!
// Another option, this fails too
RSAKey __rsaKey2 = new com.nimbusds.jose.jwk.RSAKey.Builder( _rsakey.toRSAPublicKey() ).x509CertChain(_jwk.getX509CertChain()).build();
JWSVerifier verifier3 = new RSASSAVerifier(__rsaKey2);
boolean verificado3 = jwsObject.verify(verifier3); // False!
The _rsaPublicKey is: "Sun RSA public key, 2048 bits", but when i get it from the JWK (_rsaPublicKey2), I get "Sun RSA public key, 3696 bits" and i don't know why.
Thanks!
On the recipient side, do you validate the X.509 certificate issuer, subject and chain before trusting the key? Signature validation must not be attempted before the recipient is certain that it can trust the certificate included in the JWS.
Another note: Do not include the public JWK in the JWS header. This should only be used for ephemeral public keys in ECDH (a different alg used for JWE). Passing the certificate chain in the JWS header is sufficient, but you must validate it / find out if the certificate can be trusted, before using its public key.
The library will not validate / find out if the certificate can be trusted for you!
If the second signature validation fails, then probably the key used to sign the JWS and the one that came with the X.509 certificate are not the same (as suggested by the different reported lengths - 2048 bits vs. 3696 bits).