前景:
1、本项目原有功能RSA客户端对敏感信息进行加密
2、本次漏洞说是服务端返回值有敏感信息,需要密文返回
方案:
本次方案不算完美,还是有被劫持篡改的风险,但基本https证书认证加持,风险相对较小
客户端本地存需要存三个值:
1、RSA公钥
2、随机生成16个字符
3、AES偏移量
java加密
安卓同服务端代码
@Slf4j
public class AESClientUtils {
private static final String AES = "AES";
private static final String STRING_FORMAT = "UTF-8";
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
//定义一个初始向量的值 AES偏移量,客户端和服务端保持统一值
private static final String IV_STRING = "9999543210123666";
/**
* 加密
*
* @param aesKey AESkey
* @param content 明文
* @return 密文
*/
public static String encrypt(String aesKey, String content) {
try {
byte[] byteContent = content.getBytes(STRING_FORMAT);
// 注意,为了能与 iOS 统一 这里的 key 不可以使用 KeyGenerator、SecureRandom、SecretKey 生成
byte[] enCodeFormat = aesKey.getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, AES);
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
// 指定加密的算法、工作模式和填充方式
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encryptedBytes = cipher.doFinal(byteContent);
// 同样对加密后数据进行 base64 编码
return Base64Utils.encode(encryptedBytes);
} catch (Exception e) {
log.error("AES Client 加密失败 content={},encodeRules={}", content, aesKey, e);
return null;
}
}
/**
* 解密
*
* @param aesKey AESkey
* @param content 密文
* @return 解密后的明文
*/
public static String decrypt(String aesKey,String content) {
try {
Base64.Decoder decoder = Base64.getDecoder();
byte[] encryptedBytes = decoder.decode(content);
byte[] enCodeFormat = aesKey.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, AES);
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] result = cipher.doFinal(encryptedBytes);
return new String(result, STRING_FORMAT);
} catch (Exception e) {
log.error("AES Client 解密失败 content={},encodeRules={}", content, aesKey, e);
return null;
}
}
}
H5解密
var iv = "9999543210123666";
const ciphertext = response.data.info[ele]
//保存本地的随机生成的16字节密钥
const key = CryptoJS.enc.Utf8.parse(sessionStorage.getItem('key'))
//AES的偏移量
const iv = CryptoJS.enc.Utf8.parse('9999543210123666')
// 解密
const bytes = CryptoJS.AES.decrypt(ciphertext, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
})
// 解密后,需要将解密后的字符串转成UTF-8格式
response.data.info[ele] = bytes.toString(CryptoJS.enc.Utf8)
IOS解密
先定义一个初始向量的值
NSString *const kInitVector = @"9999543210123666"
确定密钥长度,这里选择 AES-128。
size_t const kKeySize = kCCKeySizeAES128
+ (NSString *)decryptAES:(NSString *)content key:(NSString *)key {
// 把 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;
}