首先附上鍊接: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 <package name>
來進行下架了,只能通過npm deprecate
來標記過時,所以建議大家除非確定自己有能力和精力去維護一個包,否則不要輕易釋出一些亂七八糟的包,更不要去佔一些自己沒有能力維護的好的包名。如果實在想unpublish
,可以郵件聯絡 support@npmjs.com 說明原因,他們會根據描述來進行處理。根據我的經驗,由於時差,一般白天發郵件他們會深夜回覆,然後你第二天會收到回覆。
希望對大家有幫助 ?