C# .NET RSACryptServiceProvider使用4096私钥加密,如何在Android上解密

C# .NET RSACryptServiceProvider使用4096私钥加密,如何在Android上解密,c#,android,.net,cryptography,rsacryptoserviceprovider,C#,Android,.net,Cryptography,Rsacryptoserviceprovider,我正在用rsacyptoserviceprovider用私钥对.NET中的消息进行加密(PKCS第1版第5版) 当我尝试使用以下使用公钥的代码在.NET中解密时,一切正常: private static string Decrypt(string key, string content) { byte[] rgb = Convert.FromBase64String(content); var cryptoServiceProvider = new RSACryptoServ

我正在用rsacyptoserviceprovider私钥对.NET中的消息进行加密(PKCS第1版第5版)

当我尝试使用以下使用公钥的代码在.NET中解密时,一切正常:

private static string Decrypt(string key, string content)
{
     byte[] rgb = Convert.FromBase64String(content);
     var cryptoServiceProvider = new RSACryptoServiceProvider(new CspParameters()
     {
          ProviderType = 1
     });
     cryptoServiceProvider.ImportCspBlob(Convert.FromBase64String(key));
     return Convert.ToBase64String(cryptoServiceProvider.Decrypt(rgb, false));
}
另一方面,当我试图找到一种算法来在Android中使用相同的解密方法时,我无法使用公钥正确地对其进行解密。我从.NET中的公钥导出了模数指数,以便在Android上正确加载

Android中的方法如下所示:

public String Decrypt(String input) {
    try {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");

        String modulusString = "mmGn1IXB+/NEm1ecLiUzgz7g2L6L5EE5DUcptppTNwZSqxeYKn0AuAccupL0iyX3LMPw6Dl9pjPXDjk93TQwYwyGgZaXOSRDQd/W2Y93g8erpGBRm/Olt7QN2GYhxP8Vn+cWUbNuikdD4yMfYX9NeD9UNt5WJGFf+jRkLk0zRK0A7ZIS+q0NvGJ/CgaRuoe3x4Mh1qYP9ZWNRw8rsDbZ6N2zyUa3Hk/WJkptRa6jrzc937r3QYF3eDTurVJZHwC7c3TJ474/8up3YNREnpK1p7hqwQ78fn35Tw4ZyTNxCevVJfYtc7pKHHiwfk36OxtOIesfKlMnHMs4vMWJm79ctixqAe3i9aFbbRj710dKAfZZ0FnwSnTpsoKO5g7N8mKY8nVpZej7tcLdTL44JqWEqnQkocRqgO/p3R8V/6To/OjQGf0r6ut9y/LnlM5qalnKJ1gFg1D7gCzZJ150TX4AO5kGSAFRyjkwGxnR0WLKf+BDZ8T/syOrFOrzg6b05OxiECwCvLWk0AaQiJkdu2uHbsFUj3J2BcwDYm/kZiD0Ri886xHqZMNExZshlIqiecqCskQhaMVC1+aCm+IFf16Qg/+eMYCd+3jm/deezT4rcMBOV/M+muownGYQ9WOdjEK53h9oVheahD3LqCW8MizABFimvXR3wAgkIUvhocVhSN0=";
        String exponentString = "AQAB";

        byte[] modulusBytes = Base64.decode(modulusString.getBytes("UTF-8"), Base64.DEFAULT);
        byte[] dBytes = Base64.decode(exponentString.getBytes("UTF-8"), Base64.DEFAULT);

        BigInteger modulus = new BigInteger(1, modulusBytes);
        BigInteger d = new BigInteger(1, dBytes);

        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, d);
        PublicKey key = keyFactory.generatePublic(keySpec);

        //at one point I read somewhere that .net reverses the byte array so that it needs to be reversed for java, but who knows any more
        /*byte[] inputArrayReversed = Base64.decode(input.getBytes("UTF-8"), Base64.DEFAULT);
        for (int i = 0; i < inputArrayReversed.length / 2; i++) {
            byte temp = inputArrayReversed[i];
            inputArrayReversed[i] = inputArrayReversed[inputArrayReversed.length - 1];
            inputArrayReversed[inputArrayReversed.length - 1] = temp;
        }*/

        byte[] decryptedText = null;
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, key);
        decryptedText = cipher.doFinal(Base64.decode(input.getBytes("UTF-8"), Base64.DEFAULT));
        return Base64.encodeToString(decryptedText, Base64.NO_WRAP);
        //return new String(decryptedText, "UTF-8");
    } catch (Exception e) {
        e.printStackTrace();
    }
    return "";
}
更新2


所以答案提供了一些有趣的线索

嘿,这个错误是最基本的问题之一,我们有一个架构,我们用公钥加密,用私钥解密。问题在于架构本身,因为在我们最初设置它时,我们向所有客户端应用程序发送私钥,这是一个很大的安全漏洞

我的错误是,我假设在客户端上我们有公钥,实际上是从私钥开始的,我一直在尝试加载公钥,然后进行解密

如果我深入了解PKI并与同事更好地沟通,我可能会注意到以下几点:

  • 解密只能用私钥完成,而另一方面验证可以用公钥完成,所以当我在.NET中看到在客户端上使用解密时,我应该假设在客户端上我们有私钥(这最终是我们希望使用PKI的一个安全缺陷)
我已经知道或学到并想与他人分享的几件事:

  • 私钥应该保密,无论您是想在服务器上还是最好只在一台客户机上,因为私钥很容易猜出公钥,然后有人很容易重复您的整个加密过程,从而破坏您的安全性
  • PKI适用于两种情况: 第一种情况是,当您想加密某个内容时,只有特定的个人/计算机才能解密它。在第一个场景中,如您所见,许多利益相关者可以拥有某人的公钥,并向他发送消息,只有他才能使用自己的私钥阅读这些消息。第二种情况是,您希望确保发送给您的消息没有被更改,并且是由特定的人员/计算机发送的。在这种情况下,您可以使用私钥对数据进行签名,并使用公钥在另一端验证。唯一适合我们的流程是Sign Verify,因为我们发送带有签名的纯文本许可证,因此在客户端上,我们希望确保没有人篡改纯文本许可证,并且它来自我们
  • 在代码中,如果解密或验证函数在50%的时间内抛出异常,这是因为加载了不正确的密钥或加载了不正确的密钥,而在其他50%的时间内,则是因为您使用了不正确的算法,或者是因为算法参数设置不正确,或者是因为算法实现不正确平台不兼容(最后一个非常罕见)
  • .NET服务器代码

      public string Sign(string privateKey, string data)
      {
           _rsaProvider.ImportCspBlob(Convert.FromBase64String(privateKey));
    
           //// Write the message to a byte array using UTF8 as the encoding.
           var encoder = new UTF8Encoding();
           byte[] byteData = encoder.GetBytes(data);
    
           //// Sign the data, using SHA512 as the hashing algorithm 
           byte[] encryptedBytes = _rsaProvider.SignData(byteData, new SHA1CryptoServiceProvider());
    
           return Convert.ToBase64String(encryptedBytes);
       }
    
    .NET客户端代码(Win Mobile)

    Android客户端代码:

    public boolean Verify(RSAPublicKey key, String signature, String data)
    {
        try
        {
            Signature sign = Signature.getInstance("SHA1withRSA");
            sign.initVerify(key);
            sign.update(data.getBytes("UTF-8"));
            return sign.verify(Base64.decode(signature.getBytes("UTF-8"), Base64.NO_WRAP));
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return false;
    }
    
    在.NET中,公钥以xml格式导出,代码如下:

    public string ExportPublicToXML(string publicKey)
    {
        RSACryptoServiceProvider csp = new RSACryptoServiceProvider(new CspParameters()
        {
            ProviderType = 1
        });
        csp.ImportCspBlob(Convert.FromBase64String(publicKey));
    
        return csp.ToXmlString(false);
    }
    
    然后在Android中使用模数和指数加载公钥:

    private RSAPublicKey GetPublicKey(String keyXmlString) throws InvalidKeySpecException, UnsupportedEncodingException, NoSuchAlgorithmException
    {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    
        String modulusString = keyXmlString.substring(keyXmlString.indexOf("<Modulus>"), keyXmlString.indexOf("</Modulus>")).replace("<Modulus>", "");
        String exponentString = keyXmlString.substring(keyXmlString.indexOf("<Exponent>"), keyXmlString.indexOf("</Exponent>")).replace("<Exponent>", "");
    
        byte[] modulusBytes = Base64.decode(modulusString.getBytes("UTF-8"), Base64.DEFAULT);
        byte[] dBytes = Base64.decode(exponentString.getBytes("UTF-8"), Base64.DEFAULT);
    
        BigInteger modulus = new BigInteger(1, modulusBytes);
        BigInteger d = new BigInteger(1, dBytes);
    
        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, d);
    
        return (RSAPublicKey) keyFactory.generatePublic(keySpec);
    }
    
    private rsaplickey GetPublicKey(String keyXmlString)抛出InvalidKeySpecException、UnsupportedEncodingException、nosuchagorithmException
    {
    KeyFactory-KeyFactory=KeyFactory.getInstance(“RSA”);
    String modulesString=keyXmlString.substring(keyXmlString.indexOf(“”),keyXmlString.indexOf(“”)。替换(“”,“”);
    String exponentString=keyXmlString.substring(keyXmlString.indexOf(“”),keyXmlString.indexOf(“”)。替换(“,”);
    byte[]modulesBytes=Base64.decode(modulesString.getBytes(“UTF-8”),Base64.DEFAULT);
    byte[]dBytes=Base64.decode(exponentString.getBytes(“UTF-8”),Base64.DEFAULT);
    BigInteger模数=新的BigInteger(1,ModulesBytes);
    BigInteger d=新的BigInteger(1,dBytes);
    RSAPublicKeySpec keySpec=新的RSAPublicKeySpec(模数d);
    return(RSAPublicKey)keyFactory.generatePublic(keySpec);
    }
    
    你能试着把模数换成密文吗?RSA的输出定义为一个八位字节字符串,而模是一个数字。八进制字符串没有小/大端问题,但数字有。公共指数也是如此,但因为它的值是
    010001
    ,所以它是一个二进制回文。但是,如果不是
    010001
    而是一个小的随机素数,那么您的算法仍然可能失败。如果这解决了问题,您可以发表评论,这样我就可以将此评论转换为答案。这种假设是否与我的第二个提示相反,但当然,在今天结束时,我会尝试,并让您知道。这是远距离调试。根据我现在相当丰富的经验,编码员总是会用手掌拍自己的头,我们只是为编码器提供足够的熵来保持调试:)@MaartenBodewes当我尝试反转模中的字节时,我得到java.lang.RuntimeException:error:0306E06C:bignum例程:BN_mod_inverse:no inverse注意,如果您根本没有使用正确的键(但键大小正确),填充异常也可能发生。
    public string ExportPublicToXML(string publicKey)
    {
        RSACryptoServiceProvider csp = new RSACryptoServiceProvider(new CspParameters()
        {
            ProviderType = 1
        });
        csp.ImportCspBlob(Convert.FromBase64String(publicKey));
    
        return csp.ToXmlString(false);
    }
    
    private RSAPublicKey GetPublicKey(String keyXmlString) throws InvalidKeySpecException, UnsupportedEncodingException, NoSuchAlgorithmException
    {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    
        String modulusString = keyXmlString.substring(keyXmlString.indexOf("<Modulus>"), keyXmlString.indexOf("</Modulus>")).replace("<Modulus>", "");
        String exponentString = keyXmlString.substring(keyXmlString.indexOf("<Exponent>"), keyXmlString.indexOf("</Exponent>")).replace("<Exponent>", "");
    
        byte[] modulusBytes = Base64.decode(modulusString.getBytes("UTF-8"), Base64.DEFAULT);
        byte[] dBytes = Base64.decode(exponentString.getBytes("UTF-8"), Base64.DEFAULT);
    
        BigInteger modulus = new BigInteger(1, modulusBytes);
        BigInteger d = new BigInteger(1, dBytes);
    
        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, d);
    
        return (RSAPublicKey) keyFactory.generatePublic(keySpec);
    }