基于国密算法满足在国企系统的建设安全要求

503 阅读4分钟

写作背景

由于我们公司在实施XX国企项目的过程,客户技术要求我们必须在IoT数据传输过程中使用国密算法。基于此客户需求对国密进行了一些了解。整理如下,可能将来对其他同学也有帮助

国密概念

国密是中国密码算法标准,也被称为商用密码。它是由中国国家密码管理局(简称“国家密码局”)组织研制的,是一种基于椭圆曲线密码体系的公钥密码算法。国密算法旨在保护国家信息安全,广泛应用于政府、金融、电信、电子商务等领域。 目前,国密算法包括国密 SM1、国密 SM2、国密 SM3 和国密 SM4 四种算法。其中,国密 SM2 是一种基于 ECC(Elliptic Curve Cryptography,椭圆曲线密码学)的公钥密码算法,支持数字签名、密钥交换和加密等功能;国密 SM3 是一种哈希算法,用于生成消息摘要;国密 SM4 是一种对称密码算法,用于数据加密和解密;国密 SM1 是一种对称密码算法,主要用于加密密钥的保护。

上面这些概念,看了之后对于普通开发人员来说,只是一堆概念,但是当我们要应用的时候,可能需要编码去实现,好在有一些类库已经帮我们做了基本的算法实现,如bouncycastle。

国密算法已经被广泛应用于各个领域,例如,在金融领域,国密算法已经成为银行卡芯片和POS终端的标准加密算法;在电信领域,国密算法已经被广泛应用于移动通信网络和移动设备的安全保护等方面;在政府领域,国密算法已经成为政务信息系统和电子政务的基本安全保障手段。

代码示例

以下是使用 BouncyCastle 加密库的 Maven 依赖配置:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15to18</artifactId>
    <version>1.69</version>
</dependency>

这里使用的是 bcprov-jdk15to18,它是 BouncyCastle 加密库的主要模块,支持 JDK 1.5 到 JDK 1.8 版本。如果需要支持其他 JDK 版本,可以使用相应的模块,例如 bcprov-jdk13、bcprov-jdk14 等。

此外,如果需要使用 BouncyCastle 加密库的其他功能,例如 SM2、SM3、SM4 等算法,还需要添加相应的模块依赖,例如:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15to18</artifactId>
    <version>1.69</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bctest-jdk15to18</artifactId>
    <version>1.69</version>
</dependency>

其中,bcpkix-jdk15to18 模块提供了 PKIX(Public Key Infrastructure)和 CMSCryptographic Message Syntax)标准的实现,包括 X.509 证书、证书链验证、OCSP(Online Certificate Status Protocol)和 CRL(Certificate Revocation List)等功能;bctest-jdk15to18 模块提供了 BouncyCastle 加密库的单元测试用例和性能测试用例。

SM2Demo

import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;

import java.security.Security;
import java.util.Arrays;

public class SM2Demo {
    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        // 生成密钥对
        ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
        X9ECParameters sm2p256v1 = org.bouncycastle.asn1.sec.SECNamedCurves.getByName("sm2p256v1");
        ECDomainParameters domainParameters = new ECDomainParameters(sm2p256v1.getCurve(), sm2p256v1.getG(), sm2p256v1.getN());
        ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(domainParameters, new SecureRandom());
        keyPairGenerator.init(keyGenerationParameters);
        AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 加密数据
        String plainText = "Hello, world!";
        byte[] publicKeyBytes = ((ECPublicKeyParameters) keyPair.getPublic()).getQ().getEncoded(false);
        byte[] cipherText = SM2Util.encrypt(plainText.getBytes("UTF-8"), publicKeyBytes);

        // 解密数据
        byte[] privateKeyBytes = ((ECPrivateKeyParameters) keyPair.getPrivate()).getD().toByteArray();
        byte[] decryptedText = SM2Util.decrypt(cipherText, privateKeyBytes);
        System.out.println(new String(decryptedText, "UTF-8"));
    }
}

SM2Util 国密算法工具类

    import org.bouncycastle.asn1.x9.X9ECParameters;
    import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
    import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
    import org.bouncycastle.crypto.params.ECDomainParameters;
    import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
    import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
    import org.bouncycastle.crypto.params.ECPublicKeyParameters;
    import org.bouncycastle.crypto.signers.SM2Signer;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import org.bouncycastle.util.encoders.Base64;

    import java.security.Security;
    import java.util.Arrays;

    public class SM2Util {
        // 公共参数 SM2_CURVE,表示 SM2 椭圆曲线
        public static final ECDomainParameters SM2_CURVE = new ECDomainParameters(
                new org.bouncycastle.math.ec.ECCurve.Fp(
                        new org.bouncycastle.math.ec.ECFieldElement.Fp(
                                new java.math.BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16)),
                        new org.bouncycastle.math.ec.ECFieldElement.Fp(
                                new java.math.BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16)),
                        new java.math.BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)),
                new org.bouncycastle.math.ec.ECPoint.Fp(
                        SM2_CURVE.getCurve(),
                        new org.bouncycastle.math.ec.ECFieldElement.Fp(
                                new java.math.BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16)),
                        new org.bouncycastle.math.ec.ECFieldElement.Fp(
                                new java.math.BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16)),
                        new java.math.BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)),
                new java.math.BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16));

    // 随机数生成器 SM2_RANDOM,用于加解密时的随机数生成
    public static final org.bouncycastle.crypto.prng.RandomGenerator SM2_RANDOM = new org.bouncycastle.crypto.prng.ThreadedSeedGenerator();

    /**
     * SM2 加密
     * @param plainText 明文
     * @param publicKey 公钥
     * @return 密文
     * @throws Exception
     */
    public static byte[] encrypt(byte[] plainText, byte[] publicKey) throws Exception {
        // 创建公钥参数对象 publicKeyParameters
        CipherParameters publicKeyParameters = new ECPublicKeyParameters(SM2Util.SM2_CURVE.getCurve().decodePoint(publicKey), SM2Util.SM2_CURVE);
        // 创建 SM2 引擎 sm2Engine,初始化为加密模式,并使用 publicKeyParameters 和 SM2_RANDOM
        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, SM2Util.SM2_RANDOM));
        // 调用 sm2Engine 的 processBlock 方法进行加密,返回加密后的密文
        return sm2Engine.processBlock(plainText, 0, plainText.length);
    }

    /**
     * SM2 解密
     * @param cipherText 密文
     * @param privateKey 私钥
     * @return 明文
     * @throws Exception
     */
    public static byte[] decrypt(byte[] cipherText, byte[] privateKey) throws Exception {
        // 创建私钥参数对象 privateKeyParameters
        CipherParameters privateKeyParameters = new ECPrivateKeyParameters(new java.math.BigInteger(1, privateKey), SM2Util.SM2_CURVE);
        // 创建 SM2 引擎 sm2Engine,初始化为解密模式,并使用 privateKeyParameters
        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(false, privateKeyParameters);
        // 调用 sm2Engine 的 processBlock 方法进行解密,返回解密后的明文
        return sm2Engine.processBlock(cipherText, 0, cipherText.length);
    }
}

依赖于 BouncyCastle 加密库。

解密过程,通过私钥创建私钥参数对象 privateKeyParameters,然后创建 SM2 引擎 sm2Engine,初始化为解密模式,并使用 privateKeyParameters。

加密过程 通过公钥创建公钥参数对象 publicKeyParameters,然后创建引擎 sm2Engine,初始化为加密模式,并使用 publicKeyParameters 和 SM2_RANDOM。接着,调用 sm2Engine 的 processBlock 方法进行加密,返回加密后的密文。