/** * 功能:加解密工具类第三方系统及对外接口专用版 * 说明:使用国密SM4算法 * JavaScript版本实现 */ const sm4 = require('sm-crypto').sm4; const crypto = require('crypto'); // 密文前缀常量 const CIPHERTEXT_PREFIX_THIRD_AES128 = "|$|"; const CIPHERTEXT_PREFIX_THIRD_AES128_REGEX = /\|\$\|/; const CIPHERTEXT_PREFIX_THIRD_SM4ECB = "|$4|"; const CIPHERTEXT_PREFIX_THIRD_SM4ECB_REGEX = /\|\$4\|/; /** * 判断字符串是否为空 * @param {string} str - 待判断的字符串 * @returns {boolean} 是否为空 */ function isBlank(str) { return !str || str.length === 0; } /** * 将字节数组转换为Base64字符串 * @param {Buffer} binaryData - 字节数组 * @returns {string} Base64字符串 */ function base64StringFromByteArray(binaryData) { try { const base64String = binaryData.toString('base64'); return base64String } catch (e) { console.error('Exception - base64StringFromByteArray() | binaryData :', binaryData); return null; } } /** * 将Base64字符串转换为字节数组 * @param {string} base64String - Base64字符串 * @returns {Buffer} 字节数组 */ function byteArrayFromBase64String(base64String) { try { return Buffer.from(base64String, 'base64'); } catch (e) { console.error('Exception - byteArrayFromBase64String() | base64String :', base64String); return null; } } /** * SM4 ECB模式加密(带PKCS5Padding) * @param {Buffer} content - 待加密内容 * @param {Buffer} key - SM4密钥(16字节) * @returns {Buffer} 加密后的字节数组 */ function encryptECBPadding(content, key) { try { // sm-crypto 的 sm4.encrypt 使用 ECB 模式,自动处理 PKCS5Padding // 密钥需要转换为十六进制字符串,内容需要转换为UTF-8字符串 const keyHex = key.toString('hex'); const contentStr = content.toString('utf8'); // sm4.encrypt(data, key, mode, outputEncoding) // mode: 1=ECB, 2=CBC; outputEncoding: 'base64' 或 'hex',默认为 'base64' // 第一个参数应该是UTF-8字符串,而不是hex字符串 const encryptedHex = sm4.encrypt(contentStr, keyHex, 1, 'hex'); return Buffer.from(encryptedHex, 'hex'); } catch (e) { console.error('Exception-encryptECBPadding()', e.message); return null; } } /** * SM4 ECB模式解密(带PKCS5Padding) * @param {Buffer} cipherText - 待解密内容 * @param {Buffer} key - SM4密钥(16字节) * @returns {Buffer} 解密后的字节数组 */ function decryptECBPadding(cipherText, key) { try { // sm-crypto 的 sm4.decrypt 使用 ECB 模式,自动处理 PKCS5Padding const keyHex = key.toString('hex'); const cipherTextHex = cipherText.toString('hex'); // sm4.decrypt(data, key, mode, outputEncoding) // mode: 1=ECB, 2=CBC; outputEncoding: 'base64' 或 'hex',默认为 'base64' // 第一个参数应该是hex字符串(密文的hex表示) const decryptedHex = sm4.decrypt(cipherTextHex, keyHex, 1, 'hex'); return Buffer.from(decryptedHex, 'hex'); } catch (e) { console.error('Exception-decryptECBPadding()', e.message); return null; } } /** * SM4 ECB模式加密(Base64输入输出) * @param {string} content - 待加密内容 * @param {string} key - Base64编码的SM4密钥 * @returns {string} Base64编码的加密结果 */ function encryptECBPaddingBase64(content, key) { if (isBlank(content) || isBlank(key)) { console.error('【警告】encryptECBPaddingBase64():[IN]content or [IN]key is null!|content=' + content); return content; } try { const contentBuffer = Buffer.from(content, 'utf8'); const keyBuffer = byteArrayFromBase64String(key); if (!keyBuffer) { return null; } const encryptedBuffer = encryptECBPadding(contentBuffer, keyBuffer); if (!encryptedBuffer) { return null; } return base64StringFromByteArray(encryptedBuffer); } catch (e) { console.error('Exception-encryptECBPaddingBase64()', e.message); return null; } } /** * SM4 ECB模式解密(Base64输入输出) * @param {string} cipherText - Base64编码的密文 * @param {string} key - Base64编码的SM4密钥 * @returns {string} 解密后的明文 */ function decryptECBPaddingBase64(cipherText, key) { if (isBlank(cipherText) || isBlank(key)) { console.error('【警告】decryptECBPaddingBase64():[IN]cipherText or [IN]key is null!|cipherText=' + cipherText); return cipherText; } try { const cipherTextBuffer = byteArrayFromBase64String(cipherText); const keyBuffer = byteArrayFromBase64String(key); if (!cipherTextBuffer || !keyBuffer) { return null; } const decryptedBuffer = decryptECBPadding(cipherTextBuffer, keyBuffer); if (!decryptedBuffer) { return null; } return decryptedBuffer.toString('utf8'); } catch (e) { console.error('Exception-decryptECBPaddingBase64()', e.message); return null; } } /** * AES128加密 * @param {string} content - 待加密内容 * @param {Buffer} byteAESKey - AES密钥(16字节) * @returns {Buffer} 加密后的字节数组 */ function encryptAES(content, byteAESKey) { try { const cipher = crypto.createCipheriv('aes-128-ecb', byteAESKey, null); cipher.setAutoPadding(true); let encrypted = cipher.update(content, 'utf8'); encrypted = Buffer.concat([encrypted, cipher.final()]); return encrypted; } catch (e) { console.error('Exception - encryptAES()', e.message); return null; } } /** * AES128解密 * @param {Buffer} content - 待解密内容 * @param {Buffer} byteAESKey - AES密钥(16字节) * @returns {Buffer} 解密后的字节数组 */ function decryptAES(content, byteAESKey) { try { const decipher = crypto.createDecipheriv('aes-128-ecb', byteAESKey, null); decipher.setAutoPadding(true); let decrypted = decipher.update(content); decrypted = Buffer.concat([decrypted, decipher.final()]); return decrypted; } catch (e) { console.error('Exception - decryptAES()', e.message); return null; } } /** * AES128加密(Base64输入输出) * @param {string} content - 待加密内容 * @param {string} base64AESKey - Base64编码的AES密钥 * @returns {string} Base64编码的加密结果 */ function encryptBase64(content, base64AESKey) { try { const keyBuffer = byteArrayFromBase64String(base64AESKey); if (!keyBuffer) { return null; } const encryptedBuffer = encryptAES(content, keyBuffer); if (!encryptedBuffer) { return null; } return base64StringFromByteArray(encryptedBuffer); } catch (e) { console.error('Exception - encryptBase64()', e.message); return null; } } /** * AES128解密(Base64输入输出) * @param {string} base64Content - Base64编码的密文 * @param {Buffer} byteAESKey - AES密钥(16字节) * @returns {Buffer} 解密后的字节数组 */ function decryptBase64FromBuffer(base64Content, byteAESKey) { try { const contentBuffer = byteArrayFromBase64String(base64Content); if (!contentBuffer) { return null; } return decryptAES(contentBuffer, byteAESKey); } catch (e) { console.error('Exception - decryptBase64FromBuffer()', e.message); return null; } } /** * AES128解密(Base64输入输出,返回字符串) * @param {string} base64Content - Base64编码的密文 * @param {string} base64AESKey - Base64编码的AES密钥 * @returns {string} 解密后的明文 */ function decryptBase64(base64Content, base64AESKey) { try { const keyBuffer = byteArrayFromBase64String(base64AESKey); if (!keyBuffer) { return null; } const decryptedBuffer = decryptBase64FromBuffer(base64Content, keyBuffer); if (!decryptedBuffer) { return null; } return decryptedBuffer.toString('utf8'); } catch (e) { console.error('Exception - decryptBase64()', e.message); return null; } } /** * 功能:国密商用SM4加密 * 说明:用于加密其它敏感类数据,带密钥参数 * @param {string} plaintext - 明文 * @param {string} key - Base64编码的SM4密钥 * @returns {string} 加密后的密文(带前缀 |$4|) */ function sm4Encrypt(plaintext, key) { if (isBlank(plaintext) || isBlank(key) || plaintext.startsWith(CIPHERTEXT_PREFIX_THIRD_AES128) || plaintext.startsWith(CIPHERTEXT_PREFIX_THIRD_SM4ECB) ) { return plaintext; } else { const tmp = encryptECBPaddingBase64(plaintext, key); if (tmp == null) { console.error('ERROR-sm4Encrypt():plaintext=' + plaintext + ',encryptRes=' + tmp); } return tmp != null ? (CIPHERTEXT_PREFIX_THIRD_SM4ECB + tmp) : null; } } /** * 功能:国密商用SM4解密 * 说明:用于解密其它敏感类数据,带密钥参数,兼容AES128算法 * @param {string} ciphertext - 密文 * @param {string} key - Base64编码的SM4密钥 * @returns {string} 解密后的明文 */ function sm4Decrypt(ciphertext, key) { if (isBlank(ciphertext) || isBlank(key)) { return ciphertext; } if (ciphertext.startsWith(CIPHERTEXT_PREFIX_THIRD_SM4ECB)) { const tmp = decryptECBPaddingBase64(ciphertext.replace(CIPHERTEXT_PREFIX_THIRD_SM4ECB_REGEX, ''), key); if (tmp == null) { console.error('ERROR-sm4Decrypt():ciphertext=' + ciphertext + ',decryptRes=' + tmp); } return tmp; } else if (ciphertext.startsWith(CIPHERTEXT_PREFIX_THIRD_AES128)) { const tmp = decryptBase64(ciphertext.replace(CIPHERTEXT_PREFIX_THIRD_AES128_REGEX, ''), key); if (tmp == null) { console.error('ERROR-sm4Decrypt():ciphertext=' + ciphertext + ',decryptRes=' + tmp); } return tmp; } else { return ciphertext; } } // 导出函数 module.exports = { sm4Encrypt, sm4Decrypt, encryptECBPaddingBase64, decryptECBPaddingBase64, encryptBase64, decryptBase64 };