美文网首页
关于RSA私钥解密遇到的问题

关于RSA私钥解密遇到的问题

作者: 钦_79f7 | 来源:发表于2019-12-20 16:32 被阅读0次

    关于RSA私钥解密遇到的问题

    [TOC]

    生成密钥对过程遇到的问题

    生成密钥

    通过openssl生成密钥对的过程中,要注意编码的问题。

    1. 生成私钥

    openssl genrsa -out private_key.pem 1024

    1. 生成公钥

    openssl rsa -in private_key.pem -pubout -out public_key.pem

    1. 私钥转换编码

    openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_rsa_private_key.pem -nowcrypt

    一般私钥在使用中,需要进行PKCS8编码。

    -nocrypt 不采用任何二次加密

    问题&解决

    最开始,直接使用了未经转码的私钥文件,即 pkcs1 格式的私钥。由于此密钥对在Android 中进行单元测试时,是可以正常加解密的,就以此作为结果写入文档了。

    后来在与Server童靴对接过程中,反应说是利用我给出的私钥串,无法生成私钥,直接报错如下:

    java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
    

    随后,我本地马上采用纯Java的API写了一套逻辑,试了一下,发现确实存在这个问题。

            @Throws(Exception::class)
        fun loadPrivateKey(privateKeyStr: String): PrivateKey {
            try {
                    // Android API
                //val buffer = Base64.decode(privateKeyStr, Base64.NO_WRAP)
               // Java API 
                val buffer = Base64.getDecoder().decode(privateKeyStr)
                val keySpec = PKCS8EncodedKeySpec(buffer)
                val keyFactory = KeyFactory.getInstance(RSA)
                return keyFactory.generatePrivate(keySpec)
            } catch (e: NoSuchAlgorithmException) {
                throw Exception("无此算法")
            } catch (e: InvalidKeySpecException) {
                throw Exception("私钥非法")
            } catch (e: NullPointerException) {
                throw Exception("私钥数据为空")
            }
        }
    

    由于代码区分仅在于Base64的区分,用JavaAPI的Base64解码生成私钥报错,用Android API 解码就没问题。

    下意识怀疑Base64解码的问题。

    后来经过排除,并不是Base64导致的问题。查阅资料后得知 Java API默认下只能加载 PKCS8 编码格式的私钥文件。

    故:通过openssl命令将私钥文件转码为了 PKCS8格式的。再次测试发现通过私钥串可以正常生产私钥了。

    Java 使用PKCS1私钥

    Java并非不能使用 PKCS1 格式的私钥文件,只是按照上述代码运行,默认使用的是PKCS8来加载私钥文件,所以会报错。

    只是Java的API中并为PKCS1密钥提供开箱即用的封装。

    PEMParser pemParser = new PEMParser(new FileReader(privateKeyFile));
    JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
    Object object = pemParser.readObject();
    KeyPair kp = converter.getKeyPair((PEMKeyPair) object);
    PrivateKey privateKey = kp.getPrivate();    
    

    上述代码摘抄自:在JAVA中读取格式为PKCS1的RSA私钥

    RSA 私钥解密中遇到的问题

    私钥生成的问题解决后,以为后面就是万事大吉了,谁成想...Server童鞋那边又反应说是用这个生成的私钥无法进行解密,解密过程中抛错了。

    javax.crypto.BadPaddingException: Decryption error
    

    首先我这边给出测试文档案例,我这边都是经过加密再解密流程的,即我这边的代码肯定是可以解密这个加密内容的。然后对照文档排除掉手残粘贴错误的情况,发现只能重走老路,再次用纯Java代码实现解密的逻辑。

                ...
                val cipher = Cipher.getInstance("RSA")
                cipher.init(Cipher.DECRYPT_MODE, privateKey)
                cipher.doFinal(encryptedData)
                ...
    

    PS:代码逻辑前后省略,仅贴关键代码

    自己跑一遍Java的解密逻辑,发现真是无法解密...

    瞬间就是脑海里万马奔腾,***!!!

    此处真的耗时良久。。。

    • 排除了可能Base64的问题;
    • 排除了可能密钥对的问题;
    • 排除了可能粘贴复制加密串到文档中字符编码不同的问题

    后来无奈之下苦思时,突然想到还有个大区别,虽然都是使用的Java API,但是运行的环境不同。

    电脑上做单元测试的虚拟机与Android手机系统的dalvik虚拟机,这就会带来很大的区别。

    后来带着这个问题,查阅资料,发现Android与SUN默认下对RSA算法的默认实现有区别。

    即Android端用Cipher.getInstance("RSA")方法进行加密时,使用的provider是Bouncycastle Security provider,Bouncycastle Security provider默认实现的是“RSA/None/NoPadding”算法,而服务器(PC)端用Cipher.getInstance("RSA")进行解密时,使用的是Sun的security provider,实现的是“RSA/None/PKCS1Padding”算法,所以,解密时会失败。

    在本地做过验证之后,发现确实如此。

    val cipher = Cipher.getInstance("RSA/None/PKCS1Padding")
    

    参考链接

    相关文章

      网友评论

          本文标题:关于RSA私钥解密遇到的问题

          本文链接:https://www.haomeiwen.com/subject/kfeinctx.html