详解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 相关文章推荐
Extjs TriggerField在弹出窗口显示不出问题的解决方法
Jan 08 Javascript
用方法封装javascript的new操作符(一)
Dec 25 Javascript
javascript动态添加样式(行内式/嵌入式/外链式等规则)
Jun 24 Javascript
Jquery 复选框取值兼容FF和IE8(测试有效)
Oct 29 Javascript
javascript调试过程中找不到哪里出错的可能原因
Dec 16 Javascript
JS Canvas定时器模拟动态加载动画
Sep 17 Javascript
Vue.JS入门教程之列表渲染
Dec 01 Javascript
WEB开发之注册页面验证码倒计时代码的实现
Dec 15 Javascript
jQuery实现移动端Tab选项卡效果
Mar 15 Javascript
微信小程序多张图片上传功能
Jun 07 Javascript
vue实现跨域的方法分析
May 21 Javascript
JavaScript基于面向对象实现的无缝滚动轮播示例
Jan 17 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输出表格的实现代码(修正版)
2010/12/29 PHP
PHP防范SQL注入的具体方法详解(测试通过)
2014/05/09 PHP
PHP函数实现从一个文本字符串中提取关键字的方法
2015/07/01 PHP
详解PHP的Yii框架中日志的相关配置及使用
2015/12/08 PHP
php模板引擎技术简单实现
2016/03/15 PHP
如何使用PHP给图片加水印
2016/10/12 PHP
php curl获取到json对象并转成数组array的方法
2018/05/31 PHP
密码框显示提示文字jquery示例
2013/08/29 Javascript
jQuery的context属性用法实例
2014/12/27 Javascript
浅谈JavaScript中Date(日期对象),Math对象
2015/02/05 Javascript
JS 拼凑字符串的简单实例
2016/09/02 Javascript
原生js 封装get ,post, delete 请求的实例
2017/08/11 Javascript
Vue在页面数据渲染完成之后的调用方法
2018/09/11 Javascript
对angularjs框架下controller间的传值方法详解
2018/10/08 Javascript
微信小程序五子棋游戏的悔棋实现方法【附demo源码下载】
2019/02/20 Javascript
解决layer.msg 不居中 ifram中的问题
2019/09/05 Javascript
js贪心算法 钱币找零问题代码实例
2019/09/11 Javascript
JS面向对象编程基础篇(二) 封装操作实例详解
2020/03/03 Javascript
nodejs中使用worker_threads来创建新的线程的方法
2021/01/22 NodeJs
[01:32]DOTA2上海特锦赛现场采访:最想COS的英雄
2016/03/25 DOTA
[01:44]Ti10举办地公布
2019/08/25 DOTA
浅谈django中的认证与登录
2016/10/31 Python
python实现人脸识别经典算法(一) 特征脸法
2018/03/13 Python
详解Python 函数如何重载?
2019/04/23 Python
利用Python库Scapy解析pcap文件的方法
2019/07/23 Python
python安装scipy的步骤解析
2019/09/28 Python
Python小整数对象池和字符串intern实例解析
2020/03/21 Python
PyQt5通过信号实现MVC的示例
2021/02/06 Python
HTML5实现视频弹幕功能
2019/08/09 HTML / CSS
少先队工作总结2015
2015/05/13 职场文书
离婚代理词范文
2015/05/23 职场文书
感恩教育主题班会
2015/08/12 职场文书
创业计划书之美甲店
2019/09/20 职场文书
利用ajax+php实现商品价格计算
2021/03/31 PHP
使用Selenium实现微博爬虫(预登录、展开全文、翻页)
2021/04/13 Python
vue自定义右键菜单之全局实现
2022/04/09 Vue.js