详解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 相关文章推荐
经典的解除许多网站无法复制文字的绝招
Dec 31 Javascript
写出更好的JavaScript之undefined篇(上)
Nov 22 Javascript
innerHTML与jquery里的html()区别介绍
Oct 12 Javascript
JS 实现获取打开一个界面中输入的值
Mar 19 Javascript
js中判断对象是否为空的三种实现方法
Dec 23 Javascript
JS替换文本域内的回车示例
Feb 18 Javascript
JavaScript实现网页加载进度条代码超简单
Sep 21 Javascript
对jquery的ajax进行二次封装以及ajax缓存代理组件:AjaxCache详解
Apr 11 Javascript
JS遍历数组和对象的区别及递归遍历对象、数组、属性的方法详解
Jun 14 Javascript
DropDownList实现可输入可选择(两种版本可选)
Dec 07 Javascript
vue2.0 + element UI 中 el-table 数据导出Excel的方法
Mar 02 Javascript
javaScript强制保留两位小数的输入数校验和小数保留问题
May 09 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/08/21 PHP
php中smarty实现多模版网站的方法
2015/06/11 PHP
PHP实现长文章分页实例代码(附源码)
2016/02/03 PHP
eclipse php wamp配置教程
2016/06/30 PHP
js获取图片长和宽度的代码
2009/11/24 Javascript
编写可维护面向对象的JavaScript代码[翻译]
2011/02/12 Javascript
dojo随手记 gird组件引用
2011/02/24 Javascript
基于jquery的滚动条滚动固定div(附演示下载)
2012/10/29 Javascript
实现动画效果核心方式的js代码
2013/09/27 Javascript
JS 获取滚动条高度示例代码
2013/10/24 Javascript
JavaScript中的this机制
2016/01/30 Javascript
JavaScript实现购物车基本功能
2017/07/21 Javascript
详解微信小程序调起键盘性能优化
2018/07/24 Javascript
详解webpack打包后如何调试的方法步骤
2018/11/07 Javascript
layui-laydate时间日历控件使用方法详解
2018/11/15 Javascript
详解puppeteer使用代理
2018/12/27 Javascript
在Vue项目中取消ESLint代码检测的步骤讲解
2019/01/27 Javascript
js实现的在本地预览图片功能示例
2019/11/09 Javascript
javascript设计模式 ? 简单工厂模式原理与应用实例分析
2020/04/09 Javascript
js面试题之异步问题的深入理解
2020/09/20 Javascript
一个基于flask的web应用诞生 记录用户账户登录状态(6)
2017/04/11 Python
Python实现的json文件读取及中文乱码显示问题解决方法
2018/08/06 Python
Python中一些深不见底的“坑”
2019/06/12 Python
python实现大文本文件分割
2019/07/22 Python
Python tkinter实现简单加法计算器代码实例
2020/05/13 Python
Python异常处理机制结构实例解析
2020/07/23 Python
关于PySnooper 永远不要使用print进行调试的问题
2021/03/04 Python
德国体育用品网上商店:SC24.com
2016/08/01 全球购物
Kipling澳洲官网:购买凯浦林包包
2020/12/17 全球购物
Java面试题:说出如下代码的执行结果
2015/10/30 面试题
留学自荐信的技巧
2013/10/17 职场文书
中专生毕业自我鉴定
2013/11/01 职场文书
商务英语专业大学生职业生涯规划书
2014/09/14 职场文书
奖学金申请个人主要事迹材料
2015/11/04 职场文书
Java Socket实现多人聊天系统
2021/07/15 Java/Android
Three.js实现雪糕地球的使用示例详解
2022/07/07 Javascript