目录

「通信安全」对称与非对称加密

加密算法对比与跨平台实现指南

一、HTTPS与加密算法基础

1. HTTPS核心机制

  • 加密实现:结合非对称加密(密钥交换)与对称加密(数据传输)
  • 安全风险:存在中间人劫持风险(需受客户端信任的证书防御)
  • 最佳实践

    为同时兼顾安全性及性能,建议采用由非对称加密进行对称加密密钥的传输,定期更换对称密钥(对称加密在当前计算水平上只能保证在一段时间内是安全的),使用对称加密完成所有数据的加密传输。

二、非对称加密详解

特性分析

特性 说明
安全性 数据加密安全性最高(基于数学难题)
适用场景 仅适合小数据加密(如密钥交换、数字签名)
性能 加解密耗时较高(计算密集型)

典型应用场景

1. 加密传输数据

  • 工作原理:A、B双方各持有对方公钥(公钥公开),使用对方公钥加密数据,仅对方私钥可解密
  • 示例:A用B的公钥加密消息→B用私钥解密

2. 防止数据被伪造

  • 工作原理:A、B双方各持有对方公钥,使用各自私钥加密数据,仅公钥可解密
  • 示例:A用私钥签名→B用A公钥验证

3. 加密并防止数据伪造(双重保障)

  • 方案一(公钥公开):
    1. 使用私钥加密(防篡改)
    2. 使用对方公钥加密(防窃听)
  • 方案二(公钥不公开):
    1. 使用私钥加密(防篡改)
    2. 无需额外加密(公钥不公开,无法伪造)

三、对称加密详解

核心特性

特性 说明
安全性 数据加密安全性较高
适用场景 适合大量数据加密(如文件传输、数据库加密)
性能 加解密耗时较少(计算高效)

典型应用场景

  • 加密并防止传输数据伪造
  • 对大量数据进行加密

跨平台兼容方案

参数
算法 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 必须严格一致

六、安全实施建议

  1. 密钥管理

    • 避免在代码中硬编码密钥
    • 使用安全的密钥存储机制(如Android Keystore、iOS Keychain)
    • 实施密钥轮换策略(建议每90天轮换一次)
  2. 传输安全

    • 所有加密数据必须通过HTTPS传输
    • 使用TLS 1.2+及以上版本
  3. 性能优化

    • 在支持AES-NI的CPU上启用硬件加速
    • 对于高频加密场景,考虑使用内存缓存密钥
  4. 安全审计

    • 每季度进行密码学实现审计
    • 每年评估并更新加密算法(建议每3年增加迭代次数2000次)

重要提示:在实际部署中,应使用标准化的加密库而非自行实现,以避免常见实现错误。推荐使用OpenSSL、Bouncy Castle等经过验证的库。