数据库加密 (C/C++)
=============

#### 场景介绍

为了增强数据库的安全性，数据库提供了安全的加密功能，以有效保护存储的内容。

通过数据库加密，实现了数据库数据存储的保密性和完整性要求，使得数据库以密文方式存储并在密态方式下工作，确保了数据安全。

加密后的数据库只能通过接口进行访问，无法通过其它方式打开数据库文件。数据库的加密属性在创建数据库时确认，无法变更；关系型数据库从API version 22开始，支持通过[OH\_Rdb\_RekeyEx](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-relational-store-h#oh_rdb_rekeyex)接口调整其加密属性。

当前仅支持使用关系型数据库（C/C++）进行数据库加密。

#### 开发步骤

关系型数据库通过调用OH\_Rdb\_SetEncrypted方法来设置是否加密。isEncrypted参数为true时表示加密，为false时表示不加密，默认不加密。

当isEncrypted为true时，可调用OH\_Rdb\_SetCryptoParam方法设置自定义的加密/解密密钥和算法等参数。

1.  CMakeLists.txt中添加以下lib。
    
    libnative\_rdb\_ndk.z.so
    
2.  导入头文件。
    
    #include <cstring>
    #include "database/rdb/relational\_store.h"
    #include "hilog/log.h"
    
3.  针对是否配置自定义加密/解密参数，有如下两种场景：
    
    *   场景1：不配置自定义加密/解密参数，此时会使用默认的配置进行数据库的加密/解密。
        
        OH\_Rdb\_ConfigV2 \*config = OH\_Rdb\_CreateConfig();
        OH\_Rdb\_SetDatabaseDir(config, "/data/storage/el2/database");
        OH\_Rdb\_SetArea(config, RDB\_SECURITY\_AREA\_EL2);
        OH\_Rdb\_SetBundleName(config, "com.example.nativedemo");
        OH\_Rdb\_SetStoreName(config, "RdbTest.db");
        OH\_Rdb\_SetSecurityLevel(config, OH\_Rdb\_SecurityLevel::S3);
        // 设置为使用加密方式创建或打开数据库
        OH\_Rdb\_SetEncrypted(config, true);
        int errCode = 0;
        // 获取OH\_Rdb\_Store实例
        OH\_Rdb\_Store \*store = OH\_Rdb\_CreateOrOpen(config, &errCode);
        OH\_Rdb\_CloseStore(store);
        store = nullptr;
        OH\_Rdb\_DestroyConfig(config);
        config = nullptr;
        
    *   场景2：使用OH\_Rdb\_SetCryptoParam接口配置加密参数，此时会使用开发者自定义的密钥和算法参数进行数据库的加密/解密。
        
        如果开发者不关心加密算法及参数，使用默认加密配置即可，无需创建和配置自定义加密参数。
        
        OH\_Rdb\_ConfigV2 \*config = OH\_Rdb\_CreateConfig();
        OH\_Rdb\_SetDatabaseDir(config, "/data/storage/el2/database");
        OH\_Rdb\_SetArea(config, RDB\_SECURITY\_AREA\_EL2);
        OH\_Rdb\_SetStoreName(config, "RdbTestConfigEncryptParam.db");
        OH\_Rdb\_SetSecurityLevel(config, OH\_Rdb\_SecurityLevel::S3);
        OH\_Rdb\_SetBundleName(config, "com.example.nativedemo");
        // 设置为使用加密方式创建或打开数据库
        OH\_Rdb\_SetEncrypted(config, true);
        // 创建自定义加密参数对象
        OH\_Rdb\_CryptoParam \*cryptoParam = OH\_Rdb\_CreateCryptoParam();
            
        // 示例中使用硬编码密钥仅用于演示目的， 实际应用中应使用安全的密钥管理服务
        uint8\_t key\[6\] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36};
        // 使用指定的密钥打开加密数据库。不指定则由数据库负责生成并保存密钥，并使用生成的密钥。
        const int32\_t length = 6;
        OH\_Crypto\_SetEncryptionKey(cryptoParam, key, length);
        // 秘钥信息使用完之后要清空
        for (size\_t i = 0; i < sizeof(key); i++) {
            key\[i\] = 0;
        }
        // 设置KDF算法迭代次数。迭代次数必须大于零。不指定或等于零则使用默认值10000和默认加密算法。
        const int64\_t iteration = 64000;
        OH\_Crypto\_SetIteration(cryptoParam, iteration);
        // 设置加密算法，如不设置默认为AES\_256\_GCM
        OH\_Crypto\_SetEncryptionAlgo(cryptoParam, Rdb\_EncryptionAlgo::RDB\_AES\_256\_CBC);
        // 设置HMAC算法，如不设置默认为SHA256
        OH\_Crypto\_SetHmacAlgo(cryptoParam, RDB\_HMAC\_SHA512);
        // 设置KDF算法，如不设置默认为SHA256
        OH\_Crypto\_SetKdfAlgo(cryptoParam, RDB\_KDF\_SHA512);
        // 设置打开加密数据库时使用的页大小，须为1024到65536之间的整数且为2的幂，如不设置默认为1024
        const int64\_t pageSize = 4096;
        OH\_Crypto\_SetCryptoPageSize(cryptoParam, pageSize);
        // 设置自定义加密参数
        OH\_Rdb\_SetCryptoParam(config, cryptoParam);
            
        int errCode = 0;
        OH\_Rdb\_Store \*store = OH\_Rdb\_CreateOrOpen(config, &errCode);
        // 销毁自定义加密参数对象
        OH\_Rdb\_DestroyCryptoParam(cryptoParam);
        cryptoParam = nullptr;
        OH\_Rdb\_CloseStore(store);
        store = nullptr;
        OH\_Rdb\_DestroyConfig(config);
        config = nullptr;
        
4.  从API version 22开始，支持更换数据库密钥和加密参数，如果开发者需要更换已创建的加密数据库的密钥或者加密参数，可以使用OH\_Rdb\_RekeyEx进行更换。
    
    针对更换数据库密钥和加密参数，有如下场景：
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/cb/v3/Jsp9MML0QNi21wH7dX9sEQ/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073243Z&HW-CC-Expire=86400&HW-CC-Sign=E224F13D278F6F83A4F41D218D6FC13E50111B7973199999BF0DF178FCE817A7)
    
    加密参数变更需谨慎，在完成OH\_Rdb\_RekeyEx操作后，必须使用新的参数来打开数据库，否则可能会导致开库失败。如果rekey过程因设备断电等原因中断，操作可能成功也可能失败。因此，建议业务方做好兜底保障（使用OH\_Rdb\_RekeyEx前后的参数进行冗余重试），确保不会错误地判断数据库的状态，从而避免出现数据库无法打开的问题。
    
    *   场景1：原数据库为默认参数加密数据库，更换密钥和加密参数。
        
        OH\_Rdb\_ConfigV2 \*config = OH\_Rdb\_CreateConfig();
        OH\_Rdb\_SetDatabaseDir(config, "/data/storage/el3/database");
        OH\_Rdb\_SetArea(config, RDB\_SECURITY\_AREA\_EL3);
        OH\_Rdb\_SetStoreName(config, "RdbTest.db");
        OH\_Rdb\_SetBundleName(config, "com.example.nativedemo");
        OH\_Rdb\_SetModuleName(config, "entry");
        OH\_Rdb\_SetSecurityLevel(config, OH\_Rdb\_SecurityLevel::S3);
        OH\_Rdb\_SetEncrypted(config, true);
        int errCode = 0;
        OH\_Rdb\_Store \*store = OH\_Rdb\_CreateOrOpen(config, &errCode);
        if (store == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create store failed, errCode: %{public}d", errCode);
            OH\_Rdb\_DestroyConfig(config);
            config = NULL;
            return;
        }
        OH\_Rdb\_CryptoParam \*crypto = OH\_Rdb\_CreateCryptoParam();
        if (crypto == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create crypto failed.");
            OH\_Rdb\_DestroyConfig(config);
            OH\_Rdb\_CloseStore(store);
            config = NULL;
            store = NULL;
            return;
        }
        OH\_Crypto\_SetEncryptionAlgo(crypto, RDB\_AES\_256\_CBC);
        OH\_Crypto\_SetHmacAlgo(crypto, RDB\_HMAC\_SHA512);
        OH\_Crypto\_SetKdfAlgo(crypto, RDB\_KDF\_SHA512);
        OH\_Crypto\_SetCryptoPageSize(crypto, 2048);
        errCode = OH\_Rdb\_RekeyEx(store, crypto);
        
        if (errCode != 0) {
            OH\_LOG\_ERROR(LOG\_APP, "RekeyEx failed, errCode: %{public}d", errCode);
        }
        // 在完成OH\_Rdb\_RekeyEx操作后，如果后续需要重新开库时必须使用新的参数来打开数据库
        OH\_Rdb\_DestroyConfig(config);
        OH\_Rdb\_CloseStore(store);
        OH\_Rdb\_DestroyCryptoParam(crypto);
        config = NULL;
        store = NULL;
        crypto = NULL;
        
    *   场景2：原数据库为自定义参数加密数据库，更换自定义密钥和加密参数。
        
        OH\_Rdb\_ConfigV2 \*config = OH\_Rdb\_CreateConfig();
        OH\_Rdb\_SetDatabaseDir(config, "/data/storage/el3/database");
        OH\_Rdb\_SetArea(config, RDB\_SECURITY\_AREA\_EL3);
        OH\_Rdb\_SetStoreName(config, "RdbTest.db");
        OH\_Rdb\_SetBundleName(config, "com.example.nativedemo");
        OH\_Rdb\_SetModuleName(config, "entry");
        OH\_Rdb\_SetSecurityLevel(config, OH\_Rdb\_SecurityLevel::S3);
        OH\_Rdb\_SetEncrypted(config, true);
        OH\_Rdb\_CryptoParam \*crypto = OH\_Rdb\_CreateCryptoParam();
        if (crypto == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create crypto failed.");
            OH\_Rdb\_DestroyConfig(config);
            config = NULL;
            return;
        }
        uint8\_t encryptionKey\[\] = "12345678";
        OH\_Crypto\_SetEncryptionKey(crypto, encryptionKey, sizeof(encryptionKey));
        memset(encryptionKey, 0, sizeof(encryptionKey));
        OH\_Rdb\_SetCryptoParam(config, crypto);
        
        int errCode = 0;
        OH\_Rdb\_Store \*store = OH\_Rdb\_CreateOrOpen(config, &errCode);
        if (store == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create store failed, errCode: %{public}d", errCode);
            OH\_Rdb\_DestroyConfig(config);
            config = NULL;
            return;
        }
        OH\_Rdb\_CryptoParam \*newCryptoParam = OH\_Rdb\_CreateCryptoParam();
        if (newCryptoParam == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create newCryptoParam failed.");
            OH\_Rdb\_DestroyConfig(config);
            OH\_Rdb\_CloseStore(store);
            OH\_Rdb\_DestroyCryptoParam(crypto);
            config = NULL;
            store = NULL;
            crypto = NULL;
            return;
        }
        // 注意：示例中使用硬编码密钥仅用于演示目的，实际应用中应使用安全的密钥管理服务，使用后应该及时清零
        uint8\_t key\[\] = "87654321";
        OH\_Crypto\_SetEncryptionKey(newCryptoParam, key, sizeof(key));
        memset(key, 0, sizeof(key));
        OH\_Crypto\_SetEncryptionAlgo(newCryptoParam, RDB\_AES\_256\_CBC);
        OH\_Crypto\_SetHmacAlgo(newCryptoParam, RDB\_HMAC\_SHA512);
        OH\_Crypto\_SetKdfAlgo(newCryptoParam, RDB\_KDF\_SHA512);
        OH\_Crypto\_SetCryptoPageSize(newCryptoParam, 4096);
        errCode = OH\_Rdb\_RekeyEx(store, newCryptoParam);
        
        if (errCode != 0) {
            OH\_LOG\_ERROR(LOG\_APP, "RekeyEx failed, errCode: %{public}d", errCode);
        }
        // 在完成OH\_Rdb\_RekeyEx操作后，如果后续需要重新开库时必须使用新的参数来打开数据库
        OH\_Rdb\_DestroyConfig(config);
        OH\_Rdb\_CloseStore(store);
        OH\_Rdb\_DestroyCryptoParam(crypto);
        OH\_Rdb\_DestroyCryptoParam(newCryptoParam);
        config = NULL;
        store = NULL;
        crypto = NULL;
        newCryptoParam = NULL;
        
    *   场景3：原数据库为默认参数加密库，更换自定义密钥和加密参数。
        
        OH\_Rdb\_ConfigV2 \*config = OH\_Rdb\_CreateConfig();
        OH\_Rdb\_SetDatabaseDir(config, "/data/storage/el3/database");
        OH\_Rdb\_SetArea(config, RDB\_SECURITY\_AREA\_EL3);
        OH\_Rdb\_SetStoreName(config, "RdbTest.db");
        OH\_Rdb\_SetBundleName(config, "com.example.nativedemo");
        OH\_Rdb\_SetModuleName(config, "entry");
        OH\_Rdb\_SetSecurityLevel(config, OH\_Rdb\_SecurityLevel::S3);
        OH\_Rdb\_SetEncrypted(config, true);
        int errCode = 0;
        OH\_Rdb\_Store \*store = OH\_Rdb\_CreateOrOpen(config, &errCode);
        if (store == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create store failed, errCode: %{public}d", errCode);
            OH\_Rdb\_DestroyConfig(config);
            config = NULL;
            return;
        }
        OH\_Rdb\_CryptoParam \*crypto = OH\_Rdb\_CreateCryptoParam();
        if (crypto == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create crypto failed.");
            OH\_Rdb\_CloseStore(store);
            OH\_Rdb\_DestroyConfig(config);
            config = NULL;
            store = NULL;
            return;
        }
        // 注意：示例中使用硬编码密钥仅用于演示目的，实际应用中应使用安全的密钥管理服务，使用后应该及时清零
        uint8\_t key\[\] = "12345678";
        errCode = OH\_Crypto\_SetEncryptionKey(crypto, key, sizeof(key));
        memset(key, 0, sizeof(key));
        OH\_Crypto\_SetEncryptionAlgo(crypto, RDB\_AES\_256\_CBC);
        OH\_Crypto\_SetHmacAlgo(crypto, RDB\_HMAC\_SHA512);
        OH\_Crypto\_SetKdfAlgo(crypto, RDB\_KDF\_SHA512);
        OH\_Crypto\_SetCryptoPageSize(crypto, 2048);
        errCode = OH\_Rdb\_RekeyEx(store, crypto);
        
        if (errCode != 0) {
            OH\_LOG\_ERROR(LOG\_APP, "RekeyEx failed, errCode: %{public}d", errCode);
        }
        // 在完成OH\_Rdb\_RekeyEx操作后，如果后续需要重新开库时必须使用新的参数来打开数据库
        OH\_Rdb\_DestroyConfig(config);
        OH\_Rdb\_CloseStore(store);
        OH\_Rdb\_DestroyCryptoParam(crypto);
        config = NULL;
        store = NULL;
        crypto = NULL;
        
    *   场景4：原数据库为自定义参数加密数据库，更换数据库生成密钥和自定义加密参数。
        
        OH\_Rdb\_ConfigV2 \*config = OH\_Rdb\_CreateConfig();
        OH\_Rdb\_SetDatabaseDir(config, "/data/storage/el3/database");
        OH\_Rdb\_SetArea(config, RDB\_SECURITY\_AREA\_EL3);
        OH\_Rdb\_SetStoreName(config, "RdbTest.db");
        OH\_Rdb\_SetBundleName(config, "com.example.nativedemo");
        OH\_Rdb\_SetModuleName(config, "entry");
        OH\_Rdb\_SetSecurityLevel(config, OH\_Rdb\_SecurityLevel::S3);
        OH\_Rdb\_SetEncrypted(config, true);
        OH\_Rdb\_CryptoParam \*crypto = OH\_Rdb\_CreateCryptoParam();
        if (crypto == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create crypto failed.");
            OH\_Rdb\_DestroyConfig(config);
            config = NULL;
            return;
        }
        // 注意：示例中使用硬编码密钥仅用于演示目的，实际应用中应使用安全的密钥管理服务，使用后应该及时清零
        uint8\_t encryptionKey\[\] = "12345678";
        OH\_Crypto\_SetEncryptionKey(crypto, encryptionKey, sizeof(encryptionKey));
        memset(encryptionKey, 0, sizeof(encryptionKey));
        OH\_Rdb\_SetCryptoParam(config, crypto);
        
        int errCode = 0;
        OH\_Rdb\_Store \*store = OH\_Rdb\_CreateOrOpen(config, &errCode);
        if (store == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create store failed, errCode: %{public}d", errCode);
            OH\_Rdb\_DestroyConfig(config);
            config = NULL;
            return;
        }
        OH\_Rdb\_CryptoParam \*newCryptoParam = OH\_Rdb\_CreateCryptoParam();
        if (newCryptoParam == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create newCryptoParam failed.");
            OH\_Rdb\_DestroyConfig(config);
            OH\_Rdb\_CloseStore(store);
            OH\_Rdb\_DestroyCryptoParam(crypto);
            config = NULL;
            store = NULL;
            crypto = NULL;
            return;
        }
        OH\_Crypto\_SetEncryptionAlgo(newCryptoParam, RDB\_AES\_256\_CBC);
        OH\_Crypto\_SetHmacAlgo(newCryptoParam, RDB\_HMAC\_SHA512);
        OH\_Crypto\_SetKdfAlgo(newCryptoParam, RDB\_KDF\_SHA512);
        OH\_Crypto\_SetCryptoPageSize(newCryptoParam, 4096);
        errCode = OH\_Rdb\_RekeyEx(store, newCryptoParam);
        
        if (errCode != 0) {
            OH\_LOG\_ERROR(LOG\_APP, "RekeyEx failed, errCode: %{public}d", errCode);
        }
        // 在完成OH\_Rdb\_RekeyEx操作后，如果后续需要重新开库时必须使用新的参数来打开数据库
        OH\_Rdb\_DestroyConfig(config);
        OH\_Rdb\_CloseStore(store);
        OH\_Rdb\_DestroyCryptoParam(crypto);
        OH\_Rdb\_DestroyCryptoParam(newCryptoParam);
        config = NULL;
        store = NULL;
        crypto = NULL;
        newCryptoParam = NULL;
        
    *   场景5：原数据库为加密数据库，更换为非加密数据库。
        
        OH\_Rdb\_ConfigV2 \*config = OH\_Rdb\_CreateConfig();
        OH\_Rdb\_SetDatabaseDir(config, "/data/storage/el3/database");
        OH\_Rdb\_SetArea(config, RDB\_SECURITY\_AREA\_EL3);
        OH\_Rdb\_SetStoreName(config, "RdbTest.db");
        OH\_Rdb\_SetBundleName(config, "com.example.nativedemo");
        OH\_Rdb\_SetModuleName(config, "entry");
        OH\_Rdb\_SetSecurityLevel(config, OH\_Rdb\_SecurityLevel::S3);
        OH\_Rdb\_SetEncrypted(config, true);
        
        int errCode = 0;
        OH\_Rdb\_Store \*store = OH\_Rdb\_CreateOrOpen(config, &errCode);
        if (store == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create store failed, errCode: %{public}d", errCode);
            OH\_Rdb\_DestroyConfig(config);
            config = NULL;
            return;
        }
        OH\_Rdb\_CryptoParam \*crypto = OH\_Rdb\_CreateCryptoParam();
        if (crypto == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create crypto failed.");
            OH\_Rdb\_DestroyConfig(config);
            OH\_Rdb\_CloseStore(store);
            config = NULL;
            store = NULL;
            return;
        }
        OH\_Crypto\_SetEncryptionAlgo(crypto, RDB\_PLAIN\_TEXT);
        errCode = OH\_Rdb\_RekeyEx(store, crypto);
        
        if (errCode != 0) {
            OH\_LOG\_ERROR(LOG\_APP, "RekeyEx failed, errCode: %{public}d", errCode);
        }
        // 在完成OH\_Rdb\_RekeyEx操作后，如果后续需要重新开库时必须使用新的参数来打开数据库
        OH\_Rdb\_DestroyConfig(config);
        OH\_Rdb\_CloseStore(store);
        OH\_Rdb\_DestroyCryptoParam(crypto);
        config = NULL;
        store = NULL;
        crypto = NULL;
        
    *   场景6：原数据库为非加密数据库，更换为加密数据库。
        
        OH\_Rdb\_ConfigV2 \*config = OH\_Rdb\_CreateConfig();
        OH\_Rdb\_SetDatabaseDir(config, "/data/storage/el3/database");
        OH\_Rdb\_SetArea(config, RDB\_SECURITY\_AREA\_EL3);
        OH\_Rdb\_SetStoreName(config, "RdbTest.db");
        OH\_Rdb\_SetBundleName(config, "com.example.nativedemo");
        OH\_Rdb\_SetModuleName(config, "entry");
        OH\_Rdb\_SetSecurityLevel(config, OH\_Rdb\_SecurityLevel::S3);
        OH\_Rdb\_SetEncrypted(config, false);
        
        int errCode = 0;
        OH\_Rdb\_Store \*store = OH\_Rdb\_CreateOrOpen(config, &errCode);
        if (store == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create store failed, errCode: %{public}d", errCode);
            OH\_Rdb\_DestroyConfig(config);
            config = NULL;
            return;
        }
        OH\_Rdb\_CryptoParam \*crypto = OH\_Rdb\_CreateCryptoParam();
        if (crypto == NULL) {
            OH\_LOG\_ERROR(LOG\_APP, "Create crypto failed.");
            OH\_Rdb\_DestroyConfig(config);
            OH\_Rdb\_CloseStore(store);
            config = NULL;
            store = NULL;
            return;
        }
        OH\_Crypto\_SetEncryptionAlgo(crypto, RDB\_AES\_256\_CBC);
        OH\_Crypto\_SetHmacAlgo(crypto, RDB\_HMAC\_SHA512);
        OH\_Crypto\_SetKdfAlgo(crypto, RDB\_KDF\_SHA512);
        OH\_Crypto\_SetCryptoPageSize(crypto, 2048);
        errCode = OH\_Rdb\_RekeyEx(store, crypto);
        
        if (errCode != 0) {
            OH\_LOG\_ERROR(LOG\_APP, "RekeyEx failed, errCode: %{public}d", errCode);
        }
        // 在完成OH\_Rdb\_RekeyEx操作后，如果后续需要重新开库时必须使用新的参数来打开数据库
        OH\_Rdb\_DestroyConfig(config);
        OH\_Rdb\_CloseStore(store);
        OH\_Rdb\_DestroyCryptoParam(crypto);
        config = NULL;
        store = NULL;
        crypto = NULL;