2

The problem is the following:

  • I generate the key in Android (Xamarin.Droid):

     public IPublicKey CreateKey(string keyID)
     {
        /*KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
                 KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
        keyPairGenerator.initialize(
                new KeyGenParameterSpec.Builder(
                        "key1",
                        KeyProperties.PURPOSE_SIGN)
                        .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
                        .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS)
                        .build());
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        Signature signature = Signature.getInstance("SHA256withRSA/PSS");
        signature.initSign(keyPair.getPrivate());
    
         // The key pair can also be obtained from the Android Keystore any time as follows:
         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
                    keyStore.load(null);
                    PrivateKey privateKey = (PrivateKey)keyStore.getKey("key1", null);
                    PublicKey publicKey = keyStore.getCertificate("key1").getPublicKey();*/
    
    
        //App.Current.MainPage.DisplayAlert("Info", "Creating a new key pair", "Ok");
        // UTILIZANDO RSA
        KeyPairGenerator kpg = 
        KeyPairGenerator.GetInstance(KeyProperties.KeyAlgorithmRsa, KEYSTORE_NAME);
        kpg.Initialize(
            new KeyGenParameterSpec.Builder(keyID,
            KeyStorePurpose.Sign)
            .SetSignaturePaddings(KeyProperties.SignaturePaddingRsaPss)
            .SetDigests(KeyProperties.DigestSha1)
            .Build()
            );
    
        KeyPair keyPair = kpg.GenerateKeyPair();
    
        Log.Debug(TAG, "New key created for fingerprint authentication");
    
        return keyPair.Public;
    }
    
  • Then i generate a signature:

        KeyStore.PrivateKeyEntry PKentry = 
        (KeyStore.PrivateKeyEntry)_keystore.GetEntry(keyID, null);
        IPublicKey pk = (IPublicKey)PKentry.Certificate.PublicKey;
        //this.pk = pk;
        privKey = PKentry.PrivateKey;
    
        //cipher.Init(Cipher.EncryptMode, privKey);
        //byte[] output = cipher.DoFinal(Encoding.UTF8.GetBytes(input));
        //String s = new string(cipher.DoFinal(input));
    
        // signature
        Signature sig = Signature.GetInstance("SHA1withRSA/PSS");
        sig.InitSign(privKey);
        byte[] inputDataToSign = Encoding.UTF8.GetBytes(input);
        sig.Update(inputDataToSign);
        byte[] signatureBytes = sig.Sign();
    
  • And i send the key and the signature to a ASP.net wep API 2 server. Client side response generation:

     RegistrationResponse registrationResponse = new RegistrationResponse();
     string fcparams = Utils.Base64Encode(JsonConvert.SerializeObject(finalChallengeParams));
      registrationResponse.fcParams = fcparams;
      byte[] signedData = sign(fcparams, registrationRequest.username, facetID);
     registrationResponse.signedData = signedData;
     registrationResponse.Base64key = convertPublicKeyToString(publicKey);
     ...
     ...
    private string convertPublicKeyToString(IPublicKey publicKey)
    {
        string publicKeyString = Base64.EncodeToString(publicKey.GetEncoded(), 0);
    
     return publicKeyString;
      }
    

I send it using Refit Nugget. And this is the code i use when i receive the HTTPRequest on server side:

[Route("regResponse/")]
    [HttpPost]
    public IHttpActionResult ProcessClientRegistrationResponse([FromBody] RegistrationResponse registrationResponse) 
    {


        //byte[] publicKeyBytes = Convert.FromBase64String(registrationResponse.Base64key);
        byte[] publicKeyBytes = registrationResponse.Base64key;
        AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(publicKeyBytes);

        RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;

        RSAParameters rsaParameters = new RSAParameters();
        rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
        rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();

        RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
        rsa.ImportParameters(rsaParameters);

        /*****/

        string alg = rsa.SignatureAlgorithm;
        byte[] signedData = registrationResponse.signedData;
        byte[] fcParamsBytes = Encoding.UTF8.GetBytes(registrationResponse.fcParams);

        RSACng rsaCng = new RSACng();
        rsaCng.ImportParameters(rsaParameters);

        SHA1Managed hash = new SHA1Managed();
        byte[] hashedData;
        hashedData = hash.ComputeHash(signedData);


        /*********/

        bool rsaCngDataOk1 = rsaCng.VerifyData(fcParamsBytes, signedData, HashAlgorithmName.SHA1, RSASignaturePadding.Pss);
        bool rsaCngDataOk2 = rsaCng.VerifyData(fcParamsBytes, signedData, HashAlgorithmName.SHA1, RSASignaturePadding.Pss);
        bool rsaCngDataOk3 = rsaCng.VerifyData(hashedData, signedData, HashAlgorithmName.SHA1, RSASignaturePadding.Pss);
        bool rsaCngDataOk4 = rsaCng.VerifyData(hashedData, signedData, HashAlgorithmName.SHA1, RSASignaturePadding.Pss);

        bool rsaCngHashOk1 = rsaCng.VerifyHash(hashedData, signedData, HashAlgorithmName.SHA1, RSASignaturePadding.Pss);

        bool dataOK1 = rsa.VerifyData(fcParamsBytes, new SHA1CryptoServiceProvider(), signedData);
        bool dataOk2 = rsa.VerifyData(fcParamsBytes, signedData, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);

        bool hashOk = rsa.VerifyHash(hashedData, CryptoConfig.MapNameToOID("SHA1"), signedData);

        return Ok(true);

    }

EVERY bool is wrong. I think the problem is clearly on the public key. The questions are,

  1. does the method publickey.encode() do what i think? I think it converts my public key to a byte[] representation (source: Android developer Key Info)

  2. do i convert the received byte[] representation of the key to a correct RSA key?

  3. Is there any problem on algorithms? I don't think so but we never know...

I don't find the solution. I searched for ways to import public keys from strings in .net or c# and for ways to export Android Public key to string or byte[] but there's no much help for this concrete questions...

Ricardo Almeida
  • 163
  • 3
  • 14
  • `PublicKeyFactory.CreateKey(publicKeyBytes);` is very likely to throw an exception unless the publickey byte array contains something that parses like a SubjectPublicKeyInfo ASN1 -encoded object. I would focus on the encoding(or lack of it) on the signed data itself, and verify that it is transferred correctly. – President James K. Polk Nov 13 '17 at 17:47
  • @JamesKPolk it doesn't throw any exception. The code runs without any problem – Ricardo Almeida Nov 14 '17 at 17:41
  • Exactly. That's why I said you should "...focus on the encoding(or lack of it) on the signed data itself, and verify that it is transferred correctly." – President James K. Polk Nov 14 '17 at 18:52
  • I verified debugging and it's exactly the same data on both sides. Even the key is the same. I compared the modulus and the exponent from the public key on the client side and the one received by the server. They are both the same so it seems like the public key is well received by the server. But the problem persists – Ricardo Almeida Nov 15 '17 at 16:03
  • Ok, I will attempt to duplicate your results in my own test environment. – President James K. Polk Nov 15 '17 at 16:05
  • I created my own example that was as faithful to yours as I could be. I don't have access to a Windows platform, so I'm using Mono on Mac OSx, but it doesn't support PSS. However, when I use Pkcs1 padding it verifies just fine. I would guess that your encoding/decoding of the data is the problem. – President James K. Polk Nov 15 '17 at 21:04
  • How do you use PKcs1 padding on Android? I can't find any "string" with pcks1 as padding [link](https://developer.android.com/reference/java/security/Signature.html#getInstance(java.lang.String, java.lang.String) Really thanks for helping – Ricardo Almeida Nov 16 '17 at 15:18
  • IT WORKED!! IT WORKED!!! MANY THANKS!!!! FOREVER GRATITUDE!!! PSS seems to be the problem... – Ricardo Almeida Nov 16 '17 at 15:43
  • Sure thing, but note that PSS is the more modern and secure signature scheme and should be used in preference to "pkcs1" in new applications. – President James K. Polk Nov 16 '17 at 15:48
  • Yes i know it. But unfortunately it doens't work =( – Ricardo Almeida Nov 16 '17 at 21:48

1 Answers1

0

@James K Polk gave me the solution. Apparently C# doesn't work well with PSS padding. I just had to change it to PKCS1. And i changed to digest algorithm too to SHA512.

Ricardo Almeida
  • 163
  • 3
  • 14