- edited description
Android: JWS Validation always returning FALSE , with SIGNED state
Hello,
Maybe related to #298
I want to validate signature of JWS structure with PS256 algorithm. So i use RSASSAVerifier. But it returns always false
I am using : org.bouncycastle:bcprov-jdk15on:1.56
and com.nimbusds:nimbus-jose-jwt
:8.19
Also i need to mention that the problem is present in Android API 23
I must mention also that the same code is working with Android API 24
see my code below :
fun jwsValidateSignatureAndReturnBody(jws: String?): String {
// Parser
val jwsObject: JWSObject
try {
jwsObject = JWSObject.parse(jws)
} catch (e: ParseException) {
throw RuntimeException("JWS parsing failed")
}
// Verifiy
try {
var verifier: JWSVerifier? = null
Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME)
Security.addProvider(BouncyCastleProviderSingleton.getInstance())
val alg = jwsObject.header.algorithm
if (alg == JWSAlgorithm.PS256 || alg == JWSAlgorithm.RS256) {
Timber.i("lamine key --> check" + Security.getProviders().get(0).values)
Timber.i("lamine key --> check" + Security.getProviders().get(1).values)
Timber.i("lamine key --> check" + Security.getProviders().get(2).values)
Timber.i("lamine key --> check" + Security.getProviders().get(3).values)
Timber.i("lamine key --> check" + Security.getProviders().get(4).values)
Timber.i("lamine key --> check" + Security.getProviders().get(5).values)
Timber.i("lamine key --> check -> " + JCASupport.isSupported(JWSAlgorithm.PS256))
val x509CertChain = jwsObject.header.x509CertChain
verifier =
RSASSAVerifier(X509CertUtils.parse(x509CertChain[0].decode()).publicKey as RSAPublicKey)
} else if (alg == JWSAlgorithm.ES256) {
verifier = ECDSAVerifier(ECKey.parse(jwsObject.header.jwk.toJSONString()))
} else {
// unsupported algorithm
throw RuntimeException()
}
Timber.i("lamine key --> check" + jwsObject.verify(verifier))
Timber.i("lamine key --> check" + JCASupport.isSupported(JWSAlgorithm.PS256))
if (!jwsObject.verify(verifier)) {
throw RuntimeException("JWS validation return false")
}
} catch (e: Exception) {
throw RuntimeException(e)
}
return jwsObject.payload.toString()
}
Comments (15)
-
reporter -
reporter - edited description
-
It seems that I am also facing this issue. I’m using the new serialized with detached=true but verification fails every time. Not sure if the problem is somewhere in my code.
byte[] encoded = Base64.getDecoder().decode(privateKeyPEM); byte[] encodedPub = Base64.getDecoder().decode(publicKeyPEM); KeyFactory keyFactory1 = KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpec1 = new X509EncodedKeySpec(encodedPub); RSAPublicKey publicKey = (RSAPublicKey) keyFactory1.generatePublic(keySpec1); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); RSAPrivateKey privateKey = (RSAPrivateKey)keyFactory.generatePrivate(keySpec); JWSSigner signer = new RSASSASigner(privateKey); HashMap<String, Object> criticalParameters = new HashMap<>(); criticalParameters.put("http://openbanking.org.uk/iat", 1501497671); criticalParameters.put("http://openbanking.org.uk/iss", keyORG); criticalParameters.put("http://openbanking.org.uk/tan", "openbankingtest.org.uk"); JWSObject jwsObject = new JWSObject( new JWSHeader.Builder(JWSAlgorithm.RS256) .base64URLEncodePayload(false) .keyID(keyID) .criticalParams(criticalParameters.keySet()) .customParams(criticalParameters) .build(), payload); jwsObject.sign(signer); JWSVerifier jwsVerifier = new RSASSAVerifier(publicKey, criticalParameters.keySet()); String jws = jwsObject.serialize(true); JWSObject parsedJWSObject = JWSObject.parse(jws, payload); if (parsedJWSObject.verify(new RSASSAVerifier(publicKey))) { System.out.println("Valid"); } else { System.out.println("Invalid"); }
-
reporter - edited description
-
@medlamine Semassel
Hi,
This is not related to your question, but I noticed an issue with the code - JWS objects must not be verified with public keys included in the JWS header, otherwise anyone can generate a JWK, and sign a JWS with it, which the receiver is always going to accept. This is akin to accepting self-signed certificates.
ECDSAVerifier(ECKey.parse(jwsObject.header.jwk.toJSONString()))
The JWK header parameter is intended for communicating ephemeral keys in ECDH.
Similarly, X.509 certificates in the JWS header must not be used to verify the signure, unless the certificate chain is resolved to a trusted certificate authority (CA).
-
Hi @Matej Mijoski,
If you replace
JWSVerifier jwsVerifier = new RSASSAVerifier(publicKey, criticalParameters.keySet()); String jws = jwsObject.serialize(true); JWSObject parsedJWSObject = JWSObject.parse(jws, payload); if (parsedJWSObject.verify(new RSASSAVerifier(publicKey))) {
with
JWSVerifier jwsVerifier = new RSASSAVerifier(publicKey, criticalParameters.keySet()); String jws = jwsObject.serialize(true); JWSObject parsedJWSObject = JWSObject.parse(jws, payload); if (parsedJWSObject.verify(jwsVerifier)) {
the signature validation will work :)
-
@medlamine Semassel
I suggest you create a simple sign - verify test to isolate the issue with the PS256 alg.
Normally, if a signing algorithm is not supported by the JCA provider this will result in a exception (post the stack trace here).
What does
JCASupport.isSupported(JWSAlgorithm.PS256))
return in Android 23 and 24?
-
- changed title to Android: JWS Validation always returning FALSE , with SIGNED state
Tags as Android issue
-
reporter Thanks for your comments @Yavor Vasilev , actually i did use
if (parsedJWSObject.verify(jwsVerifier)) {
but still got false with verify, and the
JCASupport.isSupported(JWSAlgorithm.PS256))
return True.
as I explained the code is working with Android above API 23
-
I encountered the same issue and I traced it down to this change in version 8.16:
https://bitbucket.org/connect2id/nimbus-jose-jwt/commits/51c40ca23acb1e3c13eb04afdf483fbe86865582It is working nimbus-jose-jwt:8.15 and below.
Any idea what’s the workaround? I tried using both
org.bouncycastle.jce.provider.BouncyCastleProvider
andcom.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton
but the result is the same. -
It looks like the underlying crypto provider (Bouncy Castle) doesn’t understand the newer PSS alg names.
Which version of the Bouncy Castle do you have?
I’m not really familiar with Android, bear that in mind.
-
I’m using the latest, 1.68:
https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15to18 -
while waiting for the fixes, is there an alternate solution you could suggest ?
Thank you.
-
Try this or a newer release and let me know if you have success, it received an updated to the JCA logic for PSS:
The change was from a PR by a developer who was experiencing issues with his HSM and the mix might also help here.
version 9.11.3 (2021-08-01) * Refactors RSASSA.getSignerAndVerifier to obtain a Signature for PSxxx without a PSSParameterSpec when the JCA algorithm name contains the necessary PSS parameters. Intended to prevent an UnsupportedOperationException with the nCipher JCA provider.
The reason the older lib version worked is that uses a SHA alg identifier that is understood by older Android BouncyCastle versions. That identifier however is not standard and breaks standard compliant JCA providers. https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest
-
A technical solution to handle such non-std JCA alg names is to provide an API to the lib, to allow setting of alternative names, e.g. “SHA-256” → “SHA256”.
- Log in to comment