1. 安装 Crypto.js
由于小程序环境限制,需要下载特定版本的 Crypto.js:
// 方法一:使用 npm(需在 project.config.json 中开启 npm 支持)
npm install crypto-js
// 方法二:手动引入
// 1. 下载 crypto-js:https://github.com/brix/crypto-js
// 2. 将所需文件复制到小程序项目中,如 utils/crypto-js/
2. 基础加密解密示例
AES 加密解密
// utils/cryptoUtil.js
const CryptoJS = require('./crypto-js/crypto-js.js');
class CryptoUtil {
/**
* AES 加密
* @param {string} data 待加密数据
* @param {string} key 密钥
* @param {string} iv 向量(可选)
* @returns {string} 加密后的 Base64 字符串
*/
static aesEncrypt(data, key, iv = '') {
try {
// 处理密钥和向量
const keyUtf8 = CryptoJS.enc.Utf8.parse(this._handleKey(key));
const ivUtf8 = iv ? CryptoJS.enc.Utf8.parse(this._handleKey(iv)) : null;
// 加密配置
const cfg = {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
};
// 如果有向量,添加到配置中
if (ivUtf8) {
cfg.iv = ivUtf8;
}
// 执行加密
const encrypted = CryptoJS.AES.encrypt(
CryptoJS.enc.Utf8.parse(data),
keyUtf8,
cfg
);
// 返回 Base64 字符串
return encrypted.toString();
} catch (error) {
console.error('AES 加密失败:', error);
return null;
}
}
/**
* AES 解密
* @param {string} data 待解密数据(Base64)
* @param {string} key 密钥
* @param {string} iv 向量(可选)
* @returns {string} 解密后的字符串
*/
static aesDecrypt(data, key, iv = '') {
try {
// 处理密钥和向量
const keyUtf8 = CryptoJS.enc.Utf8.parse(this._handleKey(key));
const ivUtf8 = iv ? CryptoJS.enc.Utf8.parse(this._handleKey(iv)) : null;
// 解密配置
const cfg = {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
};
// 如果有向量,添加到配置中
if (ivUtf8) {
cfg.iv = ivUtf8;
}
// 执行解密
const decrypted = CryptoJS.AES.decrypt(data, keyUtf8, cfg);
// 返回 UTF-8 字符串
return decrypted.toString(CryptoJS.enc.Utf8);
} catch (error) {
console.error('AES 解密失败:', error);
return null;
}
}
/**
* 处理密钥长度
* @private
*/
static _handleKey(key) {
// AES 密钥长度:16/24/32 字节(对应 128/192/256 位)
const keyLength = [16, 24, 32];
const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
// 如果密钥长度符合要求,直接返回
for (let len of keyLength) {
if (keyUtf8.sigBytes === len) {
return key;
}
}
// 密钥长度不符合,进行补全或截取
let newKey = key;
if (keyUtf8.sigBytes < 16) {
// 补全到 16 位
while (newKey.length < 16) {
newKey += '0';
}
newKey = newKey.substring(0, 16);
} else if (keyUtf8.sigBytes > 32) {
// 截取到 32 位
newKey = newKey.substring(0, 32);
}
return newKey;
}
}
module.exports = CryptoUtil;
DES 加密解密
// 在 CryptoUtil 类中添加 DES 方法
class CryptoUtil {
// ... 之前的 AES 方法 ...
/**
* DES 加密
* @param {string} data 待加密数据
* @param {string} key 密钥(8字节)
* @param {string} iv 向量(8字节)
* @returns {string} 加密后的 Base64 字符串
*/
static desEncrypt(data, key, iv = '') {
try {
const keyUtf8 = CryptoJS.enc.Utf8.parse(key.substring(0, 8));
const ivUtf8 = iv ? CryptoJS.enc.Utf8.parse(iv.substring(0, 8)) : null;
const cfg = {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
iv: ivUtf8
};
const encrypted = CryptoJS.DES.encrypt(
CryptoJS.enc.Utf8.parse(data),
keyUtf8,
cfg
);
return encrypted.toString();
} catch (error) {
console.error('DES 加密失败:', error);
return null;
}
}
/**
* DES 解密
* @param {string} data 待解密数据
* @param {string} key 密钥(8字节)
* @param {string} iv 向量(8字节)
* @returns {string} 解密后的字符串
*/
static desDecrypt(data, key, iv = '') {
try {
const keyUtf8 = CryptoJS.enc.Utf8.parse(key.substring(0, 8));
const ivUtf8 = iv ? CryptoJS.enc.Utf8.parse(iv.substring(0, 8)) : null;
const cfg = {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
iv: ivUtf8
};
const decrypted = CryptoJS.DES.decrypt(data, keyUtf8, cfg);
return decrypted.toString(CryptoJS.enc.Utf8);
} catch (error) {
console.error('DES 解密失败:', error);
return null;
}
}
}
3. 哈希函数示例
// 在 CryptoUtil 类中添加哈希方法
class CryptoUtil {
// ... 之前的加解密方法 ...
/**
* MD5 哈希
* @param {string} data 待哈希数据
* @returns {string} 哈希结果(十六进制)
*/
static md5(data) {
return CryptoJS.MD5(data).toString();
}
/**
* SHA1 哈希
* @param {string} data 待哈希数据
* @returns {string} 哈希结果(十六进制)
*/
static sha1(data) {
return CryptoJS.SHA1(data).toString();
}
/**
* SHA256 哈希
* @param {string} data 待哈希数据
* @returns {string} 哈希结果(十六进制)
*/
static sha256(data) {
return CryptoJS.SHA256(data).toString();
}
/**
* HMAC-SHA256 签名
* @param {string} data 待签名数据
* @param {string} key 密钥
* @returns {string} 签名结果(十六进制)
*/
static hmacSha256(data, key) {
return CryptoJS.HmacSHA256(data, key).toString();
}
/**
* Base64 编码
* @param {string} data 待编码数据
* @returns {string} Base64 编码结果
*/
static base64Encode(data) {
return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(data));
}
/**
* Base64 解码
* @param {string} data Base64 数据
* @returns {string} 解码后的字符串
*/
static base64Decode(data) {
return CryptoJS.enc.Base64.parse(data).toString(CryptoJS.enc.Utf8);
}
}
4. 使用示例
页面中使用
// pages/index/index.js
const CryptoUtil = require('../../utils/cryptoUtil.js');
Page({
data: {
originalText: 'Hello, 微信小程序!',
encryptedText: '',
decryptedText: '',
hashResult: ''
},
onLoad() {
this.testEncryption();
},
testEncryption() {
// 测试数据
const text = this.data.originalText;
const key = '0123456789abcdef'; // 16字节密钥
const iv = 'fedcba9876543210'; // 16字节向量
// 1. AES 加密解密
const encrypted = CryptoUtil.aesEncrypt(text, key, iv);
const decrypted = CryptoUtil.aesDecrypt(encrypted, key, iv);
console.log('AES 加密结果:', encrypted);
console.log('AES 解密结果:', decrypted);
// 2. MD5 哈希
const md5Hash = CryptoUtil.md5(text);
console.log('MD5 哈希:', md5Hash);
// 3. SHA256 哈希
const sha256Hash = CryptoUtil.sha256(text);
console.log('SHA256 哈希:', sha256Hash);
// 4. Base64 编解码
const base64Encoded = CryptoUtil.base64Encode(text);
const base64Decoded = CryptoUtil.base64Decode(base64Encoded);
console.log('Base64 编码:', base64Encoded);
console.log('Base64 解码:', base64Decoded);
this.setData({
encryptedText: encrypted,
decryptedText: decrypted,
hashResult: md5Hash
});
},
// 实际应用:加密用户数据
encryptUserData() {
const userData = {
userId: '12345',
username: '张三',
timestamp: Date.now()
};
const jsonStr = JSON.stringify(userData);
const key = 'mySecretKey123456'; // 实际应用中应从安全位置获取
const iv = 'initialVector123';
// 加密数据
const encrypted = CryptoUtil.aesEncrypt(jsonStr, key, iv);
// 发送到服务器
wx.request({
url: 'https://api.example.com/save',
method: 'POST',
data: {
encryptedData: encrypted,
// 可以同时发送哈希值用于验证
signature: CryptoUtil.hmacSha256(encrypted, key)
},
success(res) {
console.log('加密数据发送成功');
}
});
}
});
WXML 页面
<!-- pages/index/index.wxml -->
<view class="container">
<view class="section">
<text class="title">原始文本:</text>
<text>{{originalText}}</text>
</view>
<view class="section">
<text class="title">AES 加密结果:</text>
<text class="encrypted">{{encryptedText}}</text>
</view>
<view class="section">
<text class="title">AES 解密结果:</text>
<text>{{decryptedText}}</text>
</view>
<view class="section">
<text class="title">MD5 哈希值:</text>
<text class="hash">{{hashResult}}</text>
</view>
<button type="primary" bindtap="encryptUserData">
加密并发送用户数据
</button>
</view>
WXSS 样式
/* pages/index/index.wxss */
.container {
padding: 20rpx;
}
.section {
margin: 30rpx 0;
padding: 20rpx;
background-color: #f5f5f5;
border-radius: 10rpx;
}
.title {
font-weight: bold;
color: #333;
display: block;
margin-bottom: 10rpx;
}
.encrypted {
word-break: break-all;
font-size: 24rpx;
color: #e44d26;
}
.hash {
font-family: monospace;
word-break: break-all;
font-size: 24rpx;
color: #1890ff;
}
5. 注意事项
密钥管理:
- 不要将密钥硬编码在客户端代码中
- 建议从服务器动态获取密钥
- 可以使用微信登录的 session_key 作为加密密钥
性能考虑:
- 大量数据加密可能影响性能
- 考虑在 Worker 线程中执行加密操作
安全性:
- 客户端加密不能替代 HTTPS
- 使用合适的加密模式和填充方式
- 定期更换密钥
小程序限制:
- 确保 Crypto.js 文件在小程序包大小限制内
- 测试在不同 iOS/Android 版本下的兼容性
6. 优化建议
// 添加缓存机制
class CryptoUtil {
static cache = new Map();
static getCachedEncrypt(data, key, iv) {
const cacheKey = `encrypt_${data}_${key}_${iv}`;
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
const result = this.aesEncrypt(data, key, iv);
this.cache.set(cacheKey, result);
return result;
}
// 添加错误重试机制
static safeEncrypt(data, key, iv, retryCount = 3) {
for (let i = 0; i < retryCount; i++) {
try {
return this.aesEncrypt(data, key, iv);
} catch (error) {
if (i === retryCount - 1) throw error;
console.warn(`加密失败,第${i + 1}次重试`);
}
}
}
}
这个示例提供了完整的 Crypto.js 在小程序中的使用方案,包括安装、基础加解密、哈希函数、实际应用和注意事项。