Skip to content

AndroidKeyStore RSA keys not able to be used for signing #1049

@aeri-ri

Description

@aeri-ri

Describe the bug
Using an AndroidKeyStore keypair's private key doesn't allow signing of a jwt. I've tried using the BouncyCastle dependencies as mentioned in the main readme as well. The following is the error I get.

io.jsonwebtoken.security.SignatureException: Unable to compute PS256 signature with JCA algorithm 'RSASSA-PSS' using key {class: android.security.keystore2.AndroidKeyStoreRSAPrivateKey, algorithm: RSA, format: null}: Signature callback execution failed: No installed provider supports this key: android.security.keystore2.AndroidKeyStoreRSAPrivateKey

To Reproduce
Steps to reproduce the behavior:

  1. Generate key pair using RSA with PSS for signature padding and SHA256 digest.
val kpg: KeyPairGenerator =
            KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore")

        val KEY_ALIAS = "example"
        kpg.initialize(

            KeyGenParameterSpec.Builder(
                KEY_ALIAS,
                KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY or KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
            )
                .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
                .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS)
                .setKeySize(2048)
                .build()
        )

        kpg.generateKeyPair()
  1. Get key pair private and public key for use in creating a jwt.
var keyStore = KeyStore.getInstance("AndroidKeyStore").apply {
            load(null) 
        }
        var entry = keyStore.getEntry("example", null) as? KeyStore.PrivateKeyEntry
        var keyPair = KeyPair(entry?.certificate?.publicKey, entry?.privateKey)
        if (entry == null || keyPair == null) {
            try {
                generateKeys();
                keyStore = KeyStore.getInstance("AndroidKeyStore").apply {
                    load(null) 
                }
                entry = keyStore.getEntry("example", null) as? KeyStore.PrivateKeyEntry
                keyPair = KeyPair(entry?.certificate?.publicKey, entry?.privateKey)
            } catch (e: Exception) {
                //
            }
        }


        pubKey = entry?.certificate?.publicKey as RSAPublicKey
        privKey = entry?.privateKey as PrivateKey
  1. Use public key and private key in creating and signing a jwt.
val jwk = Jwks.builder()
                .key(pubKey).build()
            val jws = Jwts.builder()
                .header()
                .jwk(jwk)
                .and().id(Uuid.random().toString())
                .claims()
                .add("random","example")
                .and()
                .signWith(privKey, Jwts.SIG.PS256)
                .compact()

Expected behavior
The jwt should be able to be signed with the AndroidKeyStore private key. I'm able to sign with the private key using the Signature instance in the following way:

val s = Signature.getInstance("SHA256withRSA/PSS")
                .apply {
                initSign(privKey)
                update(payload)
            }
val signature: ByteArray = s.sign()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions