最近公司项目需求要对上传/下载的数据进行AES+RSA的加解密需求,客户有vc的RSA加解密接口,而且说要与他们的系统兼容,也就是说vc下要用他们的模块实现加解密。期间过程有几个坑,原因都是自己的对这些加密的协议/准则/规范不熟,以此文记录备案。
坑1:
首先先是百度java的RSAUtils,网上很多现成的,例如这个:http://www.2cto.com/kf/201408/328112.html,公钥使用X509的解析类,私钥使用PKCS#8的解析类。编写测试程序一切正常。。。
然后就是调试客户提供的vc模块,测试程序如下图
然后走到RSA_EnCrypt的时候就出现错误,缺出现了0xC0000005未知性错误。(绝对不是输出没分配内存or内存不足-_-!!)这下就有点难搞了,打电话咨询对方,对方就说要遵循openssl标准生成的key哦~然后我汗颜了回他说,我是用你demo生成的key放进去接口用得,还会报错?然后对方也鄙视我了一下说,你发你的测试程序来看看。
发过去半个小时后,对方就返回一个截图给我
然后还说:上面的截图的做法没错,只是如果你传密匙或私匙字符串参数的时候,需要保持格式分割,如在VC里面你可以在每行后面加个\n,如果读入整个文本即保持了每行后面有\r\n就可以了,这个是标准pem格式的要求,事实上如果直接用openssl命令行生成密匙对并保存为文本,它也是用\n来分割的,直接文本打开是看不到回车换行的。
嗯,以上这句话就是坑1结论,上面说的:用传统的记事本打开用openssl生成的pem确实没了\n的格式,就一行串的写过去,而且可能是java的工具类内部已经处理这种一行串字符串格式的key,实际应用硬编码或者测试的时候会对我这种小白造成第一印象错误,而openssl的接口需要保持这种文本格式才能解析成功。还有,那个begin和end也是需要写进去的!!!
坑2
好了,既然java有工具类了,vc有接口了,那就是实现互相加解密吧~先是vc用public加密,java用privatekey解密,恩恩。
然而实践中中是有各种不知为何,而知后也不过如此的错误。首先声明一点就是,加密后的内容全球99.99%的人都是用base64将其编码,解密前需先将其这串内容用base64解码还原,嗯嗯。使用客户方提供的demo生成的公私密钥对,vc加密后把加密内容的传给java平台,然后java使用privatekey解密,遗憾的是java解码失败,出错的原因居然是privatekey解析不成功,这下又摸不着头脑了,不是说都按openssl的标准吗?sun公司不会另起标准的吧?
混乱了一阵子,仔细查看log是pkcs8的编码出错了,然后又百度pkcs8为何物,直叫我生死相许~.~
http://bbs.csdn.net/topics/190044123 <--- 这链接就详细说明了这些密钥的分类划分,然后看见到pkcs系列的介绍,然后还有http://blog.csdn.net/jdsjlzx/article/details/41441147这兄弟说的:密钥就基本生成,不过这样密钥对的私钥是无法在代码中直接使用的,要想使用它需要借助RSAPrivateKeyStructure这个类,java是不自带的。所以为了方便使用,我们需要对私钥进行PKCS#8编码!!!
然后一个猜测出现在我的脑海里,我的梦里,我的心里,我的记录里。。。难不成vc的不是按照pkcs#8的?打电话给客户方,得到答复是:不是pkcs#8,但不记得是pkcs#12还是什么的。=_=
好吧,再看看pkcs#12是何方神圣,链接我就不附带了。蛋疼的是,pkcs#12是证书的协议而且带密码的啊,你就一个密钥字符串,这是哪跟哪呢?
再一次在混乱的思维中冷静下来,客户方一直说遵从openssl标准,vc的openssl我不是没用过,最原始的java不能使用的pem文件是什么格式?经百度是pkcs#1,嗯哼!?这里有玄机,立刻再搜索有没工具把pkcs#1的变pkcs#8的,http://tool.chacuo.net/cryptrsapkcs1pkcs8 <-这网址就是把pkcs#1的私钥转成pkcs#8的格式,好吧,再次利用vc的demo生成公私密钥对(假设是pkcs#1),然后提取私钥利用工具生成一串pkcs#8格式的密钥。放进去java测试,解密成功!
总结下坑2:java现有的工具都是x509-pkcs#8的公私密钥对,而vc利用openssl的就很原始很开放的,就怪双方没有沟通好,造成坑2一系列的麻烦。
坑3,android的rsa隐坑=_=
按道理,java的系统jar包能在android上找到,那为啥java平台的rsa加解密能与vc互通,android就失败?android加密的内容送到java/vc里解密是一段空串?还有一点奇怪的是,java/vc的rsa加密后的内容都是随机不一样的,为啥android的加密后的内容那串字符串居然是一样的呢?搜索如下文章http://blog.csdn.net/yanzi1225627/article/details/26508035 和 http://blog.csdn.net/anod/article/details/8734608最后就是这个http://my.oschina.net/cwalet/blog/35867,还有这个http://juliusdavies.ca/commons-ssl/pkcs8.html(<-需要FQ) 慢慢的理解到android的虚拟机与windows上的虚拟机还真不一样。
android自带的只包含一个 默认的 RSA的最标准算法 是没填充“RSA/ECB/NoPadding”
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
所以android的每次运行都一样
然后这个Cipher里提供了第二个参数给你更换算法提供者
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding",new org.bouncycastle.jce.provider.BouncyCastleProvider());
这个就是按PKCS1填充的然后每次都不一样。这就是区别。
坑3结论:无话可说。从得到需求和客户方沟通到三平台互通用时4天。