如何验证Android中生成的.net中的签名

时间:2017-11-13 15:46:17

标签: c# android xamarin.forms rsa public-key-exchange

问题如下:

  • 我在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;
    }
    
  • 然后我生成一个签名:

        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();
    
  • 我将密钥和签名发送到ASP.net wep API 2服务器。 客户端响应生成:

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

我使用Refit Nugget发送它。 这是我在服务器端收到HTTPRequest时使用的代码:

[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);

    }

每个布尔都错了。我认为问题显然在公钥上。 问题是,

  1. 方法publickey.encode()是否符合我的想法?我认为它将我的公钥转换为byte []表示(来源:Android developer Key Info

  2. 将接收到的密钥[]表示转换为正确的RSA密钥吗?

  3. 算法有问题吗?我不这么认为,但我们永远不知道......

  4. 我找不到解决方案。我搜索了从.net或c#中的字符串导入公钥的方法,以及将Android公钥导出到字符串或字节[]的方法,但对这些具体问题没有多大帮助...

1 个答案:

答案 0 :(得分:0)

@James K Polk给了我解决方案。 显然C#与PSS填充不兼容。我只需将其更改为PKCS1。我也改为使用消化算法来使用SHA512。