【Java加解密系列】- SM2加解密

上一篇博客介绍了SM2算法生成密钥的过程,详见-SM2生成密钥。这篇博客接着介绍SM2算法如何用上篇博客生成的密钥进行加解密操作。

因为密钥都是byte数组,在进行加解密前,我们需要将密钥转换成BC库的CipherParameters。代码如下:

/**
     * 私钥转换为 {@link ECPrivateKeyParameters}
     * @param key key
     * @return
     * @throws InvalidKeyException
     */
    public static ECPrivateKeyParameters privateKeyToParams(String algorithm, byte[] key) throws InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException {
        Preconditions.checkNotNull(key, "key must be not null !");
        PrivateKey privateKey = generatePrivateKey(algorithm, key);
        return (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(privateKey);
    }

    /**
     * 生成私钥
     * @param algorithm 算法
     * @param key       key
     * @return
     */
    public static PrivateKey generatePrivateKey(String algorithm, byte[] key) throws NoSuchAlgorithmException, InvalidKeySpecException {
        Preconditions.checkNotNull(algorithm, "algorithm must be not null !");
        Preconditions.checkNotNull(key, "key must be not null !");
        KeySpec keySpec = new PKCS8EncodedKeySpec(key);
        algorithm = getAlgorithmAfterWith(algorithm);
        return getKeyFactory(algorithm).generatePrivate(keySpec);
    }

    /**
     * 公钥转换为 {@link ECPublicKeyParameters}
     * @param key key
     * @return
     * @throws InvalidKeyException
     */
    public static ECPublicKeyParameters publicKeyToParams(String algorithm, byte[] key) throws InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException {
        Preconditions.checkNotNull(key, "key must be not null !");
        PublicKey publicKey = generatePublicKey(algorithm, key);
        return (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(publicKey);
    }

    /**
     * 生成公钥
     * @param algorithm 算法
     * @param key       key
     * @return
     */
    public static PublicKey generatePublicKey(String algorithm, byte[] key) throws NoSuchAlgorithmException, InvalidKeySpecException {
        Preconditions.checkNotNull(algorithm, "algorithm must be not null !");
        Preconditions.checkNotNull(key, "key must be not null !");
        KeySpec keySpec = new X509EncodedKeySpec(key);
        algorithm = getAlgorithmAfterWith(algorithm);
        return getKeyFactory(algorithm).generatePublic(keySpec);
    }

    /**
     * 获取用于密钥生成的算法<br>
     * 获取XXXwithXXX算法的后半部分算法,如果为ECDSA或SM2,返回算法为EC
     * @param algorithm XXXwithXXX算法
     * @return 算法
     */
    private static String getAlgorithmAfterWith(String algorithm) {
        Preconditions.checkNotNull(algorithm, "algorithm must be not null !");
        int indexOfWith = StringUtils.lastIndexOfIgnoreCase(algorithm, "with");
        if (indexOfWith > 0) {
            algorithm = StringUtils.substring(algorithm, indexOfWith + "with".length());
        }
        if ("ECDSA".equalsIgnoreCase(algorithm) || "SM2".equalsIgnoreCase(algorithm)) {
            algorithm = "EC";
        }
        return algorithm;
    }

    /**
     * 获取{@link KeyFactory}
     * @param algorithm 非对称加密算法
     * @return {@link KeyFactory}
     */
    private static KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException {
        final Provider provider = new BouncyCastleProvider();
        return KeyFactory.getInstance(algorithm, provider);
    }

SM2操作主要有四类:加密、解密、签名和验签。代码如下:

/**
     * 加密
     * @param data      数据
     * @param publicKey 公钥
     * @return 加密之后的数据
     */
    public static byte[] encrypt(byte[] data, byte[] publicKey) throws Exception {
        CipherParameters pubKeyParameters = new ParametersWithRandom(publicKeyToParams("SM2", publicKey));
        SM2Engine engine = new SM2Engine(DIGEST);
        engine.init(true, pubKeyParameters);
        return engine.processBlock(data, 0, data.length);
    }

    /**
     * 解密
     * @param data       数据
     * @param privateKey 私钥
     * @return 解密之后的数据
     */
    public static byte[] decrypt(byte[] data, byte[] privateKey) throws Exception {
        CipherParameters privateKeyParameters = privateKeyToParams("SM2", privateKey);
        SM2Engine engine = new SM2Engine(DIGEST);
        engine.init(false, privateKeyParameters);
        byte[] byteDate = engine.processBlock(data, 0, data.length);
        return byteDate;
    }

    /**
     * 签名
     * @param data 数据
     * @return 签名
     */
    public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
        SM2Signer signer = new SM2Signer();
        CipherParameters param = new ParametersWithRandom(privateKeyToParams("SM2", privateKey));
        signer.init(true, param);
        signer.update(data, 0, data.length);
        return signer.generateSignature();
    }

    /**
     * 用公钥检验数字签名的合法性
     * @param data      数据
     * @param sign      签名
     * @param publicKey 公钥
     * @return 是否验证通过
     */
    public static boolean verify(byte[] data, byte[] sign, byte[] publicKey) throws Exception {
        SM2Signer signer = new SM2Signer();
        CipherParameters param = publicKeyToParams("SM2", publicKey);
        signer.init(false, param);
        signer.update(data, 0, data.length);
        return signer.verifySignature(sign);
    }

跑个测试用例试下:

public static void main(String[] args) throws Exception {
        KeyPair keyPair = generateSm2KeyPair();
        //明文
        String plaintext = "test";
        //加密
        String ciphertext = Base64Utils.encode(encrypt(plaintext.getBytes("utf-8"), keyPair.getPublic().getEncoded()));
        //生成签名
        String signature = Base64Utils.encode(sign(plaintext.getBytes("utf-8"),keyPair.getPrivate().getEncoded()));
        System.out.println("ciphertext: " + ciphertext);
        System.out.println("signature: " + signature);
        //解密
        plaintext = new String(decrypt(Base64Utils.decode(ciphertext),keyPair.getPrivate().getEncoded()),"utf-8");
        //验签
        boolean result = verify(plaintext.getBytes("utf-8"),Base64Utils.decode(signature),keyPair.getPublic().getEncoded());
        System.out.println("plaintext: " + plaintext);
        System.out.println("verify result: " + result);
    }

输出结果如下:

ciphertext: BFH0SXC1bX7OL1i5I4GO/Ck8Lak6gfzrsEzhrk4GY2cKZOug73ThLBYSjbtnpC5z30CJuKmaAf/W+jlviRr9PT1dcBWyrO599UUC4XWW8as4XHej8nE/Rlr9TP5+wP+4AWaub6o=
signature: MEYCIQC7OldJ7B8JvP51zw8P2DfieG5iAj5rWEybqZ3bPG8D5wIhAIVXlOmMxk0t4cNm0oQ0HYIzJZ5JShGIWuVwZLZ/t9mQ
plaintext: test
verify result: true

从结果可以看出,可以进行正常的加解密、生成签名以及验签操作。

  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
好的,我可以为您提供一些关于 JavaScript 和 JavaSM2 加解密的基本信息。 JavaScript 中的 SM2 加解密可以使用 jsrsasign 库进行实现。以下是一个简单的示例代码: ```javascript // 密钥对生成 var ec = new KJUR.crypto.ECDSA({'curve': 'sm2'}); var keypair = ec.generateKeyPairHex(); // 加密 var pubKey = keypair.ecpubhex; var plainText = 'hello world'; var cipher = KJUR.crypto.Cipher.encrypt(pubKey, plainText, 'SM2'); // 解密 var privKey = keypair.ecprvhex; var decrypted = KJUR.crypto.Cipher.decrypt(privKey, cipher, 'SM2'); console.log(decrypted); ``` Java 中的 SM2 加解密可以使用 Bouncy Castle 库进行实现。以下是一个简单的示例代码: ```java // 密钥对生成 ECNamedCurveParameterSpec sm2Spec = ECNamedCurveTable.getParameterSpec("sm2p256v1"); KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider()); kpg.initialize(sm2Spec, new SecureRandom()); KeyPair keyPair = kpg.generateKeyPair(); // 加密 Cipher cipher = Cipher.getInstance("SM2", new BouncyCastleProvider()); cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic()); byte[] plainText = "hello world".getBytes(); byte[] cipherText = cipher.doFinal(plainText); // 解密 cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); byte[] decrypted = cipher.doFinal(cipherText); System.out.println(new String(decrypted)); ``` 需要注意的是,JavaScript 和 Java 中的 SM2 加解密算法的实现细节可能有所不同,需要根据具体需求进行调整。
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值