- edited description
User key not Authenticated, When Sign JWT with java.security PrivateKey with android Fingerprint
ECDSASigner signer = new ECDSASigner(getPrivateKey(), Curve.P_256);
JWSObject jwsObject = new JWSObject(
new JWSHeader.Builder(JWSAlgorithm.ES256).keyID(BiometricHelper.getKid())
.customParam("auth_type","fingerprint").build(),new Payload(message));
Im getting key not Authenticated even after the fingerprint authentication is successful.
Also i don't know how the Signature which pass in CryptoObject is connected with the signing process of jws here
jwsObject.sign(signer)
This throws User key not Authenticated
I have also tried the Android Biometric prompt with ActionRequiredForJWSCompletionException
which detaches from the flow and doesn't seems to be correct because it doesn't have that Signature which we get from the onAuthenticationSucceeded. It just pause the signing process and it needed to be resume manually when with actionRequired.getSignCompletion().complete();
and we get signed jws with jwsObject.serialize();
I could be wrong here, Can someone please look into this.
Comments (24)
-
reporter -
- changed status to open
We are not a mobile dev company, but if you are able to discover what issue you have here and how to address, let us know se we can update the docs.
https://connect2id.com/products/nimbus-jose-jwt/examples/jws-with-android-biometric-or-pin-prompt
This feature was contributed by a large extent.
-
reporter https://connect2id.com/products/nimbus-jose-jwt/examples/jws-with-android-biometric-or-pin-prompt
Here the signing was done with
ActionRequiredForJWSCompletionException
and this can be done even without the fingerprint authentication. That is the problem. With the above documentation its not following the actual biometric flow with androidRefer : https://developer.android.com/training/sign-in/biometric-auth#biometric-only
What im looking here is, signing the jws object with android signed signature.
- get Signature
Signature.getInstance("SHA256withECDSA")
signature.initSign(privateKey)
- Create a CryptoObject with
Signature
- Call Biometric prompt with CryptoObject
-
once the biometric fingerprint is given
- we will get callback on
onAuthenticationSucceeded with Signature
-
val signature = result.cryptoObject.signature
signature?.update(payload.toByteArray(Charsets.UTF_8))
signature?.sign()
- we will get callback on
Here im getting user key not Authenticated. Does this support signing the jws with Android Keystore.
Please see the last comment in this issue: https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/373/biometricprompt-support-in-android
- get Signature
-
I’m curious, how does the code that instantiates the
privateKey
look like? -
reporter If you are asking about the type then Its
java.security.PrivateKey
and not ECPrivateKey
-
I mean how the private key gets instantiated from the KeyStore. This is crucial for everything else to work
-
reporter val keyStore = KeyStore.getInstance(“AndroidKeyStore”) keyStore.load(null) val privateKey = keyStore.getKey(“alias”, null) as? PrivateKey
For the key pair generation,
I use KeyGenParameterSpec with setAlgorithmParameterSpec(ECGenParameterSpec(stdName)) and setUserAuthenticationRequired(true)
-
reporter I could be wrong here, please let me know if there is any way to sign a jws with android fingerprint
-
reporter Team, I'm able to tweak the nimbus-jose-jwt ECDSASigner to sign the JWS with android fingerprint and its working.
-
Interesting. Is it necessary to update the docs at https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/373/biometricprompt-support-in-android ?
-
Thanks for proposing this patch.
I see that there is a new
ECDSASigner
constructor that accepts aSignature
argument. I’m curious, how is thisSignature
created? How does that differ from the current way of obtaining the Signature inside theECDSASigner
? -
reporter That signature is created with an instance and initialised with the PrivateKey which is generated from Androidkeystore. So we do initialise the signature before the biometric prompt auth and after that we do the jwsobject.sign(signer).
-
reporter Basically with the
ECDSASigner
signer would have the Signature which is already initialised for signing, which is from the Androidkeystore.The signature is retrieved from
ECDSA.getSignerAndVerifier(alg, getJCAContext().getProvider())
and i think the provider is different here. I might be wrong
-
How does this code to init the Signature look like? AFAIK this is the “secret sauce” that ppl will need to get the signer working with AndroidKeyStore. Could you paste a snippet?
-
reporter Signature.getInstance(“SHA256withECDSA”)
Then we pass the signature in crypto object to the biometric prompt.
-
reporter val sig = Signature.getInstance(“SHA256withECDSA”) sig.initSign(privatekey)
-
Thanks. When does the prompt appear? Right after
initSign
? -
reporter Yes correct,
With the prompt we do pass a crypto object which is having this signature
-
Hang on, the current code base seems okay, and to be doing what you did:
final Signature dsa = ECDSA.getSignerAndVerifier(alg, getJCAContext().getProvider()); dsa.initSign(privateKey, getJCAContext().getSecureRandom()); // <--- the init if (OptionUtils.optionIsPresent(opts, UserAuthenticationRequired.class)) { throw new ActionRequiredForJWSCompletionException( "Authenticate user to complete signing", UserAuthenticationRequired.getInstance(), new CompletableJWSObjectSigning() { @Override public Signature getInitializedSignature() { return dsa; } @Override public Base64URL complete() throws JOSEException { try { dsa.update(signingInput); final byte[] jcaSignature = dsa.sign(); final int rsByteArrayLength = ECDSA.getSignatureByteArrayLength(header.getAlgorithm()); final byte[] jwsSignature = ECDSA.transcodeSignatureToConcat(jcaSignature, rsByteArrayLength); return Base64URL.encode(jwsSignature); } catch (SignatureException e) { throw new JOSEException(e.getMessage(), e); } } } ); } dsa.update(signingInput); jcaSignature = dsa.sign();
I’m perplexed now
-
reporter I don't think so, Because the dsa signature object is created inside the nimbus library and we need to pass the cypto object with this signature to the biometric prompt to authenticate the user. And
ActionRequiredForJWSCompletionException
is kind of pausing the sign and resuming the when we set manually.At least we need a provision to get the below signature with a function so that we can use this signature to authenticate user.
final Signature dsa = ECDSA.getSignerAndVerifier(alg, getJCAContext().getProvider());
-
We updated the example to should how the
Signature
can be obtained from theActionRequiredForJWSCompletionException
https://connect2id.com/products/nimbus-jose-jwt/examples/jws-with-android-biometric-or-pin-prompt
if (actionRequired != null) { // Perform user authentication to unlock the private RSA key, // e.g. with biometric prompt Signature sig = actionRequired.getInitializedSignature(); // Pass the Signature in a CryptoObject to the biometric prompt // ... // Complete the signing when the private key is unlocked actionRequired.getCompletableJWSObjectSigning().complete(); }
-
reporter No luck, Tried this with ECDSA signer and getting internal signing error, user key not authenticated. I tried this previously with
RSASSASigner
and got the callback witharException
-
Would you be able to make a test, in your own code, outside the lib, using the your key init that worked, but with this change:
dsa.initSign(privateKey, new SecureRandom());
With this test I want to find out if the passed SecureRandom is causing an issue. In theory, it shouldn’t.
My objective it to keep the current lib API as they are, and if the issue is related to the key init, to fix it internally, rather than introduce a new ECDSASigner constructor.
-
- changed status to invalid
There was no response, so I'm closing this issue as invalid.
Feel free to reopen if there is still an issue after the doc update.
- Log in to comment