User key not Authenticated, When Sign JWT with java.security PrivateKey with android Fingerprint

Issue #534 invalid
Ajay created an issue
    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)

  1. Ajay 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 android

    Refer : https://developer.android.com/training/sign-in/biometric-auth#biometric-only

    What im looking here is, signing the jws object with android signed signature.

    1. get Signature Signature.getInstance("SHA256withECDSA")
    2. signature.initSign(privateKey)
    3. Create a CryptoObject with Signature
    4. Call Biometric prompt with CryptoObject
    5. 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()

    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

  2. Vladimir Dzhuvinov

    I mean how the private key gets instantiated from the KeyStore. This is crucial for everything else to work

  3. Ajay 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)

  4. Ajay reporter

    I could be wrong here, please let me know if there is any way to sign a jws with android fingerprint

  5. Ajay reporter

    Team, I'm able to tweak the nimbus-jose-jwt ECDSASigner to sign the JWS with android fingerprint and its working.

  6. Vladimir Dzhuvinov

    Thanks for proposing this patch.

    I see that there is a new ECDSASigner constructor that accepts a Signature argument. I’m curious, how is this Signature created? How does that differ from the current way of obtaining the Signature inside the ECDSASigner ?

  7. Ajay 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).

  8. Ajay 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

  9. Vladimir Dzhuvinov

    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?

  10. Ajay reporter

    Signature.getInstance(“SHA256withECDSA”)

    Then we pass the signature in crypto object to the biometric prompt.

  11. Vladimir Dzhuvinov

    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

  12. Ajay 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());

  13. Vladimir Dzhuvinov

    We updated the example to should how the Signature can be obtained from the ActionRequiredForJWSCompletionException

    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();
    }
    

  14. Ajay 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 with arException

  15. Vladimir Dzhuvinov

    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.

  16. Vladimir Dzhuvinov

    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.

  17. Log in to comment