详解Node.js 中使用 ECDSA 签名遇到的坑


Posted in Javascript onNovember 26, 2018

最近有个朋友问我关于 Node.js 下使用 ECDSA 的问题,主要是使用 Node.js 的 Crypto 模块无法校验网络传输过来的签名结果。在踩坑无数后,终于搞清楚了原因。

坑 0x00:签名输出格式

在排除了证书、消息不一致的可能之后,我开始对比使用 Node.js 签名的结果与网络传输过来的签名,发现长度不一致,大约差了5~7个字节。于是去网上搜索了一下,才知道原来 Node.js (基于 OpenSSL)签名得到的是 DER 格式的内容,而网络上常用的 ECDSA 签名结果是 IEEE P1363 格式的。(也可以写作 R|S)

参考:https://stackoverflow.com/a/39575576

知道问题了就好解决了。但是,DER 和 IEEE P1363 两个格式互转也不是那么容易的。

简单科普一下,ECDSA 是指基于 ECC 椭圆加密算法的签名方式,签名结果是两个整数 R 和 S。 R 和 S 一般长度相同,或者接近。如果长度不同,在各自前面补字节 0x00 直到等长。把 R 和 S 以大头字节序表示,然后依次前后拼接,就是所谓 IEEE P1363 格式。

坑 0x01:DER 的整数问题

先来了解一下 ECDSA 的 DER 输出格式,大概如下:

SEQUENCE <LENGTH>
 INTEGER <INTEGER_LENGTH> <INTEGER_VALUE...> # 整数 R
 INTEGER <INTEGER_LENGTH> <INTEGER_VALUE...> # 整数 S

其中

SEQUENCE 是 DER 数组(串?)标头,用一个字节 0x30 表示

<LENGTH> 是 SEQUENCE 的长度,用一个字节表示,不包括标头和这个长度本身

INTEGER 是整数标头,用一个字节 0x02 表示

<INTEGER_LENGTH> 是整数的字节长度,用一个字节表示。

<INTEGER_VALUE> 是整数的内容,以大头字节序表示。

另一个坑我也已经写出来了,不知道有人发现没有?没想到的话,继续往下。

IEEE P1363 格式下,R 和 S 都是等长的。所以只要把 IEEE P1363 格式的签名从中间切分就可以得到 R 和 S 的内容了。而且 IEEE P1363 格式下,R 和 S 也是以大头字节序表示的,因此没有字节序转换问题了。现在,只需要按上面的格式构造一个 DER 即可。

坑 0x01.0:缺少整数前置字节 0x00

我第一次尝试将 IEEE P1363 格式的签名转换成 DER 格式,并没有失败,但是当我换一个签名结果,却失败了……我对比了 DER 和 IEEE P1363 的区别,发现了一个特点,在 DER 格式下,R 和 S 偶尔会有前置字节 0x00,但不是一定的。

查资料后才明白,DER 下没有“无符号整数”之说,也就是说整数都是有符号的。如果 INTEGER 所表示的整数最高字节大于 0x7F,也就是最高位(符号位)为 1,则表示负数。如果要表示正数,必须在前面补一个字节 0x00……

参考 https://bitcointalk.org/index.php?topic=215205.msg2258789#msg2258789

坑 0x01.1:多余的整数前置字节 0x00

在我修改代码后,虽然提高了成功率,可仍然有失败的情况,仔细看了下,原来是因为 IEEE P1363 格式里,R 和 S 可能被补了不止 1 个字节 0x00……

而 DER 下虽然要求补字节 0x00,却是有且只能有一个字节 0x00。

到此,问题都解决了——直到我测试了 521-bit (是的,你没看错,不是 512) 长度的密钥时,完全失败,毫无例外。

坑 0x02:DER SEQUENCE 的长度超过 0x7F

前面说了,<LENGTH> 只能用一个字节表示,这是一个整数,前文我提到的整数正负问题,这里也存在!

即是说,ECDSA 签名使用 DER 输出格式时,如果使用 521-bit (是的,你没看错,不是 512) 长度的密钥时,DER的长度将超出 0x7F,使得 <LENGTH> 变成了负数!

而解决方案不是补字节 0x00,而是用字节 0x81 填充 <LENGTH>,再在下一个字节用一个无符号整数的表示长度(0 ~ 255)。

参考:https://stackoverflow.com/a/47099047

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js保存当前路径(cookies记录)
Dec 14 Javascript
分析Node.js connect ECONNREFUSED错误
Apr 09 Javascript
利用函数的惰性载入提高javascript代码执行效率
May 05 Javascript
node.js中的fs.fstat方法使用说明
Dec 15 Javascript
基于jquery实现人物头像跟随鼠标转动
Aug 23 Javascript
location.hash保存页面状态的技巧
Apr 28 Javascript
JavaScript常用正则函数用法示例
Jan 23 Javascript
layui点击按钮添加可编辑的一行方法
Aug 15 Javascript
vee-validate vue 2.0自定义表单验证的实例
Aug 28 Javascript
如何让微信小程序页面之间的通信不再变困难
Jun 03 Javascript
Vue 实现复制功能,不需要任何结构内容直接复制方式
Nov 09 Javascript
js实现3D旋转效果
Aug 18 Javascript
Vue.js的动态组件模板的实现
Nov 26 #Javascript
解决Vue开发中对话框被遮罩层挡住的问题
Nov 26 #Javascript
Vue项目部署在Spring Boot出现页面空白问题的解决方案
Nov 26 #Javascript
electron制作仿制qq聊天界面的示例代码
Nov 26 #Javascript
Vuex的初探与实战小结
Nov 26 #Javascript
微信小程序页面间值传递的两种方法
Nov 26 #Javascript
Vue中的methods、watch、computed的区别
Nov 26 #Javascript
You might like
PHP 获取目录下的图片并随机显示的代码
2009/12/28 PHP
2个自定义的PHP in_array 函数,解决大量数据判断in_array的效率问题
2014/04/08 PHP
PHP实现的构造sql语句类实例
2016/02/03 PHP
PHP中类的自动加载的方法
2017/03/17 PHP
PHP命名空间简单用法示例
2018/12/28 PHP
PHP工厂模式的日常使用
2019/03/20 PHP
De facto standard 世界上不可思议的事实标准
2010/08/29 Javascript
网页右下角弹出窗体实现代码
2014/06/05 Javascript
jQuery+css3实现Ajax点击后动态删除功能的方法
2015/08/10 Javascript
javascript动态添加checkbox复选框的方法
2015/12/23 Javascript
js实现滚动条滚动到某个位置便自动定位某个tr
2021/01/20 Javascript
BootStrap扔进Django里的方法详解
2016/05/13 Javascript
jQuery实现扑克正反面翻牌效果
2017/03/10 Javascript
从零开始学习Node.js系列教程一:http get和post用法分析
2017/04/13 Javascript
JavaScript 通过Ajax 动态加载CheckBox复选框
2017/08/31 Javascript
vue中配置mint-ui报css错误问题的解决方法
2017/10/11 Javascript
React Native使用百度Echarts显示图表的示例代码
2017/11/07 Javascript
JS使用canvas中的measureText方法测量字体宽度示例
2019/02/02 Javascript
vue项目首屏打开速度慢的解决方法
2019/03/31 Javascript
JavaScript 继承 封装 多态实现及原理详解
2019/07/29 Javascript
ES6学习笔记之字符串、数组、对象、函数新增知识点实例分析
2020/01/22 Javascript
基于Vue sessionStorage实现保留搜索框搜索内容
2020/06/01 Javascript
实现vuex原理的示例
2020/10/21 Javascript
js实现碰撞检测
2021/01/29 Javascript
Python实现的Kmeans++算法实例
2014/04/26 Python
Python算法之图的遍历
2017/11/16 Python
Python使用Matplotlib实现Logos设计代码
2017/12/25 Python
python判断字符串或者集合是否为空的实例
2019/01/23 Python
Python3如何判断三角形的类型
2020/04/12 Python
存储过程和sql语句的优缺点
2014/07/02 面试题
Linux中如何用命令创建目录
2016/12/02 面试题
大学生求职自荐信
2013/12/12 职场文书
五月的鲜花活动方案
2014/08/21 职场文书
暑假社会实践心得体会
2014/09/02 职场文书
有关水浒传的读书笔记
2015/06/25 职场文书
详解MongoDB排序时内存大小限制与创建索引的注意事项
2022/05/06 MongoDB