首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
华为云
V2EX  ›  Java

问一个 Java 的 RSA 解密的问题

  •  
  •   linuxchild · 350 天前 · 1930 次点击
    这是一个创建于 350 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在安卓上使用 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);
    

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

    9 回复  |  直到 2017-09-08 17:00:23 +08:00
        1
    ipeony   350 天前   ♥ 1
    试了下这样可以的,看有没有帮助

    <script src=".js"></script>
        2
    linuxchild   348 天前
    @ipeony 先感谢,回头试一下看看行不行
        3
    linuxchild   347 天前
    @ipeony 仔细看了看代码,加解密模块都一样,只是解密时私钥都读取不一样;

    我使用都是 BC 提供去读取都 pem 格式的私钥,是不是我的读取过程有问题?
        4
    ipeony   347 天前
    @linuxchild #3 我试了下用 BC 读也是可以的,你的密钥对怎么生成的
        5
    linuxchild   347 天前
    @ipeony 同事给的,相同的密钥和文本使用 Python 直接解出来了…

    帮忙看看使用 bc 去读私钥我贴的那段代码有问题么? pkcs8 格式的私钥
        6
    ipeony   347 天前
    @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);
        7
    linuxchild   346 天前
    @ipeony

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

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

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

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

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

    再次感谢
        8
    linuxchild   346 天前
    @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

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


    至于原因,我猜测在加密动时候可能就使用了这个模块。
        9
    ipeony   346 天前
    @linuxchild #8 学习了
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   3214 人在线   最高记录 3762   ·  
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.1 · 19ms · UTC 02:51 · PVG 10:51 · LAX 19:51 · JFK 22:51
    ♥ Do have faith in what you're doing.
    沪ICP备16043287号-1