C# 和 Cryptojs 的 TripleDESCryptoServiceProvider 给出了不同的结果
TripleDESCryptoServiceProvider for C# and Cryptojs gives different results
为什么我使用 c# 和 JavaScript cryptojs 在 TriplesDes 上加密时得到不同的结果?请在下面查看我的代码。
c#
public static string EncryptTxt()
{
SHA512CryptoServiceProvider sha = new SHA512CryptoServiceProvider();
using (var tdes = new TripleDESCryptoServiceProvider())
{
var msg = 'jvofs:JCV XXXXX:201911141547:12345678';
var key = 'jjvofs';
var keyOffset = 10;
System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();
byte[] Results;
byte[] newKeyx = new byte[24];
byte[] keybyte = sha.ComputeHash(Encoding.UTF8.GetBytes(key));
Array.Copy(keybyte, keyOffset, newKeyx, 0, newKeyx.Length);
TDESAlgorithm.Key = newKeyx;
TDESAlgorithm.Mode = CipherMode.ECB;
TDESAlgorithm.Padding = PaddingMode.PKCS7;
byte[] DataToEncrypt = UTF8.GetBytes(msg);
try
{
ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor();
Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length);
}
finally
{
TDESAlgorithm.Clear();
}
var a = Convert.ToBase64String(DataToEncrypt);
var b = Convert.ToBase64String(newKeyx);
var c = Convert.ToBase64String(Results);
return Convert.ToBase64String(Results);
}
}
JavaScript 使用 cryptojs
txtEncrypter = () => {
const msg = 'jvofs:JCV XXXXX:201911141547:12345678';
const key = 'jjvofs';
const keyOffset = 10;
const keybyte: any = this.wordArrayToByteArray(crypto.SHA512(key), 100);
// For message
const dataToEncrypt = crypto.enc.Utf8.parse(msg);
const dte = this.wordArrayToByteArray(dataToEncrypt, 100);
const dataToEncryptx = this._arrayBufferToBase64(dte);
const dataToEncryptxx = crypto.enc.Utf8.parse(dataToEncryptx);
// For key
let newKeyx = keybyte.slice(keyOffset, 34);
const newKeyxB4Splice = newKeyx;
const newKeyxB4Splicex = this._arrayBufferToBase64(newKeyx);
newKeyx = crypto.enc.Utf8.parse(newKeyx);
const options = {
mode: crypto.mode.ECB,
padding: crypto.pad.Pkcs7
};
const encrypted = crypto.TripleDES.encrypt(dataToEncrypt, newKeyx, options);
const base64String = encrypted.toString();
console.log(base64String);
}
wordArrayToByteArray(wordArray, length) {
if (wordArray.hasOwnProperty('sigBytes') && wordArray.hasOwnProperty('words')) {
length = wordArray.sigBytes;
wordArray = wordArray.words;
}
const result = [];
let bytes: any;
let i = 0;
while (length > 0) {
bytes = this.wordToByteArray(wordArray[i], Math.min(4, length));
length -= bytes.length;
result.push(bytes);
i++;
}
return [].concat.apply([], result);
}
wordToByteArray(word: any, length: any) {
const ba = [], xFF = 0xFF;
if (length > 0) {
// tslint:disable-next-line:no-bitwise
ba.push(word >>> 24);
}
if (length > 1) {
// tslint:disable-next-line:no-bitwise
ba.push((word >>> 16) & xFF);
}
if (length > 2) {
// tslint:disable-next-line:no-bitwise
ba.push((word >>> 8) & xFF);
}
if (length > 3) {
// tslint:disable-next-line:no-bitwise
ba.push(word & xFF);
}
return ba;
}
byteArrayToWordArray(ba) {
const wa = [];
let i = 0;
for (i = 0; i < ba.length; i++) {
// tslint:disable-next-line:no-bitwise
wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i);
}
return crypto.lib.WordArray.create(wa);
}
toUTF8Array(str) {
const utf8 = [];
for (let i = 0; i < str.length; i++) {
let charcode = str.charCodeAt(i);
if (charcode < 0x80) { utf8.push(charcode); } else if (charcode < 0x800) {
// tslint:disable-next-line:no-bitwise
utf8.push(0xc0 | (charcode >> 6),
// tslint:disable-next-line: no-bitwise
0x80 | (charcode & 0x3f));
} else if (charcode < 0xd800 || charcode >= 0xe000) {
// tslint:disable-next-line: no-bitwise
utf8.push(0xe0 | (charcode >> 12),
// tslint:disable-next-line: no-bitwise
0x80 | ((charcode>>6) & 0x3f),
// tslint:disable-next-line: no-bitwise
0x80 | (charcode & 0x3f));
} else {
i++;
// UTF-16 encodes 0x10000-0x10FFFF by
// subtracting 0x10000 and splitting the
// 20 bits of 0x0-0xFFFFF into two halves
// tslint:disable-next-line:no-bitwise
charcode = 0x10000 + (((charcode & 0x3ff)<<10)
// tslint:disable-next-line:no-bitwise
| (str.charCodeAt(i) & 0x3ff));
// tslint:disable-next-line:no-bitwise
utf8.push(0xf0 | (charcode >>18),
// tslint:disable-next-line:no-bitwise
0x80 | ((charcode>>12) & 0x3f),
// tslint:disable-next-line: no-bitwise
0x80 | ((charcode>>6) & 0x3f),
// tslint:disable-next-line: no-bitwise
0x80 | (charcode & 0x3f));
}
}
return utf8;
}
_arrayBufferToBase64( buffer ) {
let binary = '';
const bytes = new Uint8Array( buffer );
const len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}
解析message和key的数据时,c#和JavaScript是一样的:
留言:
[C#] anZvZnM6SkNWIFhYWFhYOjIwMTkxMTE0MTU0NzoxMjM0NTY3OA==
[cryptojs] anZvZnM6SkNWIFhYWFhYOjIwMTkxMTE0MTU0NzoxMjM0NTY3OA==
键:
[C#] smbkkmDrCBdRev7S4hLaWE16Nvym+9gW
[cryptojs] smbkkmDrCBdRev7S4hLaWE16Nvym+9gW
但是一旦“crypto.TripleDES.encrypt(....)”运行,我得到 c# 和 javascript:
的不同结果
[C#] 1pjvBOB81iAOqsskZ+cM080yDU37XBoCwMhbYULwva/Nql5vbEMiPQ==
[cryptojs] ncCcCYNy3jVsB/95SaC2N1rH5Q+hX04WvScMvwmtkPkrnL7Ki1bmPg==
从哈希的字节数组中确定密钥为子数组(包括索引 10 到索引 34 除外)后,必须将此子数组转换回 WordArray
具有等效的内容,即行:
newKeyx = crypto.enc.Utf8.parse(newKeyx);
必须替换为:
newKeyx = byteArrayToWordArray(newKeyx);
通过此更改,NodeJS 代码 returns 与 C# 代码的结果相同。
转换 WordArray <-> byte-array
(以及这些转换所需的所有函数)并不是真正必要的,因为作为替代方案,密钥也可以仅使用 CryptoJS-encoders:
...
const key = 'jjvofs';
const keyOffset = 10;
const keyLength = 24;
const keyHash = crypto.enc.Hex.stringify(crypto.SHA512(key));
const newKey = crypto.enc.Hex.parse(keyHash.slice(keyOffset * 2, (keyOffset + keyLength) * 2));
...
顺便说一下:ECB-mode is insecure and instead of TripleDES 应该使用性能更高的 AES。
为什么我使用 c# 和 JavaScript cryptojs 在 TriplesDes 上加密时得到不同的结果?请在下面查看我的代码。
c#
public static string EncryptTxt()
{
SHA512CryptoServiceProvider sha = new SHA512CryptoServiceProvider();
using (var tdes = new TripleDESCryptoServiceProvider())
{
var msg = 'jvofs:JCV XXXXX:201911141547:12345678';
var key = 'jjvofs';
var keyOffset = 10;
System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();
byte[] Results;
byte[] newKeyx = new byte[24];
byte[] keybyte = sha.ComputeHash(Encoding.UTF8.GetBytes(key));
Array.Copy(keybyte, keyOffset, newKeyx, 0, newKeyx.Length);
TDESAlgorithm.Key = newKeyx;
TDESAlgorithm.Mode = CipherMode.ECB;
TDESAlgorithm.Padding = PaddingMode.PKCS7;
byte[] DataToEncrypt = UTF8.GetBytes(msg);
try
{
ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor();
Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length);
}
finally
{
TDESAlgorithm.Clear();
}
var a = Convert.ToBase64String(DataToEncrypt);
var b = Convert.ToBase64String(newKeyx);
var c = Convert.ToBase64String(Results);
return Convert.ToBase64String(Results);
}
}
JavaScript 使用 cryptojs
txtEncrypter = () => {
const msg = 'jvofs:JCV XXXXX:201911141547:12345678';
const key = 'jjvofs';
const keyOffset = 10;
const keybyte: any = this.wordArrayToByteArray(crypto.SHA512(key), 100);
// For message
const dataToEncrypt = crypto.enc.Utf8.parse(msg);
const dte = this.wordArrayToByteArray(dataToEncrypt, 100);
const dataToEncryptx = this._arrayBufferToBase64(dte);
const dataToEncryptxx = crypto.enc.Utf8.parse(dataToEncryptx);
// For key
let newKeyx = keybyte.slice(keyOffset, 34);
const newKeyxB4Splice = newKeyx;
const newKeyxB4Splicex = this._arrayBufferToBase64(newKeyx);
newKeyx = crypto.enc.Utf8.parse(newKeyx);
const options = {
mode: crypto.mode.ECB,
padding: crypto.pad.Pkcs7
};
const encrypted = crypto.TripleDES.encrypt(dataToEncrypt, newKeyx, options);
const base64String = encrypted.toString();
console.log(base64String);
}
wordArrayToByteArray(wordArray, length) {
if (wordArray.hasOwnProperty('sigBytes') && wordArray.hasOwnProperty('words')) {
length = wordArray.sigBytes;
wordArray = wordArray.words;
}
const result = [];
let bytes: any;
let i = 0;
while (length > 0) {
bytes = this.wordToByteArray(wordArray[i], Math.min(4, length));
length -= bytes.length;
result.push(bytes);
i++;
}
return [].concat.apply([], result);
}
wordToByteArray(word: any, length: any) {
const ba = [], xFF = 0xFF;
if (length > 0) {
// tslint:disable-next-line:no-bitwise
ba.push(word >>> 24);
}
if (length > 1) {
// tslint:disable-next-line:no-bitwise
ba.push((word >>> 16) & xFF);
}
if (length > 2) {
// tslint:disable-next-line:no-bitwise
ba.push((word >>> 8) & xFF);
}
if (length > 3) {
// tslint:disable-next-line:no-bitwise
ba.push(word & xFF);
}
return ba;
}
byteArrayToWordArray(ba) {
const wa = [];
let i = 0;
for (i = 0; i < ba.length; i++) {
// tslint:disable-next-line:no-bitwise
wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i);
}
return crypto.lib.WordArray.create(wa);
}
toUTF8Array(str) {
const utf8 = [];
for (let i = 0; i < str.length; i++) {
let charcode = str.charCodeAt(i);
if (charcode < 0x80) { utf8.push(charcode); } else if (charcode < 0x800) {
// tslint:disable-next-line:no-bitwise
utf8.push(0xc0 | (charcode >> 6),
// tslint:disable-next-line: no-bitwise
0x80 | (charcode & 0x3f));
} else if (charcode < 0xd800 || charcode >= 0xe000) {
// tslint:disable-next-line: no-bitwise
utf8.push(0xe0 | (charcode >> 12),
// tslint:disable-next-line: no-bitwise
0x80 | ((charcode>>6) & 0x3f),
// tslint:disable-next-line: no-bitwise
0x80 | (charcode & 0x3f));
} else {
i++;
// UTF-16 encodes 0x10000-0x10FFFF by
// subtracting 0x10000 and splitting the
// 20 bits of 0x0-0xFFFFF into two halves
// tslint:disable-next-line:no-bitwise
charcode = 0x10000 + (((charcode & 0x3ff)<<10)
// tslint:disable-next-line:no-bitwise
| (str.charCodeAt(i) & 0x3ff));
// tslint:disable-next-line:no-bitwise
utf8.push(0xf0 | (charcode >>18),
// tslint:disable-next-line:no-bitwise
0x80 | ((charcode>>12) & 0x3f),
// tslint:disable-next-line: no-bitwise
0x80 | ((charcode>>6) & 0x3f),
// tslint:disable-next-line: no-bitwise
0x80 | (charcode & 0x3f));
}
}
return utf8;
}
_arrayBufferToBase64( buffer ) {
let binary = '';
const bytes = new Uint8Array( buffer );
const len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}
解析message和key的数据时,c#和JavaScript是一样的:
留言:
[C#] anZvZnM6SkNWIFhYWFhYOjIwMTkxMTE0MTU0NzoxMjM0NTY3OA==
[cryptojs] anZvZnM6SkNWIFhYWFhYOjIwMTkxMTE0MTU0NzoxMjM0NTY3OA==
键:
[C#] smbkkmDrCBdRev7S4hLaWE16Nvym+9gW
[cryptojs] smbkkmDrCBdRev7S4hLaWE16Nvym+9gW
但是一旦“crypto.TripleDES.encrypt(....)”运行,我得到 c# 和 javascript:
的不同结果[C#] 1pjvBOB81iAOqsskZ+cM080yDU37XBoCwMhbYULwva/Nql5vbEMiPQ==
[cryptojs] ncCcCYNy3jVsB/95SaC2N1rH5Q+hX04WvScMvwmtkPkrnL7Ki1bmPg==
从哈希的字节数组中确定密钥为子数组(包括索引 10 到索引 34 除外)后,必须将此子数组转换回 WordArray
具有等效的内容,即行:
newKeyx = crypto.enc.Utf8.parse(newKeyx);
必须替换为:
newKeyx = byteArrayToWordArray(newKeyx);
通过此更改,NodeJS 代码 returns 与 C# 代码的结果相同。
转换 WordArray <-> byte-array
(以及这些转换所需的所有函数)并不是真正必要的,因为作为替代方案,密钥也可以仅使用 CryptoJS-encoders:
...
const key = 'jjvofs';
const keyOffset = 10;
const keyLength = 24;
const keyHash = crypto.enc.Hex.stringify(crypto.SHA512(key));
const newKey = crypto.enc.Hex.parse(keyHash.slice(keyOffset * 2, (keyOffset + keyLength) * 2));
...
顺便说一下:ECB-mode is insecure and instead of TripleDES 应该使用性能更高的 AES。