在Java中进行RSA加密工作正常,但从C#加密时不起作用。

huangapple go评论62阅读模式
英文:

In RSA Encrypting working in Java but not working with encrypting from c#?

问题

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 {
    // ...(代码细节省略)
}

static PrivateKey GetPrivateKey(String nModulusPublic, String eExponentPrivate) throws Exception {
    // ...(代码细节省略)
}

我在C#中使用nModulusPublic和eExponentPublic进行加密和解密,但无法正常工作。在C#函数的RSA.Encrypt(textBytes, true);参数中将其改为false,并将RSAEncryptionPadding.Pkcs1但仍无法工作。当我在Java中使用C# Encrypt函数的结果进行解密时,总是遇到以下错误:

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

C#加密函数:

static string Encrypt(string text) {
    // ...(代码细节省略)
}

附加信息,Java加密和解密主要函数:

static String Decrypt(String encodedString, PrivateKey privKey) {
    // ...(代码细节省略)
}

static String Encrypt(String encodedString, PublicKey pubKey) {
    // ...(代码细节省略)
}

更新:

我在研究中发现Java的加密函数和C#具有两种不同类型,Java的结果总是以"=="结束,而C#函数末尾只有一个"=",这似乎是问题所在。

C#加密函数结果:

AJiltxqa1/8HU20XZlKJsJvclQ8PyQetpWdbCOpbqrXVg0q/v4x5tXLxbzGKbO5InvKkib7tDQp+9BU0SYbZLv0=

Java加密函数结果:

RlarFQBo2mcCWjidQ5l7ho2EOG6KGQWpR3ByXXHsGo6+HRQzmO4v7TUTMdfB9wjI3aO6quruSReitrWu7QF9Vw==
英文:

RSA encryption and decryption work well in java side with modulus and exponent as below:

Java RSA Modulus and Exponent:

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

Java Public and Private Key Generators:

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

I use nModulusPublic and eExponentPublic in c# to encrypting and decrypting in Java but doesn't work.
Worked on RSA.Encrypt(textBytes, true); parameters in c# function change it to false and RSAEncryptionPadding.Pkcs1 but doesn't' work. When I use the result of the c# Encrypt function in java to decrypt it always encounter with this error :

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

C# encrypt function:

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 and Decrypt main functions:

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

Update:

I was working on it and found that java encrypts function and c# have two different types, Java result always ended with "==" but c# function have one "=" at the end, It seems this is the problem.

C# Encrypt function result:

AJiltxqa1/8HU20XZlKJsJvclQ8PyQetpWdbCOpbqrXVg0q
/v4x5tXLxbzGKbO5InvKkib7tDQp+9BU0SYbZLv0=

Java Encrypt function result:

RlarFQBo2mcCWjidQ5l7ho2EOG6KGQWpR3ByXXHsGo6+HRQzmO4v7
TUTMdfB9wjI3aO6quruSReitrWu7QF9Vw==

答案1

得分: 2

关于C#的加密函数,你提供了参数 'true':

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

这意味着C#并没有使用PKCS1填充,而是使用了OAEP填充。

只需在Java端的Decrypt方法中(以及Encrypt方法中)修改以下行:

// 将
// Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// 修改为
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING");

我使用了你的密钥对进行测试,结果如预期。

编辑:
在C#端,我运行了这段代码:

string plaintext = "The quick brown fox";
string encryptedDataBase64So = Encrypt(plaintext);
Console.WriteLine("encrypted SO: " + encryptedDataBase64So);

控制台输出:

encrypted SO: ZLylMsqcqbuDM7DprrmqIU8c8Q79fPXHudOY4INCNAo+iU7Oor3mZ8i+PP5PjtDkifqAXKYT8ON/ia9WjEFqRQ==

在Java端,我以Base64字符串作为输入:

String fromCsharp = "Ew3nTEQuOX1tWfRNJEERa75A1o2bn6+HurVPYzGzA7kt+HAZAMdXKNACY2emvU6Bf42i8zpBO89lqvzuxNmRIw==";
String decryptedtext = DecryptWorking(fromCsharp, privateKey);
System.out.println("\ndecrypted from c#: " + decryptedtext);

控制台输出:

decrypted from c#: The quick brown fox

顺便提一下:这是C#端的公钥,我从Java端生成的公钥中提取并将其用作RSA加密的源:

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

On C# encrypt function you give the parameter 'true':

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

This means that C# is NOT using the PKCS1Padding but the OEAPPadding.

Simply change on Java-side in your Decrypt-method (and in your ENCRYPT-method as well 在Java中进行RSA加密工作正常,但从C#加密时不起作用。 the line

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

I tested it with your keypair and it works like expected.

Edit:
On C#-side I run this code:

string plaintext = &quot;The quick brown fox&quot;;
string encryptedDataBase64So = Encrypt(plaintext);
Console.WriteLine(&quot;encrypted SO : &quot; + encryptedDataBase64So);

Console output:
encrypted SO : ZLylMsqcqbuDM7DprrmqIU8c8Q79fPXHudOY4INCNAo+iU7Oor3mZ8i+PP5PjtDkifqAXKYT8ON/ia9WjEFqRQ==

On Java-side I took the base64-String as input:

String fromCsharp = &quot;Ew3nTEQuOX1tWfRNJEERa75A1o2bn6+HurVPYzGzA7kt+HAZAMdXKNACY2emvU6Bf42i8zpBO89lqvzuxNmRIw==&quot;;
String decryptedtext = DecryptWorking(fromCsharp, privateKey);
System.out.println(&quot;\ndecrypted from c#: &quot; + decryptedtext);

Console output:
decrypted from c#: The quick brown fox

BTW: this is the PublicKey on C#-side that I generated from the PublicKey on Java-side and used it like this as source for the RSA-Encryption:

var publicKey = &quot;&lt;RSAKeyValue&gt;&lt;Modulus&gt;n4v91Uv2OdFfogzzsfzHZ9Hbi3rCnMENXttyA9ZuV6BHQs0r3XP85Jf6VItSAdIon22j8Sjw4yxESk2gmUTMjQ==&lt;/Modulus&gt;&lt;Exponent&gt;AQAB&lt;/Exponent&gt;&lt;/RSAKeyValue&gt;&quot;;
RSA.FromXmlString(publicKey);

答案2

得分: 1

除了Michael Fehr的答案中详细描述的填充错误之外,还有另一个问题实际上导致了错误消息IllegalBlockSizeException: Data must not be longer than 64 bytes. 不一致的填充会在首先引发IllegalBlockSizeException之前引发BadPaddingException

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

<pre>
Base64: AJiltxqa1/8HU20XZlKJsJvclQ8PyQetpWdbCOpbqrXVg0q/v4x5tXLxbzGKbO5InvKkib7tDQp+9BU0SYbZLv0=
Hex: 0098a5b71a9ad7ff07536d17665289b09bdc950f0fc907ada5675b08ea5baab5d5834Abf8c79b572f16f318a6cee489ef2a489beed0d0a7ef415344986d92efd
</pre>

如果您在Java端尝试解密此密文,您将获得发布的错误:IllegalBlockSizeException: Data must not be longer than 64 bytes.

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

<pre>
Base64: AJ+L/dVL9jnRX6IM87H8x2fR24t6wpzBDV7bcgPWblegR0LNK91z/OSX+lSLUgHSKJ9to/Eo8OMsREpNoJlEzI0=
Hex: 009f8bfdd54bf639d15fa20cf3b1fcc767d1db8b7ac29cc10d5edb7203d66e57a04742cd2bdd73fce497fa548b5201d2289f6da3F128f0e32c444a4da09944cc8d
</pre>

模数是使用BigInteger.toByteArray()推导出的(参见这个问题Update section),它返回二进制补码表示,并在前导字节的值大于0x7f时在前面放置一个前导0字节。

模数中的前导0字节导致C#代码生成的密文也有一个前导0字节,从而长度为65字节,这没有太多意义,可能是一个错误。

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

<pre>
n4v91Uv2OdFfogzzsfzHZ9Hbi3rCnMENXttyA9ZuV6BHQs0r3XP85Jf6VItSAdIon22j8Sjw4yxESk2gmUTMjQ==
</pre>

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

<pre>
mKW3GprX/wdTbRdmUomwm9yVDw/JB62lZ1sI6luqtdWDSr+/jHm1cvFvMYps7kie8qSJvu0NCn70FTRJhtku/Q==
</pre>

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

<pre>
Davood
</pre>

英文:

Besides the padding error described in detail in Michael Fehr's answer, there is another issue which is actually responsible for the error message IllegalBlockSizeException: Data must not be longer than 64 bytes. The inconsistent padding would throw a BadPaddingException if the IllegalBlockSizeException would not be thrown first.

The posted ciphertext has a leading 0 byte and is therefore 65 bytes in size:

<pre>
Base64: AJiltxqa1/8HU20XZlKJsJvclQ8PyQetpWdbCOpbqrXVg0q/v4x5tXLxbzGKbO5InvKkib7tDQp+9BU0SYbZLv0=
Hex: 0098a5b71a9ad7ff07536d17665289b09bdc950f0fc907ada5675b08ea5baab5d5834abfbf8c79b572f16f318a6cee489ef2a489beed0d0a7ef415344986d92efd
</pre>

If you try to decrypt this ciphertext on the Java side, you will get the posted error: IllegalBlockSizeException: Data must not be longer than 64 bytes.

Why does the C# code produce a too long ciphertext? This is because of the modulus, which also has a leading 0 byte and therefore a length of 65 bytes:

<pre>
Base64: AJ+L/dVL9jnRX6IM87H8x2fR24t6wpzBDV7bcgPWblegR0LNK91z/OSX+lSLUgHSKJ9to/Eo8OMsREpNoJlEzI0=
Hex: 009f8bfdd54bf639d15fa20cf3b1fcc767d1db8b7ac29cc10d5edb7203d66e57a04742cd2bdd73fce497fa548b5201d2289f6da3f128f0e32c444a4da09944cc8d
</pre>

The modulus was derived with BigInteger.toByteArray() (see this question, Update section), which returns the two's-complement representation and places a leading 0 byte in front if the frontmost byte has a value larger than 0x7f.

The leading 0 byte in the modulus results in a ciphertext generated by the C# code, which also has a leading 0 byte and thus an invalid length of 65 bytes. This does not make much sense and might be a bug.

To solve the problem the 0 byte in the modulus should be removed for the C# code, resulting in the following Base64 encoded modulus (which will produce 64 bytes ciphertexts):

<pre>
n4v91Uv2OdFfogzzsfzHZ9Hbi3rCnMENXttyA9ZuV6BHQs0r3XP85Jf6VItSAdIon22j8Sjw4yxESk2gmUTMjQ==
</pre>

Alternatively the 0 byte in the ciphertext can be removed, resulting in the following ciphertext (Base64 encoded):

<pre>
mKW3GprX/wdTbRdmUomwm9yVDw/JB62lZ1sI6luqtdWDSr+/jHm1cvFvMYps7kie8qSJvu0NCn70FTRJhtku/Q==
</pre>

which can now be successfully decrypted by the Java code to the plaintext (if a consistent padding is applied, see Michael Fehr's answer):

<pre>
Davood
</pre>

huangapple
  • 本文由 发表于 2020年8月30日 02:08:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/63650264.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定