首先附上链接:https://github.com/ecfexorg/w...
由于工作原因,先后进行过微信开放平台,企业微信,阿里钉钉的第三方开发。在这个过程中都会有解密服务器推送来的消息的需求,而且经过这几次开发后,发现微信开放平台/企业微信/阿里钉钉的加密解密算法都是使用的 AES256,同时加密的消息体结构也是一样的。
同时由于找到的第三方包的代码要么提供的功能太多,要么用了过时的 api,对于强迫症的我来说很难忍受,所以自己造了一个轮子来解决这三方的消息加密解密的需求。
使用的是 TypeScript 编写的,编译生成声明文件,用 vscode 开发能有良好的代码提示(不过这么简单的库貌似也没啥需求...)
下面说下用 TypeScript 开发一个 npm 项目并且发布到 npm 的构建和开发流程:
一、首先在 github 上创建一个项目,项目名称最好和想要发布的 npm 包名一致,创建时可以选择是否生成 README 和证书,这里我就选择了 MIT 证书和默认的 README。
二、然后在本地使用git clone
命令将项目克隆下来,然后cd wx-ding-aes
,再执行npm init
,因为目录中包含了.git
文件夹,所以 npm 初始化时可以自动填入 github 地址,文档地址等 package 属性。
我们给生成的 package.json 文件的scripts
增加一条"build": "tsc"
,于是呆会儿执行npm run build
就能编译我们的代码了。
同时为了让别人使用我们的库时也能有良好的代码提示,所以我们编译时生成声明文件在types
文件夹下,发布时要连声明文件一起提交,同时还要在 package.json 中增加一个属性"types": "types/index.d.ts"
,意思是告诉别人这个库是自带声明文件的,并且声明文件的入口是在types
目录下的index.d.ts
里,当然如果你的"main"
的值不是index.js
,那么"types"
也要改变,让它能和"main"
对上号。
三、初始化完成后,执行npm i typescript @types/node -D
安装 TypeScript 和 Node.js 标准库的声明文件。然后在touch tsconfig.json
创建 TypeScript 的配置文件,在 vscode 中编写 TypeScript 的配置文件会有属性名和属性值提示。
{
"compilerOptions": {
"target": "es2017",
"outDir": "dist",
"module": "commonjs",
"declaration": true,
"declarationDir": "types"
},
"include": [
"src"
]
}
这里我的配置很简单,"target": "es2017"
表示编译目标的 JS 版本是 es2017;"outDir": "dist"
表示编译后的 JS 文件放在dist
文件夹中;"module": "commonjs"
表示编译后的 JS 还是使用 commonjs 的模块系统;"declaration": true
和"declarationDir": "types"
表示编译时会自动生成声明文件,并且声明文件放在types
目录下。"include": [ "src" ]
表示 src
目录里面的文件会被编译。
注:Node.js 从 8.5 版本开始已经开始支持 es 模块系统,不过写这篇文章时还处于实验阶段,需要加上--experimental-modules
参数才能使用
四、准备工作做完,然后可以开始写代码了,我把所有的代码都放在src
目录下,写完后npm run build
就能看到自动生成的dist
文件夹里装着编译后的 JS 文件,而types
文件夹则装着自动生成的声明文件。
关于微信的加密解密逻辑,其使用的是标准的 aes-256 加密算法的 cbc 模式,算法是可以实现的,不过 node.js 的标准库提供的crypto
模块已经有了,所以我们就不需要重复造轮子(有人看到这个名词肯定会马上跑去 npmjs.org 搜 aes256,但是我觉得一个项目的依赖应该越少越好,毕竟别人写的东西质量可维护性都不可控)。aes-256 加密时,被加密的内容长度应该是 32 字节的倍数,如果不是 32 的倍数则需要进行补全。补全有很多种方式,最简单的是用 0x00 补全,但是微信要求用 pkcs7 进行补全,所以这里我们也用这种方式(有人看到肯定又会跑去 npmjs.org 搜 pkcs7 了...)。
简单几句话就能概括这种补全方式:
假设被加密的内容长度为 x
- 如果 x 不是 32 的倍数,可以得到比 x 大的最小的 32 的整数是 y,加密时在被加密内容后面加上 y - x 个 y - x
- 如果 x 是 32 的倍数,那么在被加密内容后面加上 32 个 32
两个例子:
- 被加密的 buffer 长度为 53 ,比 53 大的、最小的 32 的倍数的证书应该是 64 ,64 - 53 = 9,那么这个 buffer 后面就加上 9 个 9 使他的长度变成 64 ;
- 被加密的 buffer 长度为 64 ,那么直接加上 32 个 32 ,使他的长度变成 96 。
这样做的目的是方便解密后截取原来的数据,解密后,只要看看最后一个数是多少(假设是 x ),那么就把最后的 x 个 byte 截掉就能得到前面被补全之前的数据了。上面的两个例子中,第一个只要看到最后一个数是 9 ,那就把最后 9 个 byte 去掉,前面的就是正确的内容,第二个例子之所以补 32 个 32 ,也是因为看到最后一个数是 32 ,那就把后面的 32 个 byte 截掉即可。假如因为已经是 32 的倍数就不补全,那么就不知道是补全了的还是没补全过的了。
代码就不一一解释,因为原理就在上面了。
五、写完后npm run build
就能编译了,如果想发布到 npmjs.org ,首先并不是整个项目里的所有文件都要被发布的,发布到 npm 的话应该在package.json
的"files"
属性里选择哪些是想上传的,比如这个项目我之上传生成的 JS 和声明文件以及证书,因为源码是没必要上传的。
如果没有 npm 帐号的话,那么就要先注册一个 npm 的帐号,然后在终端里执行npm login
登陆你的帐号。然后运行npm publish
即可发布,如果你担心有时忘了编译就执行了publish
,可以在"scripts"
里面增加"prepublish": "npm run build"
,表示每次发布之前都会先自动执行编译脚本。更多的钩子命令可以在 npmjs.org 的官方文档查看。
六、
目前 npm 发布的包是无法使用npm unpublish
来进行下架了,只能通过npm deprecate
来标记过时,所以建议大家除非确定自己有能力和精力去维护一个包,否则不要轻易发布一些乱七八糟的包,更不要去占一些自己没有能力维护的好的包名。如果实在想unpublish
,可以邮件联系 [email protected] 说明原因,他们会根据描述来进行处理。根据我的经验,由于时差,一般白天发邮件他们会深夜回复,然后你第二天会收到回复。
希望对大家有帮助 :P