讨论广场 问答详情
我想定期更新加密密钥,避免长期使用同一把。所以我应该如何使用 PBKDF2 之类的方式派生新密钥啊?求个思路。
略略略学习 2025-09-12 11:39:03
144 评论 分享
鸿蒙官网鸿蒙应用开发

我想定期更新加密密钥,避免长期使用同一把。所以我应该如何使用 PBKDF2 之类的方式派生新密钥啊?求个思路。
相关代码如下:

```ts
const key = await crypto.subtle.deriveKey(
  { name:'PBKDF2', salt, iterations:10000, hash:'SHA-256' },
  baseKey, { name:'AES-GCM', length:256 }, true, ['encrypt','decrypt']
);
```


 

144 评论 分享
写回答
全部评论(1)
1 楼
import { crypto } from '@ohos.crypto';
import { util } from '@ohos.util';

// 密钥管理类
class KeyManager {
    private static readonly PBKDF2_ITERATIONS = 100000;
    private static readonly SALT_LENGTH = 16; // 128位盐值
    private static readonly KEY_LENGTH = 256; // AES-256密钥长度
    
    // 存储密钥版本和盐值的数据结构
    private keyVersion: number = 1;
    private keySalts: Map<number, Uint8Array> = new Map();
    
    constructor() {
        // 初始化时生成第一个盐值
        this.generateNewSalt(this.keyVersion);
    }
    
    // 生成随机盐值
    private generateNewSalt(version: number): void {
        const salt = new Uint8Array(KeyManager.SALT_LENGTH);
        crypto.getRandomValues(salt);
        this.keySalts.set(version, salt);
    }
    
    // 使用PBKDF2派生密钥
    async deriveKey(
        password: ArrayBuffer, 
        version: number = this.keyVersion
    ): Promise<crypto.CryptoKey> {
        // 获取对应版本的盐值
        const salt = this.keySalts.get(version);
        if (!salt) {
            throw new Error(`No salt found for key version ${version}`);
        }
        
        try {
            // 导入密码作为基础密钥材料
            const baseKey = await crypto.subtle.importKey(
                'raw',
                password,
                { name: 'PBKDF2' },
                false,
                ['deriveKey']
            );
            
            // 使用PBKDF2派生密钥
            const key = await crypto.subtle.deriveKey(
                {
                    name: 'PBKDF2',
                    salt: salt,
                    iterations: KeyManager.PBKDF2_ITERATIONS,
                    hash: 'SHA-256'
                },
                baseKey,
                { name: 'AES-GCM', length: KeyManager.KEY_LENGTH },
                true,
                ['encrypt', 'decrypt']
            );
            
            return key;
        } catch (error) {
            console.error('Key derivation failed:', error);
            throw error;
        }
    }
    
    // 定期更新密钥
    async rotateKey(password: ArrayBuffer): Promise<number> {
        // 增加密钥版本
        this.keyVersion++;
        
        // 为新版本生成盐值
        this.generateNewSalt(this.keyVersion);
        
        // 派生新密钥
        await this.deriveKey(password, this.keyVersion);
        
        console.log(`Key rotated to version ${this.keyVersion}`);
        return this.keyVersion;
    }
    
    // 获取当前密钥版本
    getCurrentVersion(): number {
        return this.keyVersion;
    }
    
    // 获取所有密钥版本信息(用于迁移旧数据)
    getKeyVersions(): number[] {
        return Array.from(this.keySalts.keys());
    }
    
    // 安全地清除旧密钥版本
    purgeOldVersions(keepLastN: number = 2): void {
        const versions = this.getKeyVersions().sort((a, b) => a - b);
        const versionsToRemove = versions.slice(0, versions.length - keepLastN);
        
        for (const version of versionsToRemove) {
            this.keySalts.delete(version);
            console.log(`Purged key version ${version}`);
        }
    }
    
    // 序列化密钥元数据(用于持久化存储)
    serializeMetadata(): string {
        const metadata: {[version: string]: string} = {};
        
        for (const [version, salt] of this.keySalts.entries()) {
            // 将盐值转换为Base64以便存储
            metadata[version.toString()] = util.base64Encode(salt);
        }
        
        return JSON.stringify({
            currentVersion: this.keyVersion,
            salts: metadata
        });
    }
    
    // 从序列化数据恢复密钥元数据
    deserializeMetadata(serialized: string): void {
        const data = JSON.parse(serialized);
        this.keyVersion = data.currentVersion;
        this.keySalts.clear();
        
        for (const [version, saltBase64] of Object.entries(data.salts)) {
            const versionNum = parseInt(version, 10);
            const salt = util.base64Decode(saltBase64 as string);
            this.keySalts.set(versionNum, salt);
        }
    }
}

// 加密服务类
class EncryptionService {
    private keyManager: KeyManager;
    private currentKey: crypto.CryptoKey | null = null;
    
    constructor() {
        this.keyManager = new KeyManager();
    }
    
    // 初始化加密服务
    async initialize(password: ArrayBuffer): Promise<void> {
        this.currentKey = await this.keyManager.deriveKey(password);
    }
    
    // 加密数据(包含密钥版本信息)
    async encryptData(data: ArrayBuffer): Promise<{ciphertext: ArrayBuffer, iv: Uint8Array, keyVersion: number}> {
        if (!this.currentKey) {
            throw new Error('Encryption service not initialized');
        }
        
        // 生成随机初始化向量
        const iv = new Uint8Array(12); // 96位IV用于AES-GCM
        crypto.getRandomValues(iv);
        
        // 加密数据
        const ciphertext = await crypto.subtle.encrypt(
            {
                name: 'AES-GCM',
                iv: iv
            },
            this.currentKey,
            data
        );
        
        return {
            ciphertext,
            iv,
            keyVersion: this.keyManager.getCurrentVersion()
        };
    }
    
    // 解密数据(自动处理密钥版本)
    async decryptData(
        ciphertext: ArrayBuffer, 
        iv: Uint8Array, 
        keyVersion: number,
        password: ArrayBuffer
    ): Promise<ArrayBuffer> {
        // 如果请求的密钥版本不是当前版本,先派生对应版本的密钥
        let key: crypto.CryptoKey;
        if (keyVersion === this.keyManager.getCurrentVersion()) {
            key = this.currentKey!;
        } else {
            key = await this.keyManager.deriveKey(password, keyVersion);
        }
        
        // 解密数据
        try {
            const decrypted = await crypto.subtle.decrypt(
                {
                    name: 'AES-GCM',
                    iv: iv
                },
                key,
                ciphertext
            );
            
            return decrypted;
        } catch (error) {
            console.error('Decryption failed:', error);
            throw new Error('Failed to decrypt data');
        }
    }
    
    // 定期轮换密钥
    async rotateKey(password: ArrayBuffer): Promise<void> {
        const newVersion = await this.keyManager.rotateKey(password);
        this.currentKey = await this.keyManager.deriveKey(password, newVersion);
    }
    
    // 数据迁移:使用新密钥重新加密所有数据
    async migrateData(
        oldPassword: ArrayBuffer,
        encryptedData: Array<{ciphertext: ArrayBuffer, iv: Uint8Array, keyVersion: number}>
    ): Promise<Array<{ciphertext: ArrayBuffer, iv: Uint8Array, keyVersion: number}>> {
        const migratedData = [];
        
        for (const item of encryptedData) {
            // 解密使用旧密钥加密的数据
            const decrypted = await this.decryptData(
                item.ciphertext,
                item.iv,
                item.keyVersion,
                oldPassword
            );
            
            // 使用新密钥重新加密
            const reencrypted = await this.encryptData(decrypted);
            migratedData.push(reencrypted);
        }
        
        return migratedData;
    }
}

// 使用示例
async function demonstrateKeyRotation() {
    // 创建加密服务实例
    const encryptionService = new EncryptionService();
    
    // 假设用户提供的密码
    const password = new TextEncoder().encode('user-secret-password');
    
    // 初始化加密服务
    await encryptionService.initialize(password);
    
    // 加密一些数据
    const originalData = new TextEncoder().encode('Sensitive data that needs protection');
    const encrypted = await encryptionService.encryptData(originalData);
    
    console.log(`Encrypted with key version: ${encrypted.keyVersion}`);
    
    // 解密数据
    const decrypted = await encryptionService.decryptData(
        encrypted.ciphertext,
        encrypted.iv,
        encrypted.keyVersion,
        password
    );
    
    console.log('Decrypted data:', new TextDecoder().decode(decrypted));
    
    // 模拟定期密钥轮换
    console.log('Rotating encryption key...');
    await encryptionService.rotateKey(password);
    
    // 使用新密钥加密数据
    const newData = new TextEncoder().encode('New data encrypted with rotated key');
    const newEncrypted = await encryptionService.encryptData(newData);
    
    console.log(`Encrypted with new key version: ${newEncrypted.keyVersion}`);
    
    // 解密使用新密钥加密的数据
    const newDecrypted = await encryptionService.decryptData(
        newEncrypted.ciphertext,
        newEncrypted.iv,
        newEncrypted.keyVersion,
        password
    );
    
    console.log('Decrypted new data:', new TextDecoder().decode(newDecrypted));
    
    // 验证旧密钥加密的数据仍然可解密
    const oldDecrypted = await encryptionService.decryptData(
        encrypted.ciphertext,
        encrypted.iv,
        encrypted.keyVersion,
        password
    );
    
    console.log('Decrypted old data:', new TextDecoder().decode(oldDecrypted));
}

// 执行示例
demonstrateKeyRotation().catch(console.error);

📋 实现说明
1. 密钥派生过程
PBKDF2 (Password-Based Key Derivation Function 2) 通过以下步骤派生密钥:

输入密码:用户提供的密码作为基础密钥材料

添加盐值:随机生成的盐值确保即使相同密码也会产生不同密钥

多次哈希迭代:增加计算成本,防止暴力破解

生成最终密钥:派生出的密钥用于加密操作

2. 密钥轮换策略
版本控制:每个密钥版本关联唯一的盐值

向后兼容:保留旧密钥版本以便解密历史数据

定期更新:根据安全策略定期生成新密钥版本

安全清理:适时清除过于陈旧的密钥版本

3. 数据加密格式
建议的加密数据格式包含:

密文:实际加密后的数据

初始化向量(IV):随机值,确保相同明文产生不同密文

密钥版本:标识加密时使用的密钥版本

2025-09-12 13:19:37