提问者:小点点

在RSA加密在Java中工作,但不使用从c#加密?


RSA加密和解密在java方面工作良好,取模和指数如下:

JavaRSA模和指数:

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(text Bytes,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;
    }

额外信息,Java加密和解密主要功能:

 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=

JavaEncrypt函数结果:

RlarFQBo2mcCWjidQ5l7ho2EOG6KGQWpR3ByXXHsGo6+HRQzmO4v7
TUTMdfB9wjI3aO6quruSReitrWu7QF9Vw==

共2个答案

匿名用户

在C#加密函数上,您给参数“true”:

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

这意味着C#使用的不是PKCS1P填充,而是OEAPP填充。

只需在Decrypt-method(以及ENCRYPT-method:-)的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

BTW:这是C#端的PublicKey,我从Java端的PublicKey生成,并将其用作RSA加密的源代码:

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

匿名用户

除了Michael Fehr回答中详细描述的填充错误之外,还有一个问题实际上是错误消息IllegalBlockSizeException的原因:数据不得超过64字节。如果不首先抛出IllegalBlockSizeException,则不一致的填充将抛出BadPaddingException。

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

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

如果您尝试在Java端解密此密文,您将收到已发布的错误:IllegalBlockSizeException:数据不得超过64字节。

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

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

模数是使用BigInteger. toByteArray()派生的(参见此问题,更新部分),它返回二进制补码表示,如果最前面的字节的值大于0x7f,则将前导0字节放在前面。

模数中的前导0字节导致由C#代码生成的密文,该密文也具有前导0字节,因此长度为65字节。这没有太大意义,可能是bug。

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

n4v91Uv2OdFfogzzsfzHZ9Hbi3rCnMENXttyA9ZuV6BHQs0r3XP85Jf6VItSAdIon22j8Sjw4yxESk2gmUTMjQ==

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

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

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

Davood