文档首页/ 数据加密服务 DEW/ 常见问题/ 密钥管理/ 密钥管理服务如何使用SM2进行离线加密数据?
更新时间:2025-09-08 GMT+08:00
分享

密钥管理服务如何使用SM2进行离线加密数据?

以如下示例进行讲解:

使用SM2主密钥,密钥用途为ENCRYPT_DECRYPT。使用公钥离线加密"HELLO WORLD!":

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;

import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;

public class SM2offlineEncryption {

    private static final String TEST_DATA = "HELLO WORLD!";

    // SM2公钥,仅用作示例,实际使用时,需替换实际使用的SM2公钥。SM2公钥可在密钥详情界面获取
    private static final String TEST_PUBLIC_KEY
        = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE/AKIN6dVB3lQFuq3Cmhg+0k1QINkp1Ylo6ch1eNHNI0iBRV9m3CgDStox234Kka8mkYGJQDr5ZzrTdCOGAZrpA==";

    public static void main(String[] args)
        throws InvalidCipherTextException, NoSuchAlgorithmException, InvalidKeySpecException, IOException {
        final byte[] plaintext = TEST_DATA.getBytes();

        // BASE64解码SM2公钥
        final byte[] psm2PublicKeyBytes = Base64.getDecoder().decode(TEST_PUBLIC_KEY);
        final byte[] offlineEncryption = sm2PublicOfflineEncryption(psm2PublicKeyBytes, plaintext);

        // 仅打印加密结果,可调用KMS接口进行在线解密认证
        System.out.println(Base64.getEncoder().encodeToString(offlineEncryption));
    }

    public static byte[] sm2PublicOfflineEncryption(byte[] publicKeyBytes, byte[] plaintext)
        throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidCipherTextException, IOException {
        // 解析公钥
        final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
        final KeyFactory keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider());
        final ECPublicKey publicKey = (ECPublicKey) keyFactory.generatePublic(keySpec);
        final ECParameterSpec parameterSpec = publicKey.getParameters();
        final ECCurve curve = parameterSpec.getCurve();
        final ECPoint g = parameterSpec.getG();
        final BigInteger n = parameterSpec.getN();
        final BigInteger h = parameterSpec.getH();
        final ECDomainParameters domainParams = new ECDomainParameters(curve, g, n, h);
        final ECPublicKeyParameters publicKeyParams = new ECPublicKeyParameters(publicKey.getQ(), domainParams);

        // 初始化SM2加密引擎(使用C1C3C2模式)
        final SM2Engine engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        engine.init(true, new ParametersWithRandom(publicKeyParams, new SecureRandom()));

        // 执行加密
        final byte[] ciphertext = engine.processBlock(plaintext, 0, plaintext.length);

        final ECNamedCurveParameterSpec sm2Spec = ECNamedCurveTable.getParameterSpec("sm2p256v1");
        final int fieldSize = (sm2Spec.getCurve().getFieldSize() + 7) / 8; // SM2的坐标长度(32字节)
        final byte[] x = Arrays.copyOfRange(ciphertext, 1, 1 + fieldSize);          // 跳过04前缀
        final byte[] y = Arrays.copyOfRange(ciphertext, 1 + fieldSize, 1 + 2 * fieldSize);
        final byte[] c3 = Arrays.copyOfRange(ciphertext, 1 + 2 * fieldSize, 1 + 2 * fieldSize + 32);
        final byte[] c2 = Arrays.copyOfRange(ciphertext, 1 + 2 * fieldSize + 32, ciphertext.length);

        // 组装ASN.1 SEQUENCE: [X, Y, C3, C2]
        final ASN1EncodableVector asn1Vector = new ASN1EncodableVector();
        asn1Vector.add(new ASN1Integer(new BigInteger(1, x)));
        asn1Vector.add(new ASN1Integer(new BigInteger(1, y)));
        asn1Vector.add(new DEROctetString(c3));
        asn1Vector.add(new DEROctetString(c2));

        // 生成DER编码
        final DERSequence derSequence = new DERSequence(asn1Vector);
        return derSequence.getEncoded();
    }
}

相关文档