场景描述
我们在做项目的时候,写接口经常会遇到这样的情况,就是和app端、web端交互的时候传输数据需要进行加密,不能用明文操作。数据传输加密最关键的就是前后端传输的数据最终能被正确的解密出来,今天就来讲讲使用AES加密传输的时候前后端使用的代码。
解决方案
首先来看一下后端java的AES加解密工具类
// 注意,为了能与 iOS 统一,这里的 key 不可以使用 KeyGenerator、SecureRandom、SecretKey 生成
/**
* <p>
* 参数加密解密工具,不要和DESEncrypt弄混了
* </p>
*
* @author ZhaoHang
* @date 2017-09-1 11:55
*/
public class AESUtils {
public static final Logger LOGGER = LoggerFactory.getLogger(AESUtils.class);
private static final String AES = "AES";
private static final String CRYPT_KEY = "y2W89L6BkRAFljhN";
private static final String IV_STRING = "dMitHORyqbeYVE0o";
/**
* 加密
*
* @param content 加密内容
* @return 密文
* @throws Exception e
*/
public static String encrypt(String content) {
byte[] encryptedBytes = new byte[0];
try {
byte[] byteContent = content.getBytes("UTF-8");
// 注意,为了能与 iOS 统一
// 这里的 key 不可以使用 KeyGenerator、SecureRandom、SecretKey 生成
byte[] enCodeFormat = CRYPT_KEY.getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, AES);
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
// 指定加密的算法、工作模式和填充方式
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
encryptedBytes = cipher.doFinal(byteContent);
// 同样对加密后数据进行 base64 编码
} catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
LOGGER.error("AES encrypt Exception,content = {},Exception = {}", content, e.getStackTrace());
}
return new BASE64Encoder().encode(encryptedBytes);
}
/**
* 解密
*
* @param content 密文
* @return 明文
* @throws Exception e
*/
public static String decrypt(String content) {
// base64 解码
try {
byte[] encryptedBytes = new BASE64Decoder().decodeBuffer(content);
byte[] enCodeFormat = CRYPT_KEY.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, AES);
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] result = cipher.doFinal(encryptedBytes);
return new String(result, "UTF-8");
} catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
LOGGER.error("AES decrypt Exception,content = {},Exception = {}", content, e.getStackTrace());
}
return null;
}
}
- android端使用同上与后端java一致
- ios端的代码如下
//先定义一个初始向量的值。
NSString *const kInitVector = @"dMitHORyqbeYVE0o";
NSString *const key= @"y2W89L6BkRAFljhN";
//确定密钥长度,这里选择 AES-128。
size_t const kKeySize = kCCKeySizeAES128;
/**
AES加密方法
@param content 需要加密的字符串
@param key key
@return 加密后的字符串
*/
+ (NSString *)encryptAES:(NSString *)content{
NSData *contentData = [content dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLength = contentData.length;
// 为结束符'\\0' +1
char keyPtr[kKeySize + 1];
memset(keyPtr, 0, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
// 密文长度 <= 明文长度 + BlockSize
size_t encryptSize = dataLength + kCCBlockSizeAES128;
void *encryptedBytes = malloc(encryptSize);
size_t actualOutSize = 0;
NSData *initVector = [kInitVector dataUsingEncoding:NSUTF8StringEncoding];
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding, // 系统默认使用 CBC,然后指明使用 PKCS7Padding
keyPtr,
kKeySize,
initVector.bytes,
contentData.bytes,
dataLength,
encryptedBytes,
encryptSize,
&actualOutSize);
if (cryptStatus == kCCSuccess) {
// 对加密后的数据进行 base64 编码
return [[NSData dataWithBytesNoCopy:encryptedBytes length:actualOutSize] base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
}
free(encryptedBytes);
return nil;
}
/**
AES解密方法
@param content 需要解密的字符串
@param key key
@return 解密后的字符串
*/
+ (NSString *)decryptAES:(NSString *)content{
// 把 base64 String 转换成 Data
NSData *contentData = [[NSData alloc] initWithBase64EncodedString:content options:NSDataBase64DecodingIgnoreUnknownCharacters];
NSUInteger dataLength = contentData.length;
char keyPtr[kKeySize + 1];
memset(keyPtr, 0, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
size_t decryptSize = dataLength + kCCBlockSizeAES128;
void *decryptedBytes = malloc(decryptSize);
size_t actualOutSize = 0;
NSData *initVector = [kInitVector dataUsingEncoding:NSUTF8StringEncoding];
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
keyPtr,
kKeySize,
initVector.bytes,
contentData.bytes,
dataLength,
decryptedBytes,
decryptSize,
&actualOutSize);
if (cryptStatus == kCCSuccess) {
return [[NSString alloc] initWithData:[NSData dataWithBytesNoCopy:decryptedBytes length:actualOutSize] encoding:NSUTF8StringEncoding];
}
free(decryptedBytes);
return nil;
}
- 接下来是js的代码,js中aes加密需要用到 CryptoJS工具包
下载地址http://download.csdn.net/download/gao36951/9974349
<script type="text/javascript" src="./CryptoJS/crypto-js.js"></script>
<script>
//直接上代码
var key = CryptoJS.enc.Utf8.parse('y2W89L6BkRAFljhN');
var iv = CryptoJS.enc.Utf8.parse('dMitHORyqbeYVE0o');
var source = '要加密的字符串';
var password=CryptoJS.enc.Utf8.parse(source);
console.log("原始字符串:"+source);
console.log("utf8处理后:"+password);
var encrypted = CryptoJS.AES.encrypt(password, key, { iv: iv,mode:CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}); //CryptoJS.pad.Pkcs7
var decrypted = CryptoJS.AES.decrypt(encrypted, key, { iv: iv,mode:CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}); //CryptoJS.pad.Pkcs7
console.log("加密后base64:"+encrypted);
var encryptedStr=encrypted.ciphertext.toString();
console.log("加密后16进制:"+encryptedStr);
console.log("解密后utf8:"+decrypted);
console.log("解密后原始字符串:"+decrypted.toString(CryptoJS.enc.Utf8));
</script>
js测试截图
使用js加密的结果在java中解密如下:
ios的同理,有兴趣的童鞋可以亲测一下,这些都是项目中实际亲测的希望有用~