「通信安全」对称与非对称加密
目录
加密算法对比与跨平台实现指南
一、HTTPS与加密算法基础
1. HTTPS核心机制
- 加密实现:结合非对称加密(密钥交换)与对称加密(数据传输)
- 安全风险:存在中间人劫持风险(需受客户端信任的证书防御)
- 最佳实践:
为同时兼顾安全性及性能,建议采用由非对称加密进行对称加密密钥的传输,定期更换对称密钥(对称加密在当前计算水平上只能保证在一段时间内是安全的),使用对称加密完成所有数据的加密传输。
二、非对称加密详解
特性分析
| 特性 | 说明 |
|---|---|
| 安全性 | 数据加密安全性最高(基于数学难题) |
| 适用场景 | 仅适合小数据加密(如密钥交换、数字签名) |
| 性能 | 加解密耗时较高(计算密集型) |
典型应用场景
1. 加密传输数据
- 工作原理:A、B双方各持有对方公钥(公钥公开),使用对方公钥加密数据,仅对方私钥可解密
- 示例:A用B的公钥加密消息→B用私钥解密
2. 防止数据被伪造
- 工作原理:A、B双方各持有对方公钥,使用各自私钥加密数据,仅公钥可解密
- 示例:A用私钥签名→B用A公钥验证
3. 加密并防止数据伪造(双重保障)
- 方案一(公钥公开):
- 使用私钥加密(防篡改)
- 使用对方公钥加密(防窃听)
- 方案二(公钥不公开):
- 使用私钥加密(防篡改)
- 无需额外加密(公钥不公开,无法伪造)
三、对称加密详解
核心特性
| 特性 | 说明 |
|---|---|
| 安全性 | 数据加密安全性较高 |
| 适用场景 | 适合大量数据加密(如文件传输、数据库加密) |
| 性能 | 加解密耗时较少(计算高效) |
典型应用场景
- 加密并防止传输数据伪造
- 对大量数据进行加密
跨平台兼容方案
| 参数 | 值 |
|---|---|
| 算法 | AES-256-CBC |
| 填充方案 | PKCS7Padding |
| 密钥派生 | PBKDF2WithHmacSHA1 |
| 迭代次数 | 10,000次 |
| 密钥长度 | 256位 |
兼容性说明:经实践验证,此组合能同时兼容PHP、iOS、Android、Java平台。
四、多平台对称加密实现
1. PHP实现
/**
* 对称加密算法类
*/
class SymmetricEncryption{
const CIPHER = MCRYPT_RIJNDAEL_128;
const MODEL = MCRYPT_MODE_CBC;
const HASH_ALGORITHM = 'sha1';
// 对应android、iOS的{0,1,2,3,4,5,6,7,8,9,0xA,0xB,0xC,0xD,0xE,0xF}
const HASH_SALT = 'AAECAwQFBgcICQoLDA0ODw==';
const HASH_ITERATIONS = 10000;
// 对应android、iOS的 KEY_LENGTH/8
const HASH_KEY_LENGTH = 32;
// 对应android、iOS的{0xA,1,0xB,5,4,0xF,7,9,0x17,3,1,6,8,0xC,0xD,91}
const IV = 'CgELBQQPBwkXAwEGCAwNWw==';
/**
* 加密
* @param $key string 加密密钥
* @param $data string 待加密明文
* @return string 密文
*/
public static function encrypt($key, $data){
// 对应安卓和iOS的PBKDF2WithHmacSHA1
$key = hash_pbkdf2(
self::HASH_ALGORITHM,
$key,
base64_decode(self::HASH_SALT),
self::HASH_ITERATIONS,
self::HASH_KEY_LENGTH,
true
);
// 使用PKCS7Padding的方式进行填充
$data = self::PKCS7Padding($data);
# 创建和 AES 兼容的密文(Rijndael 分组大小 = 256)
$cipherText = mcrypt_encrypt(
self::CIPHER,
$key,
$data,
self::MODEL,
base64_decode(self::IV)
);
# 对密文进行 base64 编码便于显性复制
$cipherTextBase64 = base64_encode($cipherText);
return $cipherTextBase64;
}
/**
* 解密
* @param $key string 解密密钥
* @param $data string 待解密密文
* @return string 明文
*/
public static function decrypt($key, $data){
// 对应安卓和iOS的PBKDF2WithHmacSHA1
$key = hash_pbkdf2(
self::HASH_ALGORITHM,
$key,
base64_decode(self::HASH_SALT),
self::HASH_ITERATIONS,
self::HASH_KEY_LENGTH,
true
);
# --- 解密 ---
$cipherTextDec = base64_decode($data);
$plaintextDec = mcrypt_decrypt(
self::CIPHER,
$key,
$cipherTextDec,
self::MODEL,
base64_decode(self::IV)
);
// 移除PKCS7Padding填充
$plaintextDec = self::PKCS7RemovePadding($plaintextDec);
return $plaintextDec;
}
/**
* PKCS7Padding模式填充
* @param $data
* @return string
*/
public static function PKCS7Padding($data){
$blockSize = mcrypt_get_block_size(self::CIPHER,self::MODEL);
$pad = $blockSize - (strlen($data) % $blockSize);
return $data . str_repeat(chr($pad), $pad);
}
/**
* 移除PKCS7Padding模式填充
* @param $data
* @return string
*/
public static function PKCS7RemovePadding($data){
$pad = ord($data[strlen($data) - 1]);
return substr($data, 0, -$pad);
}
}
2. iOS实现
static const char encodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const NSUInteger kAlgorithmKeySize = kCCKeySizeAES256;
const NSUInteger kPBKDFRounds = 10000; // ~80ms on an iPhone 4
static Byte saltBuff[] = {0,1,2,3,4,5,6,7,8,9,0xA,0xB,0xC,0xD,0xE,0xF};
static Byte ivBuff[] = {0xA,1,0xB,5,4,0xF,7,9,0x17,3,1,6,8,0xC,0xD,91};
@implementation NSData (AES256)
+ (NSData *)AESKeyForPassword:(NSString *)password{
NSMutableData *derivedKey = [NSMutableData dataWithLength:kAlgorithmKeySize];
NSData *salt = [NSData dataWithBytes:saltBuff length:kCCKeySizeAES128];
int result = CCKeyDerivationPBKDF(kCCPBKDF2,
password.UTF8String,
password.length,
salt.bytes,
salt.length,
kCCPRFHmacAlgSHA1,
kPBKDFRounds,
derivedKey.mutableBytes,
derivedKey.length);
NSAssert(result == kCCSuccess,
@"Unable to create AES key for spassword: %d", result);
return derivedKey;
}
/*加密方法*/
+ (NSString *)AES256EncryptWithPlainText:(NSString *)plain key:(NSString *)key {
NSData *plainText = [plain dataUsingEncoding:NSUTF8StringEncoding];
char keyPtr[kCCKeySizeAES256+1];
bzero(keyPtr, sizeof(keyPtr));
NSUInteger dataLength = [plainText length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
bzero(buffer, sizeof(buffer));
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
[[NSData AESKeyForPassword:key] bytes], kCCKeySizeAES256,
ivBuff,
[plainText bytes], dataLength,
buffer, bufferSize,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
NSData *encryptData = [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
return [encryptData base64Encoding];
}
free(buffer);
return nil;
}
/*解密方法*/
+ (NSString *)AES256DecryptWithCiphertext:(NSString *)ciphertexts key:(NSString *)key{
NSData *cipherData = [NSData dataWithBase64EncodedString:ciphertexts];
char keyPtr[kCCKeySizeAES256+1];
bzero(keyPtr, sizeof(keyPtr));
NSUInteger dataLength = [cipherData length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
[[NSData AESKeyForPassword:key] bytes], kCCKeySizeAES256,
ivBuff,
[cipherData bytes], dataLength,
buffer, bufferSize,
&numBytesDecrypted);
if (cryptStatus == kCCSuccess) {
NSData *encryptData = [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
return [[NSString alloc] initWithData:encryptData encoding:NSUTF8StringEncoding];
}
free(buffer);
return nil;
}
@end
3. Android实现
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class AES {
private final String KEY_GENERATION_ALG = "PBKDF2WithHmacSHA1";
private final int HASH_ITERATIONS = 10000;
private final int KEY_LENGTH = 256;
private byte[] salt = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD,
0xE, 0xF };
private byte[] iv = { 0xA, 1, 0xB, 5, 4, 0xF, 7, 9, 0x17, 3, 1, 6, 8, 0xC,
0xD, 91 };
private final String CIPHERMODEPADDING = "AES/CBC/PKCS7Padding";
private PBEKeySpec myKeyspec = null;
private SecretKeyFactory keyfactory = null;
private SecretKey sk = null;
private SecretKeySpec skforAES = null;
private IvParameterSpec IV;
public AES(String password) {
try {
myKeyspec = new PBEKeySpec(password.toCharArray(), salt,
HASH_ITERATIONS, KEY_LENGTH);
keyfactory = SecretKeyFactory.getInstance(KEY_GENERATION_ALG);
sk = keyfactory.generateSecret(myKeyspec);
} catch (Exception ex) {
System.err.println("Encryption Exception: " + ex.toString());
}
byte[] skAsByteArray = sk.getEncoded();
skforAES = new SecretKeySpec(skAsByteArray, "AES");
IV = new IvParameterSpec(iv);
}
public String encrypt(byte[] plaintext) {
byte[] ciphertext = encrypt(CIPHERMODEPADDING, skforAES, IV, plaintext);
String base64_ciphertext = Base64Encoder.encode(ciphertext);
return base64_ciphertext;
}
public String decrypt(String ciphertext_base64) {
byte[] s = Base64Decoder.decodeToBytes(ciphertext_base64);
String decrypted = new String(decrypt(CIPHERMODEPADDING, skforAES, IV, s));
return decrypted;
}
private byte[] encrypt(String cmp, SecretKey sk, IvParameterSpec IV, byte[] msg) {
try {
Cipher c = Cipher.getInstance(cmp);
c.init(Cipher.ENCRYPT_MODE, sk, IV);
return c.doFinal(msg);
} catch (Exception ex) {
System.err.println("Encryption Exception: " + ex.toString());
}
return null;
}
private byte[] decrypt(String cmp, SecretKey sk, IvParameterSpec IV, byte[] ciphertext) {
try {
Cipher c = Cipher.getInstance(cmp);
c.init(Cipher.DECRYPT_MODE, sk, IV);
return c.doFinal(ciphertext);
}catch (Exception ex) {
System.err.println("Decryption Exception: " + ex.toString());
}
return null;
}
}
五、关键实现差异说明
重要说明:PHP官方文档中使用对称加密的方式是生成随机的IV并将其合并到加密数据之前一起传输(优点是相同数据每次加密结果不同),而Android和iOS的做法是使用固定salt将对称密钥转换为AES密钥后作为实际加解密的密钥。
兼容性关键点
| 要素 | 说明 | 重要性 |
|---|---|---|
| Salt值 | 0,1,2,3,4,5,6,7,8,9,0xA,0xB,0xC,0xD,0xE,0xF | 必须严格一致 |
| IV向量 | 0xA,1,0xB,5,4,0xF,7,9,0x17,3,1,6,8,0xC,0xD,91 | 必须严格一致 |
| 密钥派生 | PBKDF2WithHmacSHA1 + 10,000次迭代 | 必须严格一致 |
| 填充方案 | PKCS7Padding | 必须严格一致 |
六、安全实施建议
-
密钥管理:
- 避免在代码中硬编码密钥
- 使用安全的密钥存储机制(如Android Keystore、iOS Keychain)
- 实施密钥轮换策略(建议每90天轮换一次)
-
传输安全:
- 所有加密数据必须通过HTTPS传输
- 使用TLS 1.2+及以上版本
-
性能优化:
- 在支持AES-NI的CPU上启用硬件加速
- 对于高频加密场景,考虑使用内存缓存密钥
-
安全审计:
- 每季度进行密码学实现审计
- 每年评估并更新加密算法(建议每3年增加迭代次数2000次)
重要提示:在实际部署中,应使用标准化的加密库而非自行实现,以避免常见实现错误。推荐使用OpenSSL、Bouncy Castle等经过验证的库。