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

关于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")

参考链接

  • 关于javax.crypto.BadPaddingException: Blocktype异常的几种解决办法

  • 在JAVA中读取格式为PKCS1的RSA私钥

你可能感兴趣的:(关于RSA私钥解密遇到的问题)