使用ECIES混合加解密(C/C++)
===================

从API版本26.0.0开始，支持ECIES算法，ECIES是一种基于椭圆曲线密码学的加密算法。

**约束条件：**

*   密钥协商算法支持ECC256、ECC384、ECC521。
*   密钥派生算法仅支持X963KDF，摘要算法支持SHA1、SHA256、SHA384、SHA512。
*   对称加密算法支持AES128、AES192、AES256。
*   分组模式仅支持GCM。

#### 在CMake脚本中链接相关动态库

target\_link\_libraries(entry PUBLIC libohcrypto.so)

#### 开发步骤

#### \[h2\]加密

1.  调用[OH\_CryptoAsymKeyGenerator\_Create](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-asym-key-h#oh_cryptoasymkeygenerator_create)和[OH\_CryptoAsymKeyGenerator\_Generate](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-asym-key-h#oh_cryptoasymkeygenerator_generate)，使用ECC算法生成密钥对。
    
2.  调用[OH\_CryptoKeyAgreement\_Create](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-key-agreement-h#oh_cryptokeyagreement_create)和[OH\_CryptoKeyAgreement\_GenerateSecret](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-key-agreement-h#oh_cryptokeyagreement_generatesecret)，基于本端的私钥（KeyPair.priKey）与对端的公钥（KeyPair.pubKey）进行密钥协商，返回共享密钥。
    
3.  X963KDF密钥派生，调用[OH\_CryptoKdf\_Create](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-kdf-h#oh_cryptokdf_create)和[OH\_CryptoKdf\_Derive](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-kdf-h#oh_cryptokdf_derive)，基于协商的共享密钥（Secret）进行密钥派生，返回派生后的密钥。
    
4.  调用[OH\_CryptoSymCipher\_Create](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-sym-cipher-h#oh_cryptosymcipher_create)，指定字符串参数'AES128|GCM'，创建对称密钥类型为AES128、分组模式为GCM的Cipher实例，用于完成加密操作。
    
5.  调用[OH\_CryptoSymCipher\_Init](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-sym-cipher-h#oh_cryptosymcipher_init)，设置模式为加密（CRYPTO\_ENCRYPT\_MODE），指定加密密钥（OH\_CryptoSymKey），初始化加密Cipher实例。
    
6.  调用[OH\_CryptoSymCipher\_Update](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-sym-cipher-h#oh_cryptosymcipher_update)，更新数据（明文），获取加密后的数据。
    
7.  调用[OH\_CryptoSymCipher\_Final](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-sym-cipher-h#oh_cryptosymcipher_final)，获取authTag。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/5/v3/eBk2UL6UR1yi_xUGGFn_Vw/caution_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073402Z&HW-CC-Expire=86400&HW-CC-Sign=54E976A49CC4EDB2D075C48261C8B357B3BC774E59E1B49B28F497B8B0B9F6DC)
    
    在GCM模式下，final会返回authTag，作为解密操作时初始化的认证信息，需要手动保存。
    
    在GCM模式下，算法库当前只支持16字节的authTag，作为解密操作时初始化的认证信息。
    

#### \[h2\]解密

1.  调用[OH\_CryptoAsymKeyGenerator\_Create](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-asym-key-h#oh_cryptoasymkeygenerator_create)和[OH\_CryptoAsymKeyGenerator\_Generate](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-asym-key-h#oh_cryptoasymkeygenerator_generate)，使用ECC算法生成密钥对。
    
2.  调用[OH\_CryptoKeyAgreement\_Create](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-key-agreement-h#oh_cryptokeyagreement_create)和[OH\_CryptoKeyAgreement\_GenerateSecret](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-key-agreement-h#oh_cryptokeyagreement_generatesecret)，基于本端的私钥（KeyPair.priKey）与对端的公钥（KeyPair.pubKey）进行密钥协商，返回共享密钥。
    
3.  X963KDF密钥派生，调用[OH\_CryptoKdf\_Create](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-kdf-h#oh_cryptokdf_create)和[OH\_CryptoKdf\_Derive](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-kdf-h#oh_cryptokdf_derive)，基于协商的共享密钥（Secret）进行密钥派生，返回派生后的密钥。
    
4.  调用[OH\_CryptoSymCipher\_Create](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-sym-cipher-h#oh_cryptosymcipher_create)，指定字符串参数'AES128|GCM'，创建对称密钥类型为AES128、分组模式为GCM的Cipher实例，用于完成解密操作。
    
5.  调用[OH\_CryptoSymCipher\_Init](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-sym-cipher-h#oh_cryptosymcipher_init)，设置模式为解密（CRYPTO\_DECRYPT\_MODE），指定解密密钥（OH\_CryptoSymKey），初始化解密Cipher实例。
    
6.  调用[OH\_CryptoSymCipher\_Update](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-sym-cipher-h#oh_cryptosymcipher_update)，更新数据（密文）。
    
7.  调用[OH\_CryptoSymCipher\_Final](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-crypto-sym-cipher-h#oh_cryptosymcipher_final)，获取解密数据。
    

#### \[h2\]示例代码

#include <cstring>
#include <hilog/log.h>
#include "CryptoArchitectureKit/crypto\_architecture\_kit.h"
#include "native\_test.h"

namespace ECIES {
uint8\_t aad\[\] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
uint8\_t tag\[\] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
const uint8\_t ivSize = 16;
const uint8\_t keySize = 16;

struct CryptoContext {
    OH\_CryptoKeyPair \*keyPair;
    OH\_CryptoSymKey \*symKey;
    Crypto\_DataBlob secret;
    Crypto\_DataBlob ivData; // secret的前16字节作为IV，浅拷贝
};

OH\_Crypto\_ErrCode GenerateKeyPair(OH\_CryptoKeyPair \*\*keyPair)
{
    OH\_CryptoAsymKeyGenerator \*asyKeyGenerator = nullptr;
    OH\_Crypto\_ErrCode ret = OH\_CryptoAsymKeyGenerator\_Create("ECC256", &asyKeyGenerator);
    if (ret != CRYPTO\_SUCCESS) {
        return ret;
    }
    ret = OH\_CryptoAsymKeyGenerator\_Generate(asyKeyGenerator, keyPair);
    OH\_CryptoAsymKeyGenerator\_Destroy(asyKeyGenerator);
    return ret;
}

OH\_Crypto\_ErrCode GenerateSecret(OH\_CryptoPrivKey \*privKey, OH\_CryptoPubKey \*pubKey, Crypto\_DataBlob \*secret)
{
    // EC密钥协商
    OH\_CryptoKeyAgreement \*keyAgreement = nullptr;
    OH\_Crypto\_ErrCode ret = OH\_CryptoKeyAgreement\_Create("ECC256", &keyAgreement);
    if (ret != CRYPTO\_SUCCESS) {
        return ret;
    }
    ret = OH\_CryptoKeyAgreement\_GenerateSecret(keyAgreement, privKey, pubKey, secret);
    OH\_CryptoKeyAgreement\_Destroy(keyAgreement);
    return ret;
}

OH\_Crypto\_ErrCode KdfDeriveSecret(OH\_CryptoPrivKey \*privKey, OH\_CryptoPubKey \*pubKey, Crypto\_DataBlob \*secret)
{
    // 使用X963KDF进行密钥派生
    OH\_CryptoKdfParams \*params = nullptr;
    OH\_Crypto\_ErrCode ret = OH\_CryptoKdfParams\_Create("X963KDF", &params);
    if (ret != CRYPTO\_SUCCESS) {
        return ret;
    }
    Crypto\_DataBlob keyData = {};
    ret = GenerateSecret(privKey, pubKey, &keyData);
    if (ret != CRYPTO\_SUCCESS) {
        OH\_CryptoKdfParams\_Destroy(params);
        return ret;
    }
    ret = OH\_CryptoKdfParams\_SetParam(params, CRYPTO\_KDF\_KEY\_DATABLOB, &keyData);
    OH\_Crypto\_FreeDataBlob(&keyData);
    if (ret != CRYPTO\_SUCCESS) {
        OH\_CryptoKdfParams\_Destroy(params);
        return ret;
    }
    const char \*info = "infostring";
    Crypto\_DataBlob infoData = { .data = (uint8\_t \*)info, .len = strlen(info) };
    ret = OH\_CryptoKdfParams\_SetParam(params, CRYPTO\_KDF\_INFO\_DATABLOB, &infoData);
    if (ret != CRYPTO\_SUCCESS) {
        OH\_CryptoKdfParams\_Destroy(params);
        return ret;
    }
    OH\_CryptoKdf \*x963Kdf = nullptr;
    ret = OH\_CryptoKdf\_Create("X963KDF|SHA256", &x963Kdf);
    if (ret != CRYPTO\_SUCCESS) {
        OH\_CryptoKdfParams\_Destroy(params);
        return ret;
    }
    ret = OH\_CryptoKdf\_Derive(x963Kdf, params, ivSize + keySize, secret);
    OH\_CryptoKdf\_Destroy(x963Kdf);
    OH\_CryptoKdfParams\_Destroy(params);
    return ret;
}

OH\_Crypto\_ErrCode GenerateSymKey(const Crypto\_DataBlob \*secret, OH\_CryptoSymKey \*\*symKey)
{
    OH\_CryptoSymKeyGenerator \*symKeyGenerator = nullptr;
    OH\_Crypto\_ErrCode ret = OH\_CryptoSymKeyGenerator\_Create("AES128", &symKeyGenerator);
    if (ret != CRYPTO\_SUCCESS) {
        return ret;
    }
    Crypto\_DataBlob keyData = { .data = secret->data + ivSize, .len = keySize };
    ret = OH\_CryptoSymKeyGenerator\_Convert(symKeyGenerator, &keyData, symKey);
    OH\_CryptoSymKeyGenerator\_Destroy(symKeyGenerator);
    return ret;
}

void SetCipherIvData(CryptoContext \*ctx, Crypto\_DataBlob \*secret)
{
    ctx->ivData.data = secret->data;
    ctx->ivData.len = ivSize;
}

OH\_Crypto\_ErrCode SetCipherParams(OH\_CryptoSymCipherParams \*params, Crypto\_DataBlob \*ivBlob, Crypto\_DataBlob \*tagBlob)
{
    Crypto\_DataBlob aadBlob = { .data = aad, .len = sizeof(aad) };
    OH\_Crypto\_ErrCode ret = OH\_CryptoSymCipherParams\_SetParam(params, CRYPTO\_AAD\_DATABLOB, &aadBlob);
    if (ret != CRYPTO\_SUCCESS) {
        return ret;
    }
    ret = OH\_CryptoSymCipherParams\_SetParam(params, CRYPTO\_IV\_DATABLOB, ivBlob);
    if (ret != CRYPTO\_SUCCESS) {
        return ret;
    }
    ret = OH\_CryptoSymCipherParams\_SetParam(params, CRYPTO\_TAG\_DATABLOB, tagBlob);
    return ret;
}

OH\_Crypto\_ErrCode Encrypt(CryptoContext \*ctx, Crypto\_DataBlob \*plainText, Crypto\_DataBlob \*cipherText,
    Crypto\_DataBlob \*tagBlob)
{
    // AES-GCM对称加密
    OH\_CryptoSymCipherParams \*params = nullptr;
    OH\_Crypto\_ErrCode ret = OH\_CryptoSymCipherParams\_Create(&params);
    if (ret != CRYPTO\_SUCCESS) {
        return ret;
    }
    Crypto\_DataBlob tagInit = { .data = tag, .len = sizeof(tag) };
    ret = SetCipherParams(params, &ctx->ivData, &tagInit);
    if (ret != CRYPTO\_SUCCESS) {
        OH\_CryptoSymCipherParams\_Destroy(params);
        return ret;
    }
    OH\_CryptoSymCipher \*encCtx = nullptr;
    ret = OH\_CryptoSymCipher\_Create("AES128|GCM", &encCtx);
    if (ret != CRYPTO\_SUCCESS) {
        OH\_CryptoSymCipherParams\_Destroy(params);
        return ret;
    }
    ret = OH\_CryptoSymCipher\_Init(encCtx, CRYPTO\_ENCRYPT\_MODE, ctx->symKey, params);
    OH\_CryptoSymCipherParams\_Destroy(params);
    if (ret != CRYPTO\_SUCCESS) {
        OH\_CryptoSymCipher\_Destroy(encCtx);
        return ret;
    }
    ret = OH\_CryptoSymCipher\_Update(encCtx, plainText, cipherText);
    if (ret != CRYPTO\_SUCCESS) {
        OH\_CryptoSymCipher\_Destroy(encCtx);
        return ret;
    }
    ret = OH\_CryptoSymCipher\_Final(encCtx, nullptr, tagBlob);
    OH\_CryptoSymCipher\_Destroy(encCtx);
    return ret;
}

OH\_Crypto\_ErrCode Decrypt(CryptoContext \*ctx, Crypto\_DataBlob \*cipherText, Crypto\_DataBlob \*plainText,
    Crypto\_DataBlob \*tagBlob)
{
    // AES-GCM对称解密
    OH\_CryptoSymCipherParams \*params = nullptr;
    OH\_Crypto\_ErrCode ret = OH\_CryptoSymCipherParams\_Create(&params);
    if (ret != CRYPTO\_SUCCESS) {
        return ret;
    }
    ret = SetCipherParams(params, &ctx->ivData, tagBlob);
    if (ret != CRYPTO\_SUCCESS) {
        OH\_CryptoSymCipherParams\_Destroy(params);
        return ret;
    }
    OH\_CryptoSymCipher \*decCtx = nullptr;
    ret = OH\_CryptoSymCipher\_Create("AES128|GCM", &decCtx);
    if (ret != CRYPTO\_SUCCESS) {
        OH\_CryptoSymCipherParams\_Destroy(params);
        return ret;
    }
    ret = OH\_CryptoSymCipher\_Init(decCtx, CRYPTO\_DECRYPT\_MODE, ctx->symKey, params);
    if (ret != CRYPTO\_SUCCESS) {
        OH\_CryptoSymCipher\_Destroy(decCtx);
        OH\_CryptoSymCipherParams\_Destroy(params);
        return ret;
    }
    ret = OH\_CryptoSymCipher\_Final(decCtx, cipherText, plainText);
    OH\_CryptoSymCipher\_Destroy(decCtx);
    OH\_CryptoSymCipherParams\_Destroy(params);
    return ret;
}

void CleanupDataBlob(Crypto\_DataBlob \*plainData, Crypto\_DataBlob \*cipherData, Crypto\_DataBlob \*tagBlob)
{
    OH\_Crypto\_FreeDataBlob(plainData);
    OH\_Crypto\_FreeDataBlob(cipherData);
    OH\_Crypto\_FreeDataBlob(tagBlob);
}

void CleanupContext(CryptoContext \*ctx)
{
    OH\_Crypto\_FreeDataBlob(&ctx->secret);
    OH\_CryptoSymKey\_Destroy(ctx->symKey);
    OH\_CryptoKeyPair\_Destroy(ctx->keyPair);
}
} // namespace ECIES

OH\_Crypto\_ErrCode doNativeTest()
{
    ECIES::CryptoContext ctxA = {};
    ECIES::CryptoContext ctxB = {};
    Crypto\_DataBlob plainData = {};
    Crypto\_DataBlob cipherData = {};
    Crypto\_DataBlob tagBlob = {};
    OH\_Crypto\_ErrCode ret = CRYPTO\_INVALID\_PARAMS;
    do {
        ret = ECIES::GenerateKeyPair(&ctxA.keyPair);
        if (ret != CRYPTO\_SUCCESS) {
            break;
        }
        ret = ECIES::GenerateKeyPair(&ctxB.keyPair);
        if (ret != CRYPTO\_SUCCESS) {
            break;
        }

        // A端密钥协商：A端的私钥 + B端的公钥
        ret = ECIES::KdfDeriveSecret(OH\_CryptoKeyPair\_GetPrivKey(ctxA.keyPair),
            OH\_CryptoKeyPair\_GetPubKey(ctxB.keyPair), &ctxA.secret);
        if (ret != CRYPTO\_SUCCESS) {
            break;
        }
        ret = ECIES::GenerateSymKey(&ctxA.secret, &ctxA.symKey);
        if (ret != CRYPTO\_SUCCESS) {
            break;
        }
        // 使用A端派生密钥的前16字节作为加解密端的IV
        ECIES::SetCipherIvData(&ctxA, &ctxA.secret);
        ECIES::SetCipherIvData(&ctxB, &ctxA.secret);
        // A端加密
        uint8\_t message\[\] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B };
        Crypto\_DataBlob plainText = { .data = message, .len = sizeof(message) };
        ret = ECIES::Encrypt(&ctxA, &plainText, &cipherData, &tagBlob);
        if (ret != CRYPTO\_SUCCESS) {
            break;
        }

        // B端密钥协商：B端的私钥 + A端的公钥
        ret = ECIES::KdfDeriveSecret(OH\_CryptoKeyPair\_GetPrivKey(ctxB.keyPair),
            OH\_CryptoKeyPair\_GetPubKey(ctxA.keyPair), &ctxB.secret);
        if (ret != CRYPTO\_SUCCESS) {
            break;
        }
        ret = ECIES::GenerateSymKey(&ctxB.secret, &ctxB.symKey);
        if (ret != CRYPTO\_SUCCESS) {
            break;
        }
        // B端解密
        ret = ECIES::Decrypt(&ctxB, &cipherData, &plainData, &tagBlob);
    } while (0);

    ECIES::CleanupDataBlob(&plainData, &cipherData, &tagBlob);
    ECIES::CleanupContext(&ctxA);
    ECIES::CleanupContext(&ctxB);
    OH\_LOG\_INFO(LOG\_APP, "doNativeTest result: %{public}s", (ret == 0 ? "Success" : "Failed"));
    return ret;
}