本文旨在深入探讨华为鸿蒙HarmonyOS Next系统(截止目前API12)中签名验签算法的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。
在数字世界中,数据的真实性、完整性和不可抵赖性至关重要。签名验签算法就像是数据的“守护者”,在HarmonyOS Next系统中发挥着不可或缺的作用。

一、签名验签算法概述

签名验签在数据安全中扮演着“数据保镖”的关键角色。想象一下,你收到一份重要文件,你怎么知道这份文件没有被篡改,并且确实是来自声称的发送者呢?这就需要签名验签算法来帮忙了。
在HarmonyOS Next系统中,签名验签算法的整体架构围绕着公钥和私钥展开。发送方使用自己的私钥对数据进行签名,就像是给数据加上了一个独一无二的“电子指纹”。这个签名包含了数据的特征信息以及发送方的身份标识(通过私钥体现)。接收方则使用发送方的公钥来验证这个签名。如果签名验证成功,就意味着数据在传输过程中没有被篡改,并且确实是由持有对应私钥的发送方发送的。这就确保了数据的真实性和完整性,同时发送方也无法抵赖发送过该数据,因为只有其私钥才能生成有效的签名。
不同的签名验签算法在实现细节上有所不同,但都遵循这个基本的架构和原理,为数据安全提供了坚实的保障。

二、RSA签名验签实战

(一)PKCS1填充模式下的操作流程

  1. 签名过程
       - 首先,我们需要生成RSA密钥对。假设我们要生成一个1024位的RSA密钥对,可以使用以下代码:

    import { cryptoFramework } from '@kit.CryptoArchitectureKit';
    async function generateRSAKeyPair() {
        let keyGenAlg = "RSA1024";
        let generator = cryptoFramework.createAsyKeyGenerator(keyGenAlg);
        return await generator.generateKeyPair();
    }

       - 然后,获取私钥并使用PKCS1填充模式和指定的摘要算法(如SHA256)创建Sign实例,对数据进行签名。例如:

    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);
    }

       - 这里,update方法用于传入要签名的数据,sign方法用于生成最终的签名。在PKCS1填充模式下,输入的数据长度需要满足一定要求,即小于RSA钥模 - 11。如果数据长度不符合要求,签名操作将失败。

  2. 验签过程
       - 同样先获取公钥,然后使用相同的PKCS1填充模式和摘要算法创建Verify实例,传入数据和签名进行验签。代码如下:

    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);
    }

       - 如果验签成功,说明数据没有被篡改且来源可靠;如果验签失败,则可能表示数据在传输过程中被修改或者签名无效。

    (二)PSS填充模式下的操作流程

  3. 签名过程
       - 生成RSA密钥对的方式与前面类似。然后,创建Sign实例时指定PSS填充模式、摘要算法(如SHA256)和掩码摘要(如MGF1_SHA256),并设置盐长度(例如32字节)。示例代码如下:

    async function signMessagePSS(priKey, plainText) {
        let signAlg = "RSA|PSS|SHA256|MGF1_SHA256";
        let signer = cryptoFramework.createSign(signAlg);
        await signer.init(priKey);
        signer.setSignSpec(cryptoFramework.SignSpecItem.PSS_SALT_LEN_NUM, 32);
        await signer.update(plainText);
        return await signer.sign(null);
    }

       - 在PSS填充模式下,对输入数据的长度要求更为严格,需要小于RSA钥模 - md摘要长度 - mgf1_md摘要长度 - 2。

  4. 验签过程
       - 创建Verify实例时,也需要使用与签名时相同的参数,包括填充模式、摘要算法、掩码摘要和盐长度。代码如下:

    async function verifyMessagePSS(pubKey, cipherText, signData) {
        let verifyAlg = "RSA2048|PSS|SHA256|MGF1_SHA256";
        let verifier = cryptoFramework.createVerify(verifyAlg);
        verifier.setVerifySpec(cryptoFramework.SignSpecItem.PSS_SALT_LEN_NUM, 32);
        await verifier.init(pubKey);
        await verifier.update(cipherText);
        return await verifier.verify(signData);
    }

       - 注意,这里的盐长度等参数必须与签名时设置的一致,否则验签将失败。

    (三)代码整合与示例

    以下是一个完整的RSA签名验签示例,包括生成密钥对、签名和验签的全过程:

    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);
    }
    // 使用PSS填充模式进行签名
    async function signMessagePSS(priKey, plainText) {
        let signAlg = "RSA|PSS|SHA256|MGF1_SHA256";
        let signer = cryptoFramework.createSign(signAlg);
        await signer.init(priKey);
        signer.setSignSpec(cryptoFramework.SignSpecItem.PSS_SALT_LEN_NUM, 32);
        await signer.update(plainText);
        return await signer.sign(null);
    }
    // 使用PSS填充模式进行验签
    async function verifyMessagePSS(pubKey, cipherText, signData) {
        let verifyAlg = "RSA2048|PSS|SHA256|MGF1_SHA256";
        let verifier = cryptoFramework.createVerify(verifyAlg);
        verifier.setVerifySpec(cryptoFramework.SignSpecItem.PSS_SALT_LEN_NUM, 32);
        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) };
            // 使用PKCS1填充模式签名
            let signDataPKCS1 = await signMessagePKCS1(keyPair.priKey, plainText);
            // 使用PKCS1填充模式验签
            let resultPKCS1 = await verifyMessagePKCS1(keyPair.pubKey, plainText, signDataPKCS1);
            if (resultPKCS1) {
                console.info('PKCS1 verify success');
            } else {
                console.error('PKCS1 verify failed');
            }
            // 使用PSS填充模式签名
            let signDataPSS = await signMessagePSS(keyPair.priKey, plainText);
            // 使用PSS填充模式验签
            let resultPSS = await verifyMessagePSS(keyPair.pubKey, plainText, signDataPSS);
            if (resultPSS) {
                console.info('PSS verify success');
            } else {
                console.error('PSS verify failed');
            }
        } catch (error) {
            console.error(`RSA signature verification “${error}“, error code: ${error.code}`);
        }
    }
    main();

    在这个示例中,我们首先生成了一个1024位的RSA密钥对。然后,分别使用PKCS1和PSS填充模式对消息进行签名,并使用相应的公钥进行验签。通过这个示例,我们可以清楚地看到RSA算法在不同填充模式下的签名验签操作流程,以及如何正确设置相关参数。

    三、其他算法签名验签

    (一)ECDSA算法

  5. 基本原理与特点
       - ECDSA(Elliptic Curve Digital Signature Algorithm)算法基于椭圆曲线密码学。它利用椭圆曲线上的点运算来生成签名和验证签名。与RSA算法相比,ECDSA算法在相同安全强度下所需的密钥长度更短,计算效率更高,尤其适用于资源受限的环境,如移动设备和物联网设备。
  6. 示例代码展示

    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('ECDSA verify success');
            } else {
                console.error('ECDSA verify failed');
            }
        } catch (error) {
            console.error(`ECDSA signature verification “${error}“, error code: ${error.code}`);
        }
    }
    main();

    在这个示例中,我们生成了ECC256的ECDSA密钥对,然后对消息进行签名和验签。可以看到,ECDSA算法的签名验签过程与RSA算法有相似之处,但在参数设置和计算方式上有所不同。

    (二)DSA算法

  7. 基本原理与特点
       - DSA(Digital Signature Algorithm)算法的安全性基于整数有限域离散对数问题的困难性。它在数字签名方面具有一定的应用,但相比其他算法,其计算效率较低,密钥长度较长。然而,在一些特定的安全标准和协议中,DSA算法因其历史原因和兼容性需求而被采用。
  8. 示例代码展示(假设支持DSA算法)

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

    在这个示例中,我们展示了DSA算法在HarmonyOS Next中的签名验签操作。需要注意的是,DSA算法在实际应用中可能会受到性能和密钥长度的限制,但在某些特定场景下仍有其价值。
  9. 基本原理与特点
       - SM2算法是我国自主设计的基于椭圆曲线的签名验签算法。它具有自主可控的优势,在国内的政务、金融等对安全性要求较高的领域得到广泛应用。SM2算法支持多种功能,如加密、签名、密钥交换等,并且在性能和安全性方面表现出色。
  10. 示例代码展示(假设当前环境支持SM2算法相关操作)

    import { cryptoFramework } from '@kit.CryptoArchitectureKit';
    import { buffer } from '@kit.ArkTS';
    // 生成SM2密钥对
    async function generateSM2KeyPair() {
        let keyGenAlg = "SM2_256";
        let generator = cryptoFramework.createAsyKeyGenerator(keyGenAlg);
        return await generator.generateKeyPair();
    }
    // 使用SM2算法进行签名(当前仅支持SM3摘要)
    async function signMessageSM2(priKey, plainText) {
        let signAlg = "SM2_256|SM3";
        let signer = cryptoFramework.createSign(signAlg);
        await signer.init(priKey);
        await signer.update(plainText);
        return await signer.sign(null);
    }
    // 使用SM2算法进行验签
    async function verifyMessageSM2(pubKey, cipherText, signData) {
        let verifyAlg = "SM2_256|SM3";
        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 generateSM2KeyPair();
            // 要签名的消息
            let message = "This is a test";
            let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(message, 'utf-8').buffer) };
            // 签名
            let signData = await signMessageSM2(keyPair.priKey, plainText);
            // 验签
            let result = await verifyMessageSM2(keyPair.pubKey, plainText, signData);
            if (result) {
                console.info('SM2 verify success');
            } else {
                console.error('SM2 verify failed');
            }
        } catch (error) {
            console.error(`SM2 signature verification “${error}“, error code: ${error.code}`);
        }
    }
在这个示例中,我们生成了SM2_256的密钥对,并使用SM3摘要进行签名验签操作。由于SM2算法的特殊性,目前仅支持SM3摘要,这在一定程度上限制了其与其他算法的通用性,但在国内特定领域中,它能提供强大的安全保障。
### (四)算法对比与优势场景分析
1. **与RSA算法的对比**
   - **密钥长度**:ECDSA和SM2算法在相同安全强度下,所需的密钥长度比RSA算法短。例如,256位的ECDSA或SM2密钥可以提供与3072位RSA密钥相当的安全级别。这使得它们在资源受限的设备上更具优势,因为较短的密钥长度意味着更少的计算资源消耗和存储需求。
   - **计算效率**:ECDSA和SM2算法的计算效率通常比RSA算法高,尤其是在椭圆曲线密码运算优化较好的情况下。它们在签名和验签操作上的速度更快,能够提高系统的整体性能。DSA算法的计算效率相对较低,在处理大量数据或高频率签名验签操作时可能会出现性能瓶颈。
   - **安全性**:RSA算法的安全性基于大整数分解问题的困难性,而ECDSA、DSA和SM2算法分别基于椭圆曲线密码学和整数有限域离散对数问题。这些算法在设计上都具有较高的安全性,但随着计算能力的提升,RSA算法的密钥长度可能需要不断增加以保持相同的安全强度,而椭圆曲线算法在这方面相对更具优势。
   - **应用场景**:RSA算法在一些传统的企业级应用和互联网场景中应用广泛,尤其是在与现有系统的兼容性方面表现较好。ECDSA算法适用于资源受限的环境,如移动支付、物联网设备之间的安全通信等场景。DSA算法在一些特定的安全标准和协议中仍有应用,但在现代应用中逐渐被其他更高效的算法替代。SM2算法由于其自主可控的特性,在国内的政务、金融等关键领域得到广泛应用。
2. **优势场景总结**
   - **ECDSA算法**:在移动设备和物联网设备中,由于其高效性和安全性,可用于保障设备之间的安全通信、身份认证和数据完整性验证。例如,在智能家居系统中,智能设备之间可以使用ECDSA算法进行签名验签,确保控制指令的真实性和完整性。
   - **DSA算法**:虽然计算效率相对较低,但在一些遵循特定安全标准和协议的传统系统中仍有其用武之地,如某些早期建立的企业内部安全架构。
   - **SM2算法**:在国内政务领域,用于电子公文的签名验签,确保公文的真实性、完整性和不可抵赖性;在金融领域,保障网上银行交易、电子合同签署等业务的安全。例如,银行的网上银行系统可以使用SM2算法对用户的交易指令进行签名验签,防止交易被篡改和伪造。

SameX
1 声望1 粉丝