在Java中重新实现RSA解密

在Java中重新实现RSA解密,java,rsa,padding,Java,Rsa,Padding,我使用一个私有RSA密钥加密一个带有默认Java RSA实现的随机AES密钥: Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, privateKey); byte[] cipherBytes = cipher.doFinal(plainText.getBytes()); 因为我们无论如何都需要一个公钥,所以这是一个方便的方法来伪装密钥,并确保它已经用我们的私钥加密。解密过程与此类似: Cip

我使用一个私有RSA密钥加密一个带有默认Java RSA实现的随机AES密钥:

Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] cipherBytes = cipher.doFinal(plainText.getBytes());
因为我们无论如何都需要一个公钥,所以这是一个方便的方法来伪装密钥,并确保它已经用我们的私钥加密。解密过程与此类似:

Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] plainBytes = cipher.doFinal(cipherBytes);
这在Oracle的JDK中可以正常工作,但在IBM的JDK中却失败了,因为IBM认为使用私钥进行加密不是一个有效的用例。不幸的是,我必须同时支持这两种JDK,所以我试图自己重新实现RSA解密

这是我目前掌握的代码:

BigInteger big = new BigInteger(cipherBytes);
big = big.modPow(pub.getPublicExponent(), pub.getModulus()); 
System.out.println(new String(big.toByteArray()));
它几乎可以工作,但似乎有一个填充问题。大多数情况下,我得到的原始文本前面有一串点状符号,但有时只是随机字节

不幸的是,我无法找出默认情况下使用的填充方案。是否有人知道我的代码中缺少了什么,或者至少可以给出一个提示,用哪种算法来处理填充

下面是一个输入和输出值的示例,根据要求。我使用了512位键来避免过大的数字

Public modulus :  8117919732251191237549784557538073836207094968952416063837701691514861428726690140363567956265691836505266266364256892197254736023284927189008247933889303
Public exponent:  65537
Plaintext:        teststring
Plaintext as BN:  549665952565679142563431
Ciphertext as BN: 6304229782339071167863563708554898540621778162930150363326921290545577949349781053660336996882823758722402137580193903457839924005473545992074817339077456
"Decrypted" BN:   409173825987017733751648712103449894027080255755383098685411421012016724550584319360408761540738019643860835515945008876151848132891805352276483731047
Resultstring: ˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇteststring
在讨论中,我为什么要这样做:

公钥已硬编码到我的软件中。我使用私钥加密AES的另一个密钥。因此,要真正用AES解码任何东西,首先需要AES密钥。要获取此密钥,必须首先使用公钥对其进行解密。由于在不进行严重操作的情况下无法修改公钥,因此只有使用私钥加密的AES密钥才能工作。您可能会以某种方式提取公钥并解密AES密钥,但这很复杂,只能获得AES密钥来解密安全内容。还有一个用私钥计算的签名,它也用公钥进行验证。所以操纵是不可能的


是的,从技术上来说,签名就足够了,因为有方法可以读取内容。但这些都是精心设计的,我不介意是否有人真的要费心,但我不想让事情变得简单。

您试图做的更像是签名而不是加密。使用单独的密钥对进行签名,因为它与加密不完全相同。

公钥用于加密和验证签名。私钥用于解密和签名。公钥的用途就是:Public。如果你做得对,应该没有理由隐藏公钥。

好的,我通过阅读RSA规范找到了答案。为了增加安全性,在加密之前添加了一个填充,并创建了以下“字符串”:

0x00 + BT + Padding + 0x00 + Data
块类型(BT)指示填充的类型。对于BT=0x01,填充为0xff;对于BT=0x02,填充为随机但非零。然后对连接的字符串进行加密

解密时,可以验证格式,但要仅读取数据,必须删除前导字节。在数据之前的0x00之前,它们都是非零的。因此,可以删除填充之前和之后的所有内容,包括0x00。剩下的是信息

此代码现在起作用:

// Decrypt
byte[] decryptedBytes = (new BigInteger(1, cipherBytes)).modPow(pub.getPublicExponent(), pub.getModulus()).toByteArray();

// Extract msg
int dataStart;
for (dataStart = 0; decryptedBytes[msgStart] != 0; dataStart++);
dataStart++;

byte finalBytes[] = new byte[decryptedBytes.length - msgStart];
System.arraycopy(decryptedBytes, msgStart, finalBytes, 0, finalBytes.length);
这也解释了我以前尝试中的“^”字符串。这些是填充字节,即BT=0x01的0xff

我只需要解密,但为了完整起见,这是加密代码:

int bitLength = 512;
String plainText = "teststring";

// Convert to bytes
byte plainBytes[] = plainText.getBytes(); 

byte encryptionBytes[] = new byte[bitLength / 8];

encryptionBytes[0] = 0; // Leading 0
encryptionBytes[1] = 1; // Block type

// Padding String
int paddingEnd = (bitLength / 8) - plainBytes.length - 2;
for (int i = 2; i < paddingEnd; i++) {
    encryptionBytes[i] = (byte) 0xff;
}
encryptionBytes[paddingEnd + 1] = 0;

// Actual data
System.arraycopy(plainBytes, 0, encryptionBytes, paddingEnd + 1, plainBytes.length);

// Encrypt
byte[] cipherBytes = (new BigInteger(1, encryptionBytes)).modPow(priv.getPrivateExponent(), priv.getModulus()).toByteArray();
int位长度=512;
String plainText=“teststring”;
//转换为字节
字节纯字节[]=纯文本。getBytes();
byte encryptionBytes[]=新字节[bitLength/8];
encryptionBytes[0]=0;//领先0
encryptionBytes[1]=1;//块类型
//填充字符串
int paddingEnd=(位长度/8)-plainBytes.length-2;
for(int i=2;i

希望这对任何人都有帮助:)

这不是一个有效的用例。一件事必须是私人的,另一件必须是公共的。当然,您可以发出“私钥”并将“公钥”保持为私钥,但这很愚蠢。为了理解填充算法,PKCS1可能是一个很好的起点,您还可以检查PKCS5和PKCS7填充标准。我已经检查了PKCS填充,我认为没有使用它。padding bytes是填充的字节数,因此必须存在重复模式。不幸的是,事实并非如此。您可以发布一些输入/输出的示例吗?为什么不安全?我必须承认我已经有一段时间没有涉足密码学了,但我从来没有在任何地方看到过这一点。我不想隐藏公钥。我想像签名一样使用私钥加密。我没有在HMAC中使用真正的签名,因为仅通过使用RSA,“签名”内容也会被加密。我知道你可以用公钥解密,但公钥是硬编码到软件中的,所以你需要私钥来加密任何东西,以便软件正确解密。等等,你是在与使用公钥解密的第三方软件通信吗?如果是这样,你应该提前说明。我也不清楚为什么你说使用RSA会强制对签名内容进行加密,因为那不是真的。发送明文加上签名是很常见的。你的答案实际上是一条评论。