HarmonyOS Next加解密演算法中的引數與模式詳解

SameX發表於2024-11-21

本文旨在深入探討華為鴻蒙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演算法的各種模式也用於不同場景,如在國內的一些安全要求較高的領域(政務、金融等)中,用於加密儲存和傳輸敏感資料。

(二)引數要求對比與示例程式碼

以下是不同對稱金鑰演算法在不同模式下的引數要求對比表格(部分示例):

演算法 加密模式 填充模式 引數要求
AES ECB [NoPadding, PKCS5, PKCS7] 金鑰長度可選128/192/256位
AES CBC [NoPadding, PKCS5, PKCS7] 需要初始化向量(IV),金鑰長度可選128/192/256位
AES GCM [NoPadding, PKCS5, PKCS7] 需要IV和附加驗證資料(aad),金鑰長度可選128/192/256位
3DES ECB [NoPadding, PKCS5, PKCS7] 金鑰長度固定為192位
3DES CBC [NoPadding, PKCS5, PKCS7] 需要IV,金鑰長度固定為192位
SM4 ECB [NoPadding, PKCS5, PKCS7] 金鑰長度固定為128位
SM4 CBC [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等引數,實現了資料的加密,並獲取了認證資訊。引數的選擇直接影響了加密的結果和安全性,如果引數設定錯誤,可能導致加密失敗或安全性降低。

三、非對稱金鑰引數與模式

(一)不同模式下的引數設定及意義

  1. RSA演算法
    - PKCS1填充模式:在簽名驗籤時,需要設定摘要演算法(如SHA256)。摘要演算法用於對要簽名的資料進行摘要計算,生成固定長度的特徵值。這個特徵值會被用於簽名過程,確保資料的完整性。在加密時,輸入資料長度需滿足一定要求(小於RSA鑰模 - 11)。
    - PKCS1_OAEP填充模式:需要設定摘要演算法(如SHA256)和掩碼摘要(如MGF1_SHA256)。摘要演算法用於生成資料的摘要,掩碼摘要則用於進一步處理摘要值,增加加密的安全性。輸入資料長度要小於RSA鑰模 - md摘要長度 - mgf1_md摘要長度 - 2。
    - OnlySign模式:對待簽名資料有長度要求,根據填充模式和是否設定摘要演算法而定。例如,PKCS1填充模式且不設定摘要演算法時,資料需小於RSA金鑰長度 - 11;設定摘要演算法時,待簽名資料必須是對應的摘要資料。
    - Recover模式:用於簽名恢復原始資料,需要與簽名時的引數保持一致,特別是填充模式和摘要演算法。
  2. ECDSA演算法
    - ECDSA演算法在簽名驗籤時,只需設定摘要演算法(如SHA256)。摘要演算法用於計算資料的摘要,簽名和驗簽過程基於橢圓曲線密碼學利用這個摘要值進行操作。

(二)示例程式碼展示與結果影響

以下是RSA演算法在不同模式下的引數設定示例:

  1. 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摘要演算法進行簽名驗籤。如果摘要演算法選擇不當(如選擇了不適合該金鑰長度的摘要演算法),可能導致簽名驗籤失敗。
2. 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掩碼摘要進行加密解密。如果引數設定錯誤,例如摘要演算法或掩碼摘要不匹配,加密解密過程將無法正確進行。
3. 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的加解密演算法中,正確理解和選擇引數與模式是確保資料安全和加解密操作成功的關鍵。不同的引數和模式組合適用於不同的場景,開發者需要根據實際需求進行合理選擇,並嚴格按照要求設定引數,以保障系統的安全性和穩定性。

相關文章