RSA 经验之谈

Wesley13
• 阅读 605

  写代码这么多年了,在涉及RSA跨语言的功能时,总会让人经历一番折磨。所以有必要把现在我掌握的这些零碎的知识总结一下公布出来,免得组内同事再走弯路。由于RSA实在太复杂,还有很多内容没弄懂,也没精力全了解,暂且把问题留下,未来也许能回答。所以这篇文章的标题为“经验之谈”

  本质上是数字运算

  RSA算法的本质是找到几个非常大的数,然后做运算,加密解密都是运算而已。进行加解密之前你总要产生一个密钥对,这就是查找这几个数的过程。在产生的数字中,p,q是两个大质数,其乘积为n,经过挑选(你不要管到底是怎么挑选的)又会得到e和d,公钥就是(n, e),私钥就是(n, d),这个n的位数就是我们所说的密钥长度, 转换成为二进制,则要为64的倍数

  从上面你可以了解到,其实所谓密钥最初就是几个很大的数,给我们带来很多麻烦的其实是如何保存和传输这些数字。而所谓的加密解密,签名验签,也不过是指数操作然后取模的数字运算而已(这里排除了摘要运算)

  基于字节的存储

  平时我们看到被加密或者签名的明文都是字符串,公私钥保存成的也是字符串。实际上在进行RSA运算时,都是基于数字的。在编程语言中,大数字可能不好表示,所以我们看到的SDK API往往都是以字节数组体现(byte[]),这样一来,在保存和传输时,字节数组和字符串之间的转换就成了容易出错的地方。

  待加密/签名的明文的字符串要根据其编码方式转换成为字节数组,比如ascii或者UTF-8。解密之后的要做逆运算转换为明文。而公私钥则要看具体遵照的规范来决定如何转换,这其中涉及到不只一步编解码处理,不过基本上最后/第一步都是base64和字节之间的转换

  C#的那一大堆参数

C#标准库的RSACryptoServiceProvider产生的密钥对儿要以xml字符串的形式导出,联调时候我们发现很多参数不知道是干什么用的,dp, dq, InverseQ...这些多出来的东西对公钥没影响,但一股脑的都放到私钥中了

在PKCS#1 version 2.2中解释其为Chinese Remainder Theorem(CRT),具体这种CRT是怎么计算的我并没深入研究,但其不再是上面是说的n是两个大质数的乘积,而是多个的乘积

最要命的是RSACryptoServiceProvider好像并没提供把这个xml转换成为我我们常用的字节数组的方法,所以我在demo中使用的BouncyCastle库

  公钥与私钥的关系

公钥用来加密私钥解密。签名则是私钥签名,公钥验签。不用硬背,你记住私钥是要自己藏起来的,公钥是分配出去,然后自己推导就好:有人想把信息隐秘的发送给你,就要求只有你能解密。而你想把信息发出的同时证明自己的身份,则要求你能够签名

也许你会因为你用过的一些框架而疑惑:私钥也可以用来加密,公钥也可以用来解密。我觉得用“加密”“解密”这个字眼并不合适,还是用运算比较好。理论上这一对密钥完全可以倒过来使用,但这样一来私钥就要公布出去,公钥反倒要妥善保存,可生成密钥对的实际情况是,各语言框架会考虑运算时的复杂程度,公钥中的e一般会取3或者65537, 而私钥中有n了,整个公钥很容易猜到了

我听到过一种说法,是“私钥可以生成公钥”,这是错误的,上面一条说私钥可以“猜”出公钥,也并不是“生成”。我想这说法应该是看到pfx文件导入到IIS中可以再导出公钥,其实pfx文件里本就包含公钥信息,不过这种包含所有参数信息(n, e, d, p, q, dp, dq...)的密钥格式似乎还不少,很多“私钥文件可以导出公钥”

签名操作

  签名操作近似于对明文做哈希操作,然后再进行基于私钥的加密,说近似是因为我曾尝试基于上面流程自己实现签名,结果不正确,也许是缺少某些步骤的处理比如填充

  要做哈希是因为一方面RSA运算比较费时,另一方面其没有AES那么多种分组算法,RSA只能做其长度(字节数)- 11的这么长的明文的运算(据说这个公式也和填充方式有关,未必准),超过的部分截断,再进行一次运算。所以基本上没人用RSA进行加密解密,都是把明文哈希之后缩短了,再进行签名验签

  PKCS是啥

  我们在调用SDK时候经常看到的PKCS#1,PKCS#8,甚至到PKCS#12,其实是“Public-Key Cryptography Standards”,其不同编号涉及的内容也不相同,之前写代码时候发现PKCS#1经常作为padding的一个参数,其实#1中规范的内容不止如此(这里的Public-Key我认为不是说这个标准仅涉及公钥),甚至包含了怎么把数字转化为字符串的方式

  每个编号都做了一些事情,你可以从wiki上看到列表

  我们常常能看见PKCS#1作为Padding的参数,对应还有常用的NoPadding,不填充。Java的Demo中,传入cipher algorithm 参数为"RSA"则会默认使用PKCS#1,C#的Demo对应RSAUtils类的Encrypt和Decrypt方法,使用了PKCS1Encoding类,而C的Demo则明确指定了PKCS1/SSL V23(鬼才知道啥是SSL V23),如果你想改成NoPadding,Java cipher algorithm 参数传入“RSA/ECB/NoPadding” (基于Java 8), C#则要去掉PKCS1Encoding类,直接使用RsaEngine类

  其他padding方式均未试验成功

  带padding的方式会导致每次加密结果不一样,因为填充了随机数

  RSA是否更安全

  RSA未必更难破解,据说等价于AES256安全级别的RSA,密钥长度要远大于现在我们常用的2048。反过来,同安全级别的RSA,据说,据说,要比其他算法慢1000倍。之所以我们要用它主要还是因为密钥泄露的几率很低

    还有一大堆没弄懂的事

为什么Java读取公钥都用X509来封装,而读取私钥则用PKCS8封装?哪怕我生成密钥对根本没做指定?

ASN.1规定了什么,DER和BER格式是怎样?

还有一大堆crt,cer, pfx格式的证书文件都是啥?

签名的流程到底是啥,为什么不用指定padding方式?我自己写的签名方法差了哪一步?

C#标准库到底要怎么导出可供别的语言用的公私钥字符串而不是证书?

最后献上4个可以互通的demo(虽然我不太知道是怎么通的)仅供参考

Java:  https://github.com/apodemakeles/rsa-java-demo 

C#:  https://github.com/apodemakeles/rsa-csharp-demo 

OC:  https://github.com/wangbaolei/RSADemo 

C:  https://github.com/woniu201/rsa

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Git使用总结
生成密钥1.打开GitBash,运行  \_sshkeygen  \_2.密钥生成空间\_~/.ssh/id\_rsa  \_(C:/User/.ssh)3.输入密码(不输入增直接回车跳过)4._~/.ssh/id\_rsa.pub_ (公钥), _id\_rsa_ (私钥)下载代码到本地
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
35岁是技术人的天花板吗?
35岁是技术人的天花板吗?我非常不认同“35岁现象”,人类没有那么脆弱,人类的智力不会说是35岁之后就停止发展,更不是说35岁之后就没有机会了。马云35岁还在教书,任正非35岁还在工厂上班。为什么技术人员到35岁就应该退役了呢?所以35岁根本就不是一个问题,我今年已经37岁了,我发现我才刚刚找到自己的节奏,刚刚上路。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这