本文旨在深入探讨华为鸿蒙HarmonyOS Next系统(截止目前API12)中加解密算法参数与模式的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。

一、加解密参数总览

在HarmonyOS Next的加解密算法中,各种参数就像是一把把钥匙,共同决定了加解密的方式和效果。

(一)加密模式

加密模式决定了数据在加密过程中的处理方式。例如,ECB(Electronic Codebook)模式是最基本的加密模式,它将明文分成固定大小的块,每个块独立进行加密。这种模式简单直接,但如果明文相同,加密后的密文也相同,可能存在安全风险,适用于对少量数据进行加密且对安全性要求不高的场景。
CBC(Cipher Block Chaining)模式则引入了反馈机制,前一个密文块会参与到下一个明文块的加密过程中,增加了加密的安全性,常用于文件加密等场景。
GCM(Galois/Counter Mode)模式不仅能加密数据,还能提供认证功能,确保数据的完整性和真实性,在安全通信中应用广泛。

(二)填充模式

填充模式用于处理明文长度不是分组长度整数倍的情况。NoPadding模式要求输入数据必须与分组长度匹配,适用于数据长度已知且恰好为分组长度整数倍的情况。
PKCS5和PKCS7填充模式则通过添加特定的填充字节,使数据长度达到分组长度的整数倍。PKCS5适用于分组长度为8字节的情况(如3DES算法),而PKCS7更为通用,适用于多种分组长度(如AES算法的16字节分组)。

(三)摘要算法与掩码摘要

在非对称密钥算法中,摘要算法(如SHA256、MD5等)用于对数据进行摘要计算,生成数据的特征值。掩码摘要(如MGF1_SHA256)则在一些算法(如RSA的PKCS1_OAEP模式)中用于进一步处理摘要值,增加安全性。
这些参数相互关联,共同影响着加解密的过程和结果。例如,加密模式的选择会影响填充模式的适用性,而摘要算法和掩码摘要的设置则与签名验签的准确性和安全性密切相关。

二、对称密钥参数与模式

(一)不同算法的模式特点与适用场景

  1. AES算法
       - ECB模式:特点是简单快速,但安全性相对较低。适用场景如加密一些配置文件中的单个参数,这些参数之间相互独立且对安全性要求不高。
       - CBC模式:安全性较高,适用于加密文件或网络通信中的连续数据,如加密用户登录时传输的密码等敏感信息。
       - GCM模式:兼具加密和认证功能,适用于对数据完整性和保密性要求都很高的场景,如安全通信协议中的数据加密。
  2. 3DES算法
       - 3DES算法的ECB、CBC等模式与AES类似,但由于其计算速度相对较慢,在现代应用中逐渐被AES取代。不过,在一些对兼容性有要求的旧系统中仍可能会使用。
  3. SM4算法
       - SM4算法的各种模式也用于不同场景,如在国内的一些安全要求较高的领域(政务、金融等)中,用于加密存储和传输敏感数据。

    (二)参数要求对比与示例代码

    以下是不同对称密钥算法在不同模式下的参数要求对比表格(部分示例):

    算法加密模式填充模式参数要求
    AESECB[NoPadding, PKCS5, PKCS7]密钥长度可选128/192/256位
    AESCBC[NoPadding, PKCS5, PKCS7]需要初始化向量(IV),密钥长度可选128/192/256位
    AESGCM[NoPadding, PKCS5, PKCS7]需要IV和附加验证数据(aad),密钥长度可选128/192/256位
    3DESECB[NoPadding, PKCS5, PKCS7]密钥长度固定为192位
    3DESCBC[NoPadding, PKCS5, PKCS7]需要IV,密钥长度固定为192位
    SM4ECB[NoPadding, PKCS5, PKCS7]密钥长度固定为128位
    SM4CBC[NoPadding, PKCS5, PKCS7]需要IV,密钥长度固定为128位

    示例代码展示如何根据需求选择参数和模式进行AES加密(以GCM模式为例):

    import { cryptoFramework } from '@kit.CryptoArchitectureKit';
    import { buffer } from '@kit.ArkTS';
    // 生成AES对称密钥
    async function generateAESKey() {
        let aesGenerator = cryptoFramework.createSymKeyGenerator('AES128');
        let keyBlob = { data: new Uint8Array([83, 217, 231, 76, 28, 113, 23, 219, 250, 71, 209, 210, 205, 97, 32, 159]) };
        return await aesGenerator.convertKey(keyBlob);
    }
    // 加密函数
    async function encryptMessage(symKey, plainText) {
        let cipher = cryptoFramework.createCipher('AES128|GCM|PKCS7');
        let iv = new Uint8Array(12); // 生成12字节的随机IV
        let aad = new Uint8Array(8); // 假设附加验证数据为8字节
        let params = {
            iv: { data: iv },
            aad: { data: aad }
        };
        await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, params);
        let encryptData = await cipher.doFinal(plainText);
        // 处理加密结果,如获取认证信息(authTag)
        let authTag = cipher.getAuthTag();
        return { encryptedData: encryptData, authTag: authTag };
    }
    async function main() {
        try {
            let symKey = await generateAESKey();
            let message = "This is a test";
            let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(message, 'utf-8').buffer) };
            let encryptedResult = await encryptMessage(symKey, plainText);
            console.log('Encrypted data:', encryptedResult.encryptedData);
            console.log('AuthTag:', encryptedResult.authTag);
        } catch (error) {
            console.error('Encryption failed:', error);
        }
    }
    main();

    在这个示例中,我们选择了AES128算法、GCM模式和PKCS7填充模式。通过正确设置密钥、IV和aad等参数,实现了数据的加密,并获取了认证信息。参数的选择直接影响了加密的结果和安全性,如果参数设置错误,可能导致加密失败或安全性降低。

    三、非对称密钥参数与模式

    (一)不同模式下的参数设置及意义

  4. RSA算法
       - PKCS1填充模式:在签名验签时,需要设置摘要算法(如SHA256)。摘要算法用于对要签名的数据进行摘要计算,生成固定长度的特征值。这个特征值会被用于签名过程,确保数据的完整性。在加密时,输入数据长度需满足一定要求(小于RSA钥模 - 11)。
       - PKCS1_OAEP填充模式:需要设置摘要算法(如SHA256)和掩码摘要(如MGF1_SHA256)。摘要算法用于生成数据的摘要,掩码摘要则用于进一步处理摘要值,增加加密的安全性。输入数据长度要小于RSA钥模 - md摘要长度 - mgf1_md摘要长度 - 2。
       - OnlySign模式:对待签名数据有长度要求,根据填充模式和是否设置摘要算法而定。例如,PKCS1填充模式且不设置摘要算法时,数据需小于RSA密钥长度 - 11;设置摘要算法时,待签名数据必须是对应的摘要数据。
       - Recover模式:用于签名恢复原始数据,需要与签名时的参数保持一致,特别是填充模式和摘要算法。
  5. ECDSA算法
       - ECDSA算法在签名验签时,只需设置摘要算法(如SHA256)。摘要算法用于计算数据的摘要,签名和验签过程基于椭圆曲线密码学利用这个摘要值进行操作。

    (二)示例代码展示与结果影响

    以下是RSA算法在不同模式下的参数设置示例:

  6. PKCS1填充模式下的签名验签示例

    import { cryptoFramework } from '@kit.CryptoArchitectureKit';
    import { buffer } from '@kit.ArkTS';
    // 生成RSA密钥对
    async function generateRSAKeyPair() {
        let keyGenAlg = "RSA1024";
        let generator = cryptoFramework.createAsyKeyGenerator(keyGenAlg);
        return await generator.generateKeyPair();
    }
    // 使用PKCS1填充模式进行签名
    async function signMessagePKCS1(priKey, plainText) {
        let signAlg = "RSA1024|PKCS1|SHA256";
        let signer = cryptoFramework.createSign(signAlg);
        await signer.init(priKey);
        await signer.update(plainText);
        return await signer.sign(null);
    }
    // 使用PKCS1填充模式进行验签
    async function verifyMessagePKCS1(pubKey, cipherText, signData) {
        let verifyAlg = "RSA1024|PKCS1|SHA256";
        let verifier = cryptoFramework.createVerify(verifyAlg);
        await verifier.init(pubKey);
        await verifier.update(cipherText);
        return await verifier.verify(signData);
    }
    async function main() {
        try {
            // 生成密钥对
            let keyPair = await generateRSAKeyPair();
            // 要签名的消息
            let message = "This is a test";
            let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(message, 'utf-8').buffer) };
            // 签名
            let signData = await signMessagePKCS1(keyPair.priKey, plainText);
            // 验签
            let result = await verifyMessagePKCS1(keyPair.pubKey, plainText, signData);
            if (result) {
                console.info('verify success');
            } else {
                console.error('verify failed');
            }
        } catch (error) {
            console.error(`RSA PKCS1 “${error}“, error code: ${error.code}`);
        }
    }
    main();

    在这个示例中,我们设置了RSA1024密钥、PKCS1填充模式和SHA256摘要算法进行签名验签。如果摘要算法选择不当(如选择了不适合该密钥长度的摘要算法),可能导致签名验签失败。

  7. PKCS1_OAEP填充模式下的加密解密示例

    import { cryptoFramework } from '@kit.CryptoArchitectureKit';
    import { buffer } from '@kit.ArkTS';
    // 生成RSA密钥对
    async function generateRSAKeyPair() {
        let keyGenAlg = "RSA2048";
        let generator = cryptoFramework.createAsyKeyGenerator(keyGenAlg);
        return await generator.generateKeyPair();
    }
    // 使用PKCS1_OAEP填充模式进行加密
    async function encryptMessagePKCS1_OAEP(pubKey, plainText) {
        let cipher = cryptoFramework.createCipher('RSA2048|PKCS1_OAEP|SHA256|MGF1_SHA256');
        await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, pubKey, null);
        return await cipher.doFinal(plainText);
    }
    // 使用PKCS1_OAEP填充模式进行解密
    async function decryptMessagePKCS1_OAEP(priKey, cipherText) {
        let decoder = cryptoFramework.createCipher('RSA2048|PKCS1_OAEP|SHA256|MGF1_SHA256');
        await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, priKey, null);
        return await decoder.doFinal(cipherText);
    }
    async function main() {
        try {
            // 生成密钥对
            let keyPair = await generateRSAKeyPair();
            // 要加密的消息
            let message = "This is a test";
            let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(message, 'utf-8').buffer) };
            // 加密
            let encryptText = await encryptMessagePKCS1_OAEP(keyPair.pubKey, plainText);
            // 解密
            let decryptText = await decryptMessagePKCS1_OAEP(keyPair.priKey, encryptText);
            if (plainText.data.toString() === decryptText.data.toString()) {
                console.info('decrypt ok');
                console.info('decrypt plainText: ' + buffer.from(decryptText.data).toString('utf-8'));
            } else {
                console.error('decrypt failed');
            }
            } catch (error) {
                console.error(`RSA PKCS1_OAEP “${error}“, error code: ${error.code}`);
            }
    }
    main();

    在这个示例中,我们使用了RSA2048密钥、PKCS1_OAEP填充模式、SHA256摘要算法和MGF1_SHA256掩码摘要进行加密解密。如果参数设置错误,例如摘要算法或掩码摘要不匹配,加密解密过程将无法正确进行。

  8. ECDSA算法签名验签示例

    import { cryptoFramework } from '@kit.CryptoArchitectureKit';
    import { buffer } from '@kit.ArkTS';
    // 生成ECDSA密钥对(以ECC256为例)
    async function generateECDSAKeyPair() {
        let keyGenAlg = "ECC256";
        let generator = cryptoFramework.createAsyKeyGenerator(keyGenAlg);
        return await generator.generateKeyPair();
    }
    // 使用ECDSA算法进行签名
    async function signMessageECDSA(priKey, plainText) {
        let signAlg = "ECC256|SHA256";
        let signer = cryptoFramework.createSign(signAlg);
        await signer.init(priKey);
        await signer.update(plainText);
        return await signer.sign(null);
    }
    // 使用ECDSA算法进行验签
    async function verifyMessageECDSA(pubKey, cipherText, signData) {
        let verifyAlg = "ECC256|SHA256";
        let verifier = cryptoFramework.createVerify(verifyAlg);
        await verifier.init(pubKey);
        await verifier.update(cipherText);
        return await verifier.verify(signData);
    }
    async function main() {
        try {
            // 生成密钥对
            let keyPair = await generateECDSAKeyPair();
            // 要签名的消息
            let message = "This is a test";
            let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(message, 'utf-8').buffer) };
            // 签名
            let signData = await signMessageECDSA(keyPair.priKey, plainText);
            // 验签
            let result = await verifyMessageECDSA(keyPair.pubKey, plainText, signData);
            if (result) {
                console.info('verify success');
            } else {
                console.error('verify failed');
            }
        } catch (error) {
            console.error(`ECDSA “${error}“, error code: ${error.code}`);
        }
    }
    main();

    在ECDSA算法示例中,正确设置摘要算法(如SHA256)对于签名验签的准确性至关重要。如果摘要算法选择错误,将导致验签失败,无法保证数据的完整性和来源可靠性。
    在HarmonyOS Next的加解密算法中,正确理解和选择参数与模式是确保数据安全和加解密操作成功的关键。不同的参数和模式组合适用于不同的场景,开发者需要根据实际需求进行合理选择,并严格按照要求设置参数,以保障系统的安全性和稳定性。


SameX
1 声望1 粉丝