Search code examples
c#androidbouncycastlekeystore

Use BouncyCastle to verify Android KeyStore signed payload


I am trying to sign data using the KeyStore API from Android and verify the data in the server using BouncyCastle and C#.

My process for the verification is like this:

  1. Generate a key pair on android
  2. Send the public key to the server
  3. Generate a challenge on the server
  4. Sign the challenge on android using the private key
  5. Verify the signed challenge on the server using the public key

I cannot get the verification to work properly. How can I get the verification to succeed? Is there a significant logic error?

This is the code I am using right now:

Generate the key pair using a KeyPairGenerator:

val generator: KeyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore")

val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder("KEY_ALIAS",
    KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
)
    .setDigests(KeyProperties.DIGEST_SHA256)
    .setKeySize(2048)
    .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS)
    .build()

generator.initialize(parameterSpec)
generator.generateKeyPair()

Get the public key which is sent to the server:

val publicKeyBytes = keyStore.getCertificate("KEY_ALIAS").publicKey.encoded
val publicKey = Base64.encodeToString(publicKeyBytes, Base64.DEFAULT)

The server then generates a challenge payload which must be verified by the android app. The signing is done like this:

val challengeBytes = Base64.decode(challenge, Base64.DEFAULT)
val entry = keyStore.getEntry("KEY_ALIAS", null) as KeyStore.PrivateKeyEntry

val signature = Signature.getInstance("SHA256withRSA/PSS")

signature.initSign(entry.privateKey)
signature.update(challengeBytes)
val signedBytes = signature.sign()

val signedChallenge = Base64.encodeToString(signedBytes, Base64.NO_WRAP)

This signedChallenge is then send to the server which will verify it using BouncyCastle in C#:

byte[] challengeBytes = // Original challenge
byte[] signedBytes = Convert.FromBase64String(signedChallenge);

ISigner signer = SignerUtilities
    .GetSigner(
        SignerUtilities
            .GetObjectIdentifier("SHA256withRSA/PSS"));

var pKeyParams = (RsaKeyParameters) PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey));
signer.Init(false, pKeyParams);
signer.BlockUpdate(challengeBytes, 0, challengeBytes.Length);

isVerified = signer.VerifySignature(signedBytes);

Setup

Android: SDK 23+ The server is written in C# using the NuGet Package Portable.BouncyCastle Version 1.8.10


Solution

  • The problem was the instantiation of the ISigner in the C# code.

    To make it work I had to get the signer like so:

    ISigner signer = SignerUtilities.GetSigner("SHA256withRSA/PSS");