【问题标题】:In RSA Encrypting working in Java but not working with encrypting from c#?在 RSA Encrypting 中使用 Java 但不能使用 c# 加密?
【发布时间】:2020-12-18 08:39:14
【问题描述】:

RSA加密和解密在java端运行良好,模数和指数如下:

Java RSA 模数和指数:

String nModulusPublic = "AJ+L/dVL9jnRX6IM87H8x2fR24t6wpzBDV7bcgPWblegR0LNK91z/OSX+lSLUgHSKJ9to/Eo8OMsREpNoJlEzI0=";
String eExponentPublic = "AQAB";
String eExponentPrivate = "AIpmE5C9TiAlgYG/Hn5dOlTS9FFv8fWseX65eZPepOUY4ivxN0lOZ+MsugZd03wmKvnxBuCGu5nv2qrUBTPzjcE=";

Java 公钥和私钥生成器:

static PublicKey GetPublicKey(String publicKString, String publicExponentStr) throws Exception {
    byte[] modulusBytes = Base64.getDecoder().decode(publicKString);
    byte[] exponentBytes = Base64.getDecoder().decode(publicExponentStr);
    BigInteger modulus = new BigInteger(1, modulusBytes);
    BigInteger exponent = new BigInteger(1, exponentBytes);

    RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, exponent);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PublicKey pubKey = fact.generatePublic(rsaPubKey);
    return pubKey;
}

static PrivateKey GetPrivateKey(String nModulusPublic, String eExponentPrivate) throws Exception {
    byte[] modulusBytes = Base64.getDecoder().decode(nModulusPublic);
    byte[] exponentBytes = Base64.getDecoder().decode(eExponentPrivate);
    BigInteger modulus = new BigInteger(1, modulusBytes);
    BigInteger exponent = new BigInteger(1, exponentBytes);

    RSAPrivateKeySpec privSpec = new RSAPrivateKeySpec(modulus, exponent);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PrivateKey privKey = fact.generatePrivate(privSpec);
    return privKey;
}

我在 c# 中使用 nModulusPublic 和 eExponentPublic 在 Java 中进行加密和解密,但不起作用。 从事 RSA.Encrypt(textBytes, true); c# 函数中的参数将其更改为 false 和 RSAEncryptionPadding.Pkcs1 但不起作用。当我在 java 中使用 c# Encrypt 函数的结果进行解密时,总是遇到这个错误:

javax.crypto.IllegalBlockSizeException: Data must not be longer than 64 bytes

C# 加密函数:

static string Encrypt(string text)
    {
        string outputB64 = string.Empty;
        byte[] textBytes = Encoding.UTF8.GetBytes(text);

        RSAParameters result = new RSAParameters()
        {
            Modulus = Convert.FromBase64String(nModulusPublic),
            Exponent = Convert.FromBase64String(eExponentPublic)
        };

        using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
        {
            RSA.ImportParameters(result);
            byte[] encryptedData = RSA.Encrypt(textBytes, true);
            outputB64 = Convert.ToBase64String(encryptedData);
        }          
        
        return outputB64;
    }

Extra Information,Java Encrypt 和 Decrypt 主要功能:

 static String Decrypt(String encodedString,PrivateKey privKey) {
    try {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privKey);
        byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encodedString));
        return new String(decrypted, "UTF-8");
    } catch (Exception err) {
        return err.fillInStackTrace().toString();
    }
}
 static String Encrypt(String encodedString,PublicKey pubKey) {
    try {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        byte[] plainBytes = new String(encodedString).getBytes("UTF-8");
        byte[] cipherData = cipher.doFinal(plainBytes);
        String encryptedString = Base64.getEncoder().encodeToString(cipherData);
        return encryptedString;
    } catch (Exception err) {
        return err.fillInStackTrace().toString();
    }
}

更新:

我正在研究它,发现java加密函数和c#有两种不同的类型,Java结果总是以“==”结尾但c#函数最后有一个“=”,看来这就是问题所在。

C#加密函数结果:

AJiltxqa1/8HU20XZlKJsJvclQ8PyQetpWdbCOpbqrXVg0q
/v4x5tXLxbzGKbO5InvKkib7tDQp+9BU0SYbZLv0=

Java Encrypt 函数结果:

RlarFQBo2mcCWjidQ5l7ho2EOG6KGQWpR3ByXXHsGo6+HRQzmO4v7
TUTMdfB9wjI3aO6quruSReitrWu7QF9Vw==

【问题讨论】:

    标签: java c# encryption rsa


    【解决方案1】:

    在 C# 加密函数中,您提供参数 'true':

    byte[] encryptedData = RSA.Encrypt(textBytes, true);
    

    这意味着 C# 不使用 PKCS1Padding 而是使用 OEAPPadding。

    只需在您的 Decrypt 方法(以及您的 ENCRYPT 方法 :-) 中更改 Java 端

    // change:
    //Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    // to
    Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING");
    

    我用您的密钥对对其进行了测试,它可以正常工作。

    编辑: 在 C# 端,我运行以下代码:

    string plaintext = "The quick brown fox";
    string encryptedDataBase64So = Encrypt(plaintext);
    Console.WriteLine("encrypted SO : " + encryptedDataBase64So);
    
    Console output:
    encrypted SO : ZLylMsqcqbuDM7DprrmqIU8c8Q79fPXHudOY4INCNAo+iU7Oor3mZ8i+PP5PjtDkifqAXKYT8ON/ia9WjEFqRQ==
    

    在 Java 端,我将 base64-String 作为输入:

    String fromCsharp = "Ew3nTEQuOX1tWfRNJEERa75A1o2bn6+HurVPYzGzA7kt+HAZAMdXKNACY2emvU6Bf42i8zpBO89lqvzuxNmRIw==";
    String decryptedtext = DecryptWorking(fromCsharp, privateKey);
    System.out.println("\ndecrypted from c#: " + decryptedtext);
    
    Console output:
    decrypted from c#: The quick brown fox
    

    顺便说一句:这是我从 Java 端的 PublicKey 生成的 C# 端的 PublicKey,并像这样使用它作为 RSA 加密的源:

    var publicKey = "<RSAKeyValue><Modulus>n4v91Uv2OdFfogzzsfzHZ9Hbi3rCnMENXttyA9ZuV6BHQs0r3XP85Jf6VItSAdIon22j8Sjw4yxESk2gmUTMjQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
    RSA.FromXmlString(publicKey);
    

    【讨论】:

    • 已测试但无法正常工作,能否分享一下 c# encrypt 函数的输出。
    • @motevalizadeh 请用测试数据查看我编辑的答案。
    • 感谢您的宝贵时间,但我无法处理,您的代码中有一些我没有如果可以在 Java 端共享您的完整源代码,您是否使用我的 Java 函数创建公钥和私钥?
    • 我使用您的密钥和您的 GetPrivate/PublicKey 方法在 Java 端加密和解密。仅在 C# 端,我更改了代码以将 PublicKey 加载到 RSA,因为我将您的 PublicKey 重建为 XML 字符串(上面显示了 onlineer。要使其正常工作,您还需要更改 Java-padding。
    【解决方案2】:

    除了 Michael Fehr 的 answer 中详细描述的填充错误之外,还有另一个问题实际上是导致错误消息 IllegalBlockSizeException: Data must not be long than 64 bytes. 不一致的填充如果不首先抛出 IllegalBlockSizeException,则会抛出 BadPaddingException

    发布的密文有一个前导 0 字节,因此大小为 65 个字节:

    Base64: AJIltxqa1/8HU20XZlKJsJvclQ8PyQetpWdbCOpbqrXVg0q/v4x5tXLxbzGKbO5InvKkib7tDQp+9BU0SYbZLv0= 十六进制:0098a5b71a9ad7ff07536d17665289b09bdc950f0fc907ada5675b08ea5baab5d5834abfbf8c79b572f16f318a6cee489ef2a489beed0d0a7ef415344986d92efd

    如果您尝试在 Java 端解密此密文,您将收到以下错误消息:IllegalBlockSizeException: Data must not be long than 64 bytes.

    为什么 C# 代码生成的密文过长?这是因为模数也有前导 0 字节,因此长度为 65 个字节:

    Base64:AJ+L/dVL9jnRX6IM87H8x2fR24t6wpzBDV7bcgPWblegR0LNK91z/OSX+lSLUgHSKJ9to/Eo8OMsREpNoJlEzI0= 十六进制:009f8bfdd54bf639d15fa20cf3b1fcc767d1db8b7ac29cc10d5edb7203d66e57a04742cd2bdd73fce497fa548b5201d2289f6da3f128f0e32c444a4da09944cc8d

    模数是使用BigInteger.toByteArray() 导出的(参见this question更新部分),它返回二进制补码表示,如果最前面的字节有值,则在前面放置一个前导 0 字节大于 0x7f。

    模数中的前导 0 字节导致由 C# 代码生成的密文,该密文也具有前导 0 字节,因此 65 字节的长度无效。这没有多大意义,可能是一个错误。

    为了解决这个问题,C# 代码中的模数中的 0 字节应该被删除,从而产生以下 Base64 编码的模数(这将产生 64 字节的密文):

    n4v91Uv2OdFfogzzsfzHZ9Hbi3rCnMENXttyA9ZuV6BHQs0r3XP85Jf6VItSAdIon22j8Sjw4yxESk2gmUTMjQ==

    或者可以删除密文中的 0 字节,得到以下密文(Base64 编码):

    mKW3GprX/wdTbRdmUomwm9yVDw/JB62lZ1sI6luqtdWDSr+/jHm1cvFvMYps7kie8qSJvu0NCn70FTRJhtku/Q==

    现在可以通过 Java 代码成功将其解密为明文(如果应用了一致的填充,请参阅 Michael Fehr 的 answer):

    达沃德

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-17
      • 2021-09-16
      • 1970-01-01
      • 2012-11-10
      • 1970-01-01
      • 2013-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多