问一个 Java 的 RSA 解密的问题

2017-09-04 16:18:16 +08:00
 linuxchild

在安卓上使用 RSA 算法加密了一个字符串,然后 Base64 了一下;

在 Mac 上使用 Java 先转码再解密这个加密后转码的字符串,遇到了如下错误:

javax.crypto.BadPaddingException: Decryption error
        at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:380)
        at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:291)
        at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:363)
        at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
        at javax.crypto.Cipher.doFinal(Cipher.java:2165)
        at RSAUtils.decryptData(RSAUtils.java:97)
        at Decrypt.main(Decrypt.java:62)

其中,加密使用的模块如下:

   public static byte[] encryptData(byte[] data, PublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA);
            // 编码前设定编码方式及密钥
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            // 传入编码数据并返回编码结果
            return cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

解密使用的模块如下:

    public static byte[] decryptData(byte[] encryptedData, PrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher.doFinal(encryptedData);
        } catch (Exception e) {
            e.printStackTrace();
            //System.out.println(e.printStackTrace());
            return null;
        }
    }

解密过程使用了 BC 模块来读取 pem 格式( pkcs8 )的私钥文件(加解密过程中均使用了同一模块进行 Base64 操作),调用解密的代码如下:

 KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
 PrivateKey privateKey = generatePrivateKey(factory, "private_key.pem");

 String encryptContent = "xxxxx";
 byte[] decryptByte = RSAUtils.decryptData(Base64Utils.decode(encryptContent), privateKey);
 

 String decryptStr = new String(decryptByte);   
 System.out.println(decryptStr);

已经谷歌了一下,但是没有找到合适的办法,不知哪位有接触过这个,希望不吝赐教,感谢。

5035 次点击
所在节点    Java
9 条回复
ipeony
2017-09-04 17:16:50 +08:00
试了下这样可以的,看有没有帮助

<script src="https://gist.github.com/dongfg/cba825a96a416924698b6be02b34c054.js"></script>
linuxchild
2017-09-06 19:35:30 +08:00
@ipeony 先感谢,回头试一下看看行不行
linuxchild
2017-09-07 14:23:34 +08:00
@ipeony 仔细看了看代码,加解密模块都一样,只是解密时私钥都读取不一样;

我使用都是 BC 提供去读取都 pem 格式的私钥,是不是我的读取过程有问题?
ipeony
2017-09-07 14:31:53 +08:00
@linuxchild #3 我试了下用 BC 读也是可以的,你的密钥对怎么生成的
linuxchild
2017-09-07 16:03:05 +08:00
@ipeony 同事给的,相同的密钥和文本使用 Python 直接解出来了…

帮忙看看使用 bc 去读私钥我贴的那段代码有问题么? pkcs8 格式的私钥
ipeony
2017-09-07 20:09:06 +08:00
@linuxchild #5 我没看出问题~感觉跟 BC 那个关系不大,可能是公钥私钥格式上的问题,我理解也不深
这是我生成密钥对的命令
----
openssl genrsa -out key_private.pem 4096
openssl rsa -pubout -in key_private.pem -out key_public.pem
openssl pkcs8 -topk8 -in key_private.pem -inform pem -outform pem -out key_private_pkcs8.pem -nocrypt
----
String privateKeyStr = FileUtils.readFileToString(new File("key_private_pkcs8.pem"), UTF_8);
String publicKeyStr = FileUtils.readFileToString(new File("key_public.pem"), UTF_8);

privateKeyStr = privateKeyStr.replaceAll("\\n", "").replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "");
publicKeyStr = publicKeyStr.replaceAll("\\n", "").replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "");

if (Security.getProvider("BC") == null) {
Security.addProvider(new BouncyCastleProvider());
}

KeyFactory kf = KeyFactory.getInstance("RSA","BC");


PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyStr));
PrivateKey privateKey = kf.generatePrivate(keySpecPKCS8);

X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyStr));
RSAPublicKey publicKey = (RSAPublicKey) kf.generatePublic(keySpecX509);

System.out.println(publicKey.getFormat());
System.out.println(privateKey.getFormat());

String rawString = "aaa";
byte[] encryptData = encryptData(rawString.getBytes(), publicKey);

byte[] decryptData = decryptData(encryptData, privateKey);
Assert.assertNotNull(decryptData);


String decryptString = new String(decryptData);

Assert.assertEquals(rawString, decryptString);
linuxchild
2017-09-08 13:30:23 +08:00
@ipeony

感谢给这么详细耐心的回复。

试了一下这段代码,用的自己的私钥和公钥,一串明文进行加解密没问题。

但是如果用这段代码去解密之前的密文就出现所述的问题了…

另外,我之前用 Python 试过,同样的密文,同样的密钥,就可以解出来,擦

我看看再找个人问一下,等解决了告诉你一下。

再次感谢
linuxchild
2017-09-08 16:21:38 +08:00
@ipeony

hello,问题解决了,是将解密的代码改动了一行:

```
public static byte[] decryptData(byte[] encryptedData, PrivateKey privateKey) {

try {
Cipher cipher = Cipher.getInstance(RSA, new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(encryptedData);
} catch (Exception e) {
e.printStackTrace();
//System.out.println(e.printStackTrace());
return null;
}
}
```

前提是把 bc 包引入,我使用动办法是

> 1. edit jre\lib\security\java.security

> add security.provider.10=org.bouncycastle.jce.provider.BouncyCastleProvider

> 2. copy bc*.jar to jre\lib\ext

摘自:
https://stackoverflow.com/questions/13721579/jce-cannot-authenticate-the-provider-bc-in-java-swing-application

然后再进行解密就可以了。


至于原因,我猜测在加密动时候可能就使用了这个模块。
ipeony
2017-09-08 17:00:23 +08:00
@linuxchild #8 学习了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/388067

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX