DingTalkEncryptor.js
4.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* eslint-disable no-bitwise */
'use strict';
const CryptoJS = require("crypto-js");
const Crypto = require('crypto');
const DingTalkEncryptException = require('./DingTalkEncryptException');
class DingTalkEncryptor {
constructor(token, encodingAesKey, corpIdOrSuiteKey) {
this.utf8 = 'utf-8';
this.base64 = 'base64';
this.AES_ENCODE_KEY_LENGTH = 43;
this.RANDOM_LENGTH = 16;
this.token = token;
this.encodingAesKey = encodingAesKey;
this.aesKey = CryptoJS.enc.Base64.parse(encodingAesKey+'=');
this.corpId = corpIdOrSuiteKey;
this.keySpec = this.aesKey;
this.iv = CryptoJS.enc.Base64.parse((encodingAesKey+'=').substring(0,22));
}
// verify encodingAesKey
set encodingAesKey(val) {
if (!val || val.length !== this.AES_ENCODE_KEY_LENGTH) {
throw new DingTalkEncryptException(900004);
}
}
encrypt(random, plainText) {
try {
const randomBuf = Buffer.from(random);
const plainTextBuf = Buffer.from(plainText);
const textLen = plainTextBuf.length;
const textLenBuf = Buffer.from([(textLen >> 24 & 255), (textLen >> 16 & 255), (textLen >> 8 & 255), (textLen & 255)]);
const cropIdBuf = Buffer.from(this.corpId);
const padCount = 32 - (randomBuf.length + textLenBuf.length + plainTextBuf.length + cropIdBuf.length) % 32;
const padBuf = Buffer.from(new Array(padCount).fill(padCount));
const finalBuf = Buffer.concat([randomBuf, textLenBuf, plainTextBuf, cropIdBuf, padBuf]);
const encrypted = CryptoJS.AES.encrypt( CryptoJS.enc.Hex.parse(finalBuf.toString('hex')), this.keySpec, {
iv: this.iv,
mode: CryptoJS.mode.CBC,
padding:CryptoJS.pad.Pkcs7
});
return encrypted.toString();
} catch (e) {
console.log(e)
throw new DingTalkEncryptException(900007);
}
}
decrypt(encrypted) {
let decrypt;
try {
// decrypt
const ciphertext = CryptoJS.enc.Base64.parse(encrypted);
decrypt = CryptoJS.AES.decrypt( {ciphertext}, this.keySpec, {
iv: this.iv,
mode: CryptoJS.mode.CBC,
padding:CryptoJS.pad.Pkcs7
});
} catch (e) {
throw new DingTalkEncryptException(900008);
}
//encrypt = Base64_Encode(AES_Encrypt[random(16B) + msg_len(4B) + msg + $key]) 钉钉加密消息格式
let cropId;
let plainText;
try {
const finalDecrypt = decrypt.toString(CryptoJS.enc.Hex);
const textLen = parseInt(finalDecrypt.substring(32,8+32), 16) ;
plainText = CryptoJS.enc.Hex.parse(finalDecrypt.substring(40, 40 + textLen*2)).toString( CryptoJS.enc.Utf8);
cropId = CryptoJS.enc.Hex.parse(finalDecrypt.substring(40 + textLen*2)).toString(CryptoJS.enc.Utf8);
} catch (e) {
throw new DingTalkEncryptException(900009);
}
if (cropId != this.corpId) {
console.log(cropId, this.corpId)
throw new DingTalkEncryptException(900010);
} else {
return plainText;
}
}
getSignature(token, timestamp, nonce, encrypt) {
timestamp = timestamp + '';
const strArr = [token, timestamp, nonce, encrypt];
strArr.sort();
const sha1 = CryptoJS.SHA1(strArr.join(''));
return sha1.toString(CryptoJS.enc.Hex);
}
getEncryptedMap(plaintext, timeStamp, nonce) {
timeStamp = timeStamp + '';
if (plaintext == null) {
throw new DingTalkEncryptException(900001);
} else if (timeStamp == null) {
throw new DingTalkEncryptException(900002);
} else if (nonce == null) {
throw new DingTalkEncryptException(900003);
} else {
const encrypt = this.encrypt(this.getRandomStr(this.RANDOM_LENGTH), plaintext);
const signature = this.getSignature(this.token, timeStamp, nonce, encrypt);
return {
msg_signature: signature,
encrypt: encrypt,
timeStamp: timeStamp,
nonce: nonce
};
}
}
getDecryptMsg(msgSignature, timeStamp, nonce, encryptMsg) {
const signature = this.getSignature(this.token, timeStamp, nonce, encryptMsg);
if (signature !== msgSignature) {
throw new DingTalkEncryptException(900006);
} else {
return this.decrypt(encryptMsg);
}
}
getRandomStr(size) {
const base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let randomStr = '';
for (let i = size; i > 0; --i) {
randomStr += base[Math.floor(Math.random() * base.length)];
}
return randomStr;
};
}
module.exports = DingTalkEncryptor;