加密与安全_三种方式实现基于国密非对称加密算法的加解密和签名验签

news2024/11/26 5:58:56

文章目录

  • 国际算法
  • 基础概念
  • 常见的加密算法及分类
  • 签名和验签
    • 基础概念
    • 常见的签名算法
    • 应用场景
  • 国密算法
    • 对称加密(DES/AES⇒SM4)
    • 非对称加密(RSA/ECC⇒SM2)
    • 散列(摘要/哈希)算法(MD5/SHA⇒SM3)
  • Code
    • 方式一 使用Bouncy Castle库实现国密SM2算法的加解密和签名验签功能
    • 方式二 基于Kona库从证书里读取公钥进行加解密和签名验签
    • 方式三 使用基于Bouncy Castle库封装的gm-java库
      • 密码算法
      • 证书
      • 密钥协商
      • 实现

在这里插入图片描述


国际算法

基础概念

  • 加密:将明文转换为密文,以保护数据的机密性。
  • 解密:将密文还原为明文,以便数据可被读取。

常见的加密算法及分类

加密算法主要分为对称加密和非对称加密两大类。

  1. 对称加密

    • 概念:使用相同的密钥进行加密和解密。
    • 优点:加密速度快,适合大数据量加密。
    • 缺点:密钥分发困难,密钥管理复杂。
    • 常见算法
      • AES(高级加密标准):广泛应用于各类数据加密。
      • DES(数据加密标准):已被认为不够安全,逐渐被淘汰。
      • 3DES(三重数据加密标准):通过三次加密提高安全性,但速度较慢。
  2. 非对称加密

    • 概念:使用一对公钥和私钥,其中公钥用于加密,私钥用于解密。
    • 优点:解决了密钥分发问题,公钥可以公开,而私钥需要保密。
    • 缺点:加密和解密速度较慢,通常用于加密小数据或密钥交换。
    • 常见算法
      • RSA:基于大素数分解,广泛用于数字签名和密钥交换。
      • ECC(椭圆曲线密码学):相对于RSA,提供更高的安全性和效率。

签名和验签

基础概念

  • 数字签名:生成消息摘要并使用私钥对摘要进行加密,确保消息的完整性和发送者的身份。
  • 验签:使用发送者的公钥解密消息摘要,并与接收到的消息计算出的摘要进行比较,验证消息的完整性和发送者的身份。

常见的签名算法

  • RSA:常用的数字签名算法,基于公钥和私钥对消息摘要进行加密和解密。
  • DSA(数字签名算法):专为数字签名设计,使用SHA-1或SHA-2生成消息摘要。
  • ECDSA(椭圆曲线数字签名算法):基于椭圆曲线密码学,提供更高效的签名和验证过程。

应用场景

  • 数据传输安全:SSL/TLS协议通过加密和签名技术确保数据传输的机密性和完整性。
  • 身份验证:数字签名用于验证身份,防止数据被篡改。
  • 电子邮件安全:PGP和S/MIME通过加密和签名保护电子邮件内容。
  • 区块链和加密货币:使用数字签名验证交易的合法性。

国密算法

在这里插入图片描述

国产密码算法(国密算法)是指国家密码局认定的国产商用密码算法,在一般信息化系统开发中主要使用公开的SM2(非对称)、SM3(摘要)、SM4(对称)三类算法。

Base64是一种编码格式,并不属于加密,常用于数据传输、兼容性问题。Base64编码本质上是一种将二进制数据转成文本数据的方案,将一些不适合传输的数据内容进行编码来适合传输。

对称加密(DES/AES⇒SM4)

🔸基本特点:

  • 同一个秘钥进行加密、解密。因此秘钥的保密性就很重要,不能泄漏。
  • 计算量小、效率高。

🔸应用场景:

  • 数据加密存储。
  • 加密通信,如HTTPS、SSL、VPN,会与其他加密算法混合使用。

非对称加密(RSA/ECC⇒SM2)

🔸基本特点:

  • 密钥配对:一个私钥、一个公钥,用私钥加密,用公钥解密。注意每一个密钥对都是独一无二、天生一对的,只能配对的秘钥加密、解密。简单来说,公钥加密的数据,只能配对的私钥才能解密。
  • 安全性高,但计算量大。
  • 基本原理:利用某一数学公式,正向计算容易,反向推理则非常难。

🔸应用场景:

  • HPPTS、SSH协议,如Git的ssh认证。
  • HTTPS的安全传输就是混合了非对称加密、对称加密,非对称加密协商会话秘钥,用会话秘钥对称加密传输数据。
  • 敏感数据的加密传输,如客户端登录时传输用户名、密码信息时,采用公钥加密数据,服务端私钥解密。
  • 对数据进行签名、验签,保障数据的完整性,同时验证身份。

散列(摘要/哈希)算法(MD5/SHA⇒SM3)

🔸基本特点:

  • 无需秘钥,“加密”后的数据不可逆。所以这也不算是“加密”,一般称为哈希(Hash)。
  • 任何长度的数据生成的哈希值长度都固定。
  • 相同数据每次生成的哈希值相同,不同的数据则不同。

🔸应用场景:

  • 数据摘要/哈希,验证数据是否被篡改、或数据丢失,保障数据的完整性、不可篡改性。
  • 单向加密保存数据,如密码的保存,密码的存储普遍都是存的哈希值,登录时比较其hash值即可。

🔸彩虹表破解:

由于散列算法的特点(同一数据生成哈希值始终一样),如果存储了大量(海量)字符内容的哈希值,就很容易进行查询破解了,这就是彩虹表,暴力破解也是一样的道理。常用的6位数字密码是相当容易破解的,如CMD5这个网站就利用存储的大量密文字典,反向查询破解。怎么解决呢?

  • 提升散列算法的安全性,常用方法就是加“盐”,核心思想就是在哈希过程中添加一些自定义的内容、规则,仅自己知道,从而避免被字典破解。

  • 最简单的比如在密文中某一位置添加一串随机字符,只有自己知道,使用的时候去掉干扰字符即可。

  • 加盐的具体方式很多,可以自己DIV,常用的一种模式如下图,加盐+双重哈希。 MD5(MD5(password) + salt)

  • 如果加盐的规则被窃取了,对于现代的硬件是挺容易被破解的,只能设置尽量复杂的密码+定期更换了。

📢实际应用中,可能会多种加密算法组合使用。

  • 比如HTTPS的安全数据传输就是同时用了对称加密、非对称加密,用非对称传送(动态)密钥,对称加密传输数据。 《HTTP协议图文简述》

  • 对一个数据进行多重加密,以防被破解


Code

方式一 使用Bouncy Castle库实现国密SM2算法的加解密和签名验签功能

    <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk18on -->
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk18on</artifactId>
            <version>1.78.1</version>
        </dependency>
package com.artisan.gmja;


import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class Sm2Example {

    /**
     * 静态初始化块,在类加载时执行。用于向Java安全提供者列表中添加Bouncy Castle提供者。
     *
     * Bouncy Castle提供者是一个著名的加密库,添加它是为了支持更多的加密算法和操作。
     * 这种做法通常用于需要加强加密能力的场景,比如数字签名、加密解密等操作。
     *
     * 选择在静态初始化块中添加提供者,是因为它在类加载时只执行一次,确保了提供者的添加是全局的和一次性的。
     * 这样做可以避免在每次使用加密操作时重复添加提供者,提高了代码的效率和可维护性。
     */
    static {
        Security.addProvider(new BouncyCastleProvider());
    }


    /**
     * 生成SM2密钥对。
     *
     * @return KeyPair 生成的密钥对,包括公钥和私钥。
     * @throws Exception 如果密钥对生成过程中发生错误,将抛出异常。
     */
    // 生成密钥对
    public static KeyPair generateKeyPair() throws Exception {
        // 实例化KeyPairGenerator对象,并指定算法为EC(椭圆曲线),提供者为BC(Bouncy Castle)
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");

        // 设置椭圆曲线参数为sm2p256v1,这是SM2算法所使用的特定椭圆曲线
        ECGenParameterSpec ecSpec = new ECGenParameterSpec("sm2p256v1");

        // 初始化KeyPairGenerator对象,使用随机数生成器以增加密钥的随机性
        kpg.initialize(ecSpec, new SecureRandom());

        // 生成密钥对,并返回
        return kpg.generateKeyPair();
    }


    /**
     * 使用SM2算法和公钥对数据进行加密。
     *
     * @param publicKey 公钥,用于加密数据。公钥是密钥对的一部分,用于加密操作,而私钥用于解密。
     * @param data      待加密的原始明文数据。
     * @return 加密后的数据以字节数组形式返回。
     * @throws Exception 如果加密过程中发生错误,将抛出异常。
     */
    // 加密
    public static byte[] encrypt(PublicKey publicKey, byte[] data) throws Exception {
        // 实例化Cipher对象,并指定使用SM2算法和BC提供者。
        Cipher cipher = Cipher.getInstance("SM2", "BC");
        // 初始化Cipher对象为加密模式,使用给定的公钥。
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        // 执行加密操作,返回加密后的数据。
        return cipher.doFinal(data);
    }


    /**
     * 使用SM2算法和私钥对加密数据进行解密。
     *
     * @param privateKey    解密所需的私钥。私钥是加密过程中使用的公钥的对应部分,
     *                      只有持有相应私钥的个体才能解密由公钥加密的数据。
     * @param encryptedData 待解密的加密数据。这些数据是由SM2算法和相应的公钥加密得到的。
     * @return 返回解密后的原始数据。这些数据在加密前是明文状态,通过解密操作恢复成原始形态。
     * @throws Exception 如果解密过程中发生错误,例如私钥无效或加密数据格式不正确,将抛出异常。
     */
    // 解密
    public static byte[] decrypt(PrivateKey privateKey, byte[] encryptedData) throws Exception {
        // 实例化Cipher对象,用于执行解密操作。Cipher是Java加密/解密的核心类。
        Cipher cipher = Cipher.getInstance("SM2", "BC");
        // 初始化Cipher对象为解密模式,使用提供的私钥。
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        // 执行解密操作,返回解密后的数据。
        return cipher.doFinal(encryptedData);
    }


    /**
     * 使用SM2算法和私钥对数据进行数字签名。
     *
     * @param privateKey 私钥,用于生成数字签名。私钥必须与用于验证签名的公钥匹配。
     * @param data       待签名的数据,通常为原始消息或消息的哈希值。
     * @return 返回使用SM2算法和私钥对数据签名后的字节数组。
     * @throws Exception 如果签名过程中发生错误,将抛出异常。
     */
    // 签名
    public static byte[] sign(PrivateKey privateKey, byte[] data) throws Exception {
        // 初始化签名对象,指定使用SM3withSM2算法和BC提供者。
        Signature signature = Signature.getInstance("SM3withSM2", "BC");
        // 使用私钥初始化签名对象,准备进行签名操作。
        signature.initSign(privateKey);
        // 更新签名对象,将待签名的数据加载到签名算法中。
        signature.update(data);
        // 执行签名操作,生成数字签名。
        return signature.sign();
    }


    /**
     * 使用SM2算法和SM3哈希函数验证数据的数字签名。
     * 此函数用于确认给定的数据是否是由对应的私钥进行签名的,通过公钥进行验证。
     *
     * @param publicKey      公钥,用于验证签名。
     * @param data           原始数据,被签名的数据。
     * @param signatureBytes 数字签名,用于验证数据的真实性和完整性。
     * @return boolean,如果签名验证成功,则返回true;否则返回false。
     * @throws Exception 如果在签名验证过程中发生错误,则抛出异常。
     */
    // 验签
    public static boolean verify(PublicKey publicKey, byte[] data, byte[] signatureBytes) throws Exception {
        // 实例化签名对象,指定使用SM2算法和SM3哈希函数。
        Signature signature = Signature.getInstance("SM3withSM2", "BC");
        // 初始化签名对象为验证模式,使用提供的公钥。
        signature.initVerify(publicKey);
        // 更新签名对象,将待验证的数据输入。
        signature.update(data);
        // 执行签名验证,返回验证结果。
        return signature.verify(signatureBytes);
    }


    /**
     * 主函数,用于演示SM2加密算法的使用。
     * 生成SM2密钥对,并进行加密、解密以及签名、验签操作。
     *
     * @param args 命令行参数
     * @throws Exception 如果密钥生成或加解密过程中发生错误
     */
    public static void main(String[] args) throws Exception {
        // 生成SM2密钥对
        // 生成密钥对
        KeyPair keyPair = generateKeyPair();
        // 获取公钥,并将其转换为Base64编码
        PublicKey publicKey = keyPair.getPublic();
        String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey.getEncoded());
        // 输出公钥
        System.out.println("公钥(Base64): " + publicKeyBase64);
        // 获取私钥,并将其转换为Base64编码
        PrivateKey privateKey = keyPair.getPrivate();
        String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey.getEncoded());
        // 输出私钥
        System.out.println("私钥(Base64): " + privateKeyBase64);

        // 准备待加密的明文
        String plaintext = "I'm Artisan ; 加密算法主要分为对称加密和非对称加密两大类";
        // 使用公钥加密明文
        byte[] encryptedData = encrypt(publicKey, plaintext.getBytes());
        // 使用私钥解密密文
        byte[] decryptedData = decrypt(privateKey, encryptedData);
        // 输出明文和解密后的明文
        System.out.println("原文: " + plaintext);
        System.out.println("解密后: " + new String(decryptedData));

        // 将加密后的数据转换为Base64编码,方便展示和传输
        // 加密数据转Base64字符串
        String encodedData = Base64.getEncoder().encodeToString(encryptedData);
        // 从Base64编码还原回加密数据,用于解密
        // 需要时,从Base64字符串还原回字节数组进行解密
        byte[] decodedData = Base64.getDecoder().decode(encodedData);
        // 输出密文(Base64编码)
        System.out.println("密文: " + encodedData);

        // 使用私钥对明文进行签名
        byte[] signature = sign(privateKey, plaintext.getBytes());
        // 使用公钥验证签名的合法性
        boolean isVerified = verify(publicKey, plaintext.getBytes(), signature);
        // 输出签名验证结果
        System.out.println("签名验证结果: " + isVerified);
    }

}

代码演示了如何使用Bouncy Castle库实现国密SM2算法的加解密和签名验签功能。具体流程如下:

  • 生成SM2密钥对。
  • 使用公钥加密数据,私钥解密数据。
  • 使用私钥对数据签名,公钥验证签名。
  • 输出相关的密钥、密文、解密结果和签名验证结果。

在这里插入图片描述


方式二 基于Kona库从证书里读取公钥进行加解密和签名验签

  <!-- https://mvnrepository.com/artifact/com.tencent.kona/kona-crypto -->
        <dependency>
            <groupId>com.tencent.kona</groupId>
            <artifactId>kona-crypto</artifactId>
            <version>1.0.12</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/com.tencent.kona/kona-pkix -->
        <dependency>
            <groupId>com.tencent.kona</groupId>
            <artifactId>kona-pkix</artifactId>
            <version>1.0.12</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/com.tencent.kona/kona-provider -->
        <dependency>
            <groupId>com.tencent.kona</groupId>
            <artifactId>kona-provider</artifactId>
            <version>1.0.12</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/com.tencent.kona/kona-ssl -->
        <dependency>
            <groupId>com.tencent.kona</groupId>
            <artifactId>kona-ssl</artifactId>
            <version>1.0.12</version>
        </dependency>

package com.artisan.kona;

import com.tencent.kona.crypto.CryptoInsts;
import com.tencent.kona.crypto.KonaCryptoProvider;
import com.tencent.kona.pkix.KonaPKIXProvider;
import com.tencent.kona.pkix.PKIXInsts;
import com.tencent.kona.ssl.KonaSSLProvider;
import com.tencent.kona.sun.security.x509.SMCertificate;

import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */


public class GmEnDeUtil {
    /**
     * 私钥字符串
     */
    private static String privateKey = "MIGHAgEA省略省略省略-----ODxilTt74";

    /**
     * 公钥证书字符串
     */
    private static String publicPEM = "-----BEGIN CERTIFICATE-----\n" +
            "MII省略省略省略-----d/s8iNVcYJ1\n" +
            "-----END CERTIFICATE-----\n";

    /**
     * 使用EC(椭圆曲线)加密算法进行加解密和数字签名的操作。
     * 包括了私钥的解码和生成、公钥证书的加载、数据的签名和验证、以及数据的加密和解密。
     *
     * @param args 命令行参数
     * @throws Exception 如果操作过程中发生错误
     */
    public static void main(String[] args) throws Exception {
        // 注册加密、PKIX和SSL提供者,这些提供者为Java加密系统提供了额外的功能和支持。
        Security.addProvider(new KonaCryptoProvider());
        Security.addProvider(new KonaPKIXProvider());
        Security.addProvider(new KonaSSLProvider());


        // 解码私钥并生成私钥对象,用于后续的签名操作。
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKey);
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = CryptoInsts.getKeyFactory("EC");
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

        // 输出私钥的算法和格式信息。
        System.out.println("私钥算法" + privateKey.getAlgorithm());
        System.out.println("私钥格式" +privateKey.getFormat());


        // 对字符串进行签名,证明数据的完整性和来源。
        String message = "I‘ artisan ,这是基于Tencent的Kona库实现的签名验签";
        // 私钥签名
        String sign = sign(message, privateKey);
        System.out.println("签名:" + sign);

        // 加载公钥证书,用于验证签名和加密数据。
        // 加载公钥证书
        X509Certificate certificate = loadCert(publicPEM, "9876543210");
        System.out.println("证书主题:" + certificate.getSubjectX500Principal().getName());

        PublicKey publicKey = certificate.getPublicKey();

        // 验证签名的有效性。
        System.out.println("验证结果:" + verify(message, publicKey, sign));

        // 使用公钥加密字符串。
        // 加密和解密字符串 (公钥加密,私钥解密)
        String encryptedString = encrypt(message, publicKey);
        System.out.println("加密字符串:" + encryptedString);

        // 使用私钥解密字符串。
        String decryptedString = decrypt(encryptedString, privateKey);
        System.out.println("解密字符串:" + decryptedString);
    }



    /**
     * 从PEM格式的字符串加载X.509证书。
     *
     * @param certPEM 证书的PEM格式字符串。
     * @param id      证书的唯一标识符,可选。
     * @return 加载成功的X.509证书。
     * @throws Exception 如果加载过程中发生错误。
     */
    private static X509Certificate loadCert(String certPEM, String id) throws Exception {
        // 创建X.509证书工厂
        CertificateFactory certFactory = PKIXInsts.getCertificateFactory("X.509");
        // 从PEM格式的字符串生成证书对象
        X509Certificate x509Cert = (X509Certificate) certFactory.generateCertificate(
                new ByteArrayInputStream(certPEM.getBytes()));

        // 如果提供了ID,并且ID不为空,则为证书设置ID
        if (id != null && !id.isEmpty()) {
            // 这里假设SMCertificate是X509Certificate的一个子类,且提供了setId方法
            ((SMCertificate) x509Cert).setId(id.getBytes(StandardCharsets.UTF_8));
        }

        // 返回加载成功的证书
        return x509Cert;
    }


    /**
     * 使用SM2私钥对给定字符串进行数字签名。
     * <p>
     * 数字签名用于验证消息的完整性和发送者的身份。此方法使用SM2加密算法,
     * 基于给定的私钥对字符串进行签名,以确保数据在传输过程中的安全性和真实性。
     *
     * @param str        待签名的字符串。
     * @param privateKey 用于签名的私钥。
     * @return 返回经过SM2签名算法处理后的签名字符串,使用Base64编码。
     * @throws Exception 如果签名过程中发生错误,则抛出异常。
     */
    private static String sign(String str, PrivateKey privateKey) throws Exception {
        // 获取SM2签名实例
        Signature signature = CryptoInsts.getSignature("SM2");
        // 初始化签名过程,使用提供的私钥
        signature.initSign(privateKey);
        // 更新待签名的数据
        signature.update(str.getBytes());
        // 执行签名操作,并将结果编码为Base64字符串返回
        return Base64.getEncoder().encodeToString(signature.sign());
    }


    /**
     * 使用SM2公钥验证数据的签名是否有效。
     *
     * @param str       待验证签名的数据。
     * @param publicKey 用于验证签名的公钥。
     * @param sign      待验证的签名字符串。
     * @return 如果签名验证通过,则返回true;否则返回false。
     * @throws Exception 如果加密操作失败,抛出异常。
     */
    private static boolean verify(String str, PublicKey publicKey, String sign) throws Exception {
        // 获取SM2签名实例
        Signature signature = CryptoInsts.getSignature("SM2");
        // 初始化签名验证器
        signature.initVerify(publicKey);
        // 更新待验证数据
        signature.update(str.getBytes());
        // 验证签名,返回验证结果
        return signature.verify(Base64.getDecoder().decode(sign));
    }


    /**
     * 使用SM2公钥对字符串进行加密。
     * <p>
     * SM2是一种基于椭圆曲线密码学的加密算法,适用于我国的加密技术标准。
     * 本方法通过获取SM2加密实例,使用给定的公钥将原始字符串加密为密文。
     * 加密后的密文通过Base64编码转换为字符串形式返回,便于存储或传输。
     *
     * @param str       待加密的原始字符串。
     * @param publicKey 用于加密的SM2公钥。
     * @return 加密后的密文,以Base64编码的字符串形式表示。
     * @throws Exception 如果加密过程中发生错误,将抛出异常。
     */
    private static String encrypt(String str, PublicKey publicKey) throws Exception {
        // 获取SM2加密实例
        Cipher cipher = CryptoInsts.getCipher("SM2");
        // 初始化加密器,设置为加密模式,并使用给定的公钥
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        // 执行加密操作,将原始字符串转换为加密后的字节数组
        byte[] encryptedBytes = cipher.doFinal(str.getBytes());
        // 将加密后的字节数组通过Base64编码转换为字符串形式返回
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }


    /**
     * 使用SM2私钥解密字符串。
     * <p>
     * SM2是一种国密算法,适用于数据解密。本方法接收一个经Base64编码的加密字符串和对应的私钥,
     * 通过SM2算法解密后返回原始字符串。
     *
     * @param str        经Base64编码且使用SM2算法加密的字符串。
     * @param privateKey 解密所需的SM2私钥。
     * @return 解密后的原始字符串。
     * @throws Exception 如果解密过程中发生错误,则抛出异常。
     */
    // 使用私钥解密字符串
    private static String decrypt(String str, PrivateKey privateKey) throws Exception {
        // 获取SM2加密算法的Cipher实例
        Cipher cipher = CryptoInsts.getCipher("SM2");
        // 初始化Cipher为解密模式,使用提供的私钥
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        // 解密Base64编码的字符串,得到原始字节数组
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(str));
        // 将解密后的字节数组转换为字符串并返回
        return new String(decryptedBytes);
    }

}

在这里插入图片描述


方式三 使用基于Bouncy Castle库封装的gm-java库

https://gitee.com/RuanKongkong/gm-java

密码算法

  • 对称密码算法 SM4(ECB/CBC/CTR/GCM)
  • 非对称密码算法 SM2(加解密/签名验签)
  • Hash算法 SM3
  • 基于SM3实现的随机数生成器(多线程加速)

证书

  • 证书解析以及证书SHA1指纹计算
  • SM2证书生成

密钥协商

  • 模拟TLS握手协议,通信双方协商会话密钥

实现

  <dependency>
            <groupId>io.github.KongkongRuan</groupId>
            <artifactId>gm-java</artifactId>
            <version>1.0.3</version>
        </dependency>

在这里插入图片描述

package com.artisan.gmja;

import com.yxj.gm.SM2.Cipher.SM2Cipher;
import com.yxj.gm.SM2.Key.SM2KeyPairGenerate;
import com.yxj.gm.SM2.Signature.SM2Signature;
import com.yxj.gm.SM3.SM3Digest;
import com.yxj.gm.SM4.SM4Cipher;
import com.yxj.gm.SM4.dto.AEADExecution;
import com.yxj.gm.enums.ModeEnum;
import com.yxj.gm.random.Random;
import org.bouncycastle.util.encoders.Hex;

import java.security.KeyPair;
import java.security.SecureRandom;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class GmJavaExample {

    /**
     * 主函数,用于演示各种密码学算法的使用,包括SM2加解密、签名验签、SM3摘要计算、SM4加解密。
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        // 定义测试消息
        String msg = "I'm artisan , 使用基于Bouncy Castle库封装的gm-java库";

        // 生成SM2密钥对
        KeyPair keyPair = SM2KeyPairGenerate.generateSM2KeyPair();

        // 使用SM2密钥对进行加密和解密
        SM2Cipher sm2Cipher = new SM2Cipher();
        byte[] encrypted = sm2Cipher.SM2CipherEncrypt(msg.getBytes(), keyPair.getPublic().getEncoded());
        byte[] decrypted = sm2Cipher.SM2CipherDecrypt(encrypted, keyPair.getPrivate().getEncoded());
        // 输出解密结果
        System.out.println("SM2解密结果:" + new String(decrypted));

        // 使用SM2密钥对进行签名和验证
        SM2Signature signature = new SM2Signature();
        byte[] signatureBytes = signature.signature(msg.getBytes(), null, keyPair.getPrivate().getEncoded());
        boolean verificationResult = signature.verify(msg.getBytes(), null, signatureBytes, keyPair.getPublic().getEncoded());
        // 输出验签结果
        System.out.println("SM2验签结果:" + verificationResult);

        // SM3摘要计算示例
        SM3Digest sm3Digest = new SM3Digest();
        sm3Digest.update(msg.getBytes());
        byte[] digest1 = sm3Digest.doFinal();
        byte[] digest2 = sm3Digest.doFinal(msg.getBytes());
        sm3Digest.update("gm-java-".getBytes());
        sm3Digest.update("1.0".getBytes());
        byte[] digest3 = sm3Digest.doFinal();
        // 输出摘要的十六进制表示
        System.out.println(Hex.toHexString(digest1));
        System.out.println(Hex.toHexString(digest2));
        System.out.println(Hex.toHexString(digest3));

        // 通过SM3算法生成随机数
        byte[] random = Random.RandomBySM3(16);
        System.out.println(Hex.toHexString(random));

        // SM4加解密示例,包括ECB、CBC、CTR和GCM模式
        SecureRandom secureRandom = new SecureRandom();
        byte[] key = new byte[16];
        byte[] iv = new byte[16];
        secureRandom.nextBytes(key);
        secureRandom.nextBytes(iv);

        // ECB模式
        SM4Cipher sm4CipherECB = new SM4Cipher(ModeEnum.ECB);
        byte[] encryptedECB = sm4CipherECB.cipherEncrypt(key, msg.getBytes(), null);
        byte[] decryptedECB = sm4CipherECB.cipherDecrypt(key, encryptedECB, iv);
        System.out.println("ECB明文:" + new String(decryptedECB));

        // CBC模式
        SM4Cipher sm4CipherCBC = new SM4Cipher(ModeEnum.CBC);
        byte[] encryptedCBC = sm4CipherCBC.cipherEncrypt(key, msg.getBytes(), iv);
        byte[] decryptedCBC = sm4CipherCBC.cipherDecrypt(key, encryptedCBC, iv);
        System.out.println("CBC明文:" + new String(decryptedCBC));

        // CTR模式
        SM4Cipher sm4CipherCTR = new SM4Cipher(ModeEnum.CTR);
        byte[] encryptedCTR = sm4CipherCTR.cipherEncrypt(key, msg.getBytes(), iv);
        byte[] decryptedCTR = sm4CipherCTR.cipherDecrypt(key, encryptedCTR, iv);
        System.out.println("CTR明文:" + new String(decryptedCTR));

        // GCM模式
        SM4Cipher sm4_gcm = new SM4Cipher();
        AEADExecution aeadExecution = sm4_gcm.cipherEncryptGCM(key, msg.getBytes(), new byte[12], "aad".getBytes(), 16);
        System.out.println("GCM密文:" + Hex.toHexString(aeadExecution.getCipherText()));
        System.out.println("GCMtag:" + Hex.toHexString(aeadExecution.getTag()));
        byte[] decryptedGCM = sm4_gcm.cipherDecryptGCM(key, aeadExecution.getCipherText(), new byte[12], "aad".getBytes(), aeadExecution.getTag());
        System.out.println("GCM明文:" + new String(decryptedGCM));
    }

}
    

输出如下:

SM2解密结果:I'm artisan , 使用基于Bouncy Castle库封装的gm-java库
SM2验签结果:true
080239d4f27412428a25c1f86d2523e4e9c6ed1d9b94e579632d77bc6d44794f
080239d4f27412428a25c1f86d2523e4e9c6ed1d9b94e579632d77bc6d44794f
cc7e992374984c82ab13f3f117d52849970628d16acf4cc1d9c137953e23a418
c963dee9f6945ec7c0a42379a576dabe
ECB明文:I'm artisan , 使用基于Bouncy Castle库封装的gm-java库
CBC明文:I'm artisan , 使用基于Bouncy Castle库封装的gm-java库
CTR明文:I'm artisan , 使用基于Bouncy Castle库封装的gm-java库
GCM密文:742d77292544e52da4c8f098dd5bd93b3bf8e9fd93a99bba1f0ab61a438074a9389ba6484664bb7dc899592e849b4c5cc2b3db0d9590e0e45b91fad85b
GCMtag:aac12ba04924fef14eaa0e896a346a45
GCM明文:I'm artisan , 使用基于Bouncy Castle库封装的gm-java库


在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1878970.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每日算法-插值查找

1.概念 插值查找是一种改良版的二分查找,其优势在于,对于较为均匀分布的有序数列,能够更快地使得mid中间游标快速接近目标值. 2.计算公式 中间游标计算公式. 公式说明: 公式的主要思路是,以第一次定位mid中间游标为例, 在接近平均分配的情况下,左右游标之间的差值表示总计供…

Linux线程同步【拿命推荐版】

目录 &#x1f6a9;引言 &#x1f6a9;听故事&#xff0c;引概念 &#x1f6a9;生产者消费者模型 &#x1f680;再次理解生产消费模型 &#x1f680;挖掘特点 &#x1f6a9;条件变量 &#x1f680;条件变量常用接口 &#x1f680;条件变量的原理 &#x1f6a9;引言 上一篇…

新的特性使得数据处理更加直观本教程将带你逐步了解如何使用Java Stream API

本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明来源(注明:作者:王文峰…

暑假集中备考2024年汉字小达人:来做18道历年选择题备考吧

结合最近几年的活动安排&#xff0c;预计2024年第11届汉字小达人比赛还有4个多月就启动&#xff0c;那么孩子们如何利用这段时间有条不紊地准备汉字小达人比赛呢&#xff1f; 我的建议是充分利用即将到来的暑假&#xff1a;①把小学1-5年级的语文课本上的知识点熟悉&#xff0…

[数据集][目标检测]围栏破损检测数据集VOC+YOLO格式1196张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1196 标注数量(xml文件个数)&#xff1a;1196 标注数量(txt文件个数)&#xff1a;1196 标注…

一篇就够了,为你答疑解惑:锂电池一阶模型-离线参数辨识(附代码)

锂电池一阶模型-参数离线辨识 背景模型简介数据收集1. 最大可用容量实验2. 开路电压实验3. 混合动力脉冲特性实验离线辨识对应模型对应代码总结下期预告文章字数有点多,耐心不够的谨慎点击阅读。 下期继续讲解在线参数辨识方法。 背景 最近又在开始重新梳理锂电池建模仿真与S…

Spring底层原理之bean的加载方式八 BeanDefinitionRegistryPostProcessor注解

BeanDefinitionRegistryPostProcessor注解 这种方式和第七种比较像 要实现两个方法 第一个方法是实现工厂 第二个方法叫后处理bean注册 package com.bigdata1421.bean;import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.…

wordpress企业主题和wordpress免费主题

农业畜牧养殖wordpress主题 简洁大气的农业畜牧养殖wordpress主题&#xff0c;农业农村现代化&#xff0c;离不开新农人、新技术。 https://www.jianzhanpress.com/?p3051 SEO优化wordpress主题 简洁的SEO优化wordpress主题&#xff0c;效果好不好&#xff0c;结果会告诉你…

天气网站爬虫及可视化

摘要&#xff1a;随着互联网的快速发展&#xff0c;人们对天气信息的需求也越来越高。本论文基于Python语言&#xff0c;设计并实现了一个天气网站爬虫及可视化系统。该系统通过网络爬虫技术从多个天气网站上获取实时的天气数据&#xff0c;并将数据进行清洗和存储。同时&#…

Halcon 椭圆

一 椭圆 方差的概念: 例1 两人的5次测验成绩如下&#xff1a;X&#xff1a; 50&#xff0c;100&#xff0c;100&#xff0c;60&#xff0c;50 E(X)72&#xff1b;Y&#xff1a; 73&#xff0c; 70&#xff0c; 75&#xff0c;72&#xff0c;70 E(Y)72。平均成绩相同&#xff0c…

idea 用久了代码提示变慢卡顿优化

idea 用久了代码提示变慢卡顿优化 修改虚拟机配置 修改编译构建堆内存

【proteus经典实战】16X192点阵程序

一、简介 6X192点阵程序通常用于表示高分辨率图像或文字&#xff0c;其中16X表示像素阵列的宽度&#xff0c;192表示每个像素阵列中的点阵数&#xff0c;16X192点阵程序需要一定的编程知识和技能才能编写和调试&#xff0c;同时还需要考虑硬件设备的兼容性和性能等因素。 初始…

智能交通(2)——IntelliLight智能交通灯

论文分享&#xff1a;IntelliLight | Proceedings of the 24th ACM SIGKDD International Conference on Knowledge Discovery & Data Mininghttps://dl.acm.org/doi/10.1145/3219819.3220096摘要 智能交通灯控制对于高效的交通系统至关重要。目前现有的交通信号灯大多由手…

共模和差模的基本概念

电压电流在导体或导线中传播时&#xff0c;存在两种工作形态&#xff1a;共模和差模。电子设备的信号线在进行相互通信时&#xff0c;至少会存在两根导线以形成电传输回路&#xff0c;除此之外&#xff0c;通常还存在第三个导体&#xff0c;即“参考地”。当信号正常传输时&…

JAVA学习笔记-JAVA基础语法-DAY19-File类、递归

第一章 File类 1.1 概述 java.io.File 类是文件和目录路径名的抽象表示&#xff0c;主要用于文件和目录的创建、查找和删除等操作。 1.2 构造方法 public File(String pathname) &#xff1a;通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。public File(St…

go Channel 原理 (一)

Channel 设计原理 不要通过共享内存的方式进行通信&#xff0c;而是应该通过通信的方式共享内存。 在主流编程语言中&#xff0c;多个线程传递数据的方式一般都是共享内存。 Go 可以使用共享内存加互斥锁进行通信&#xff0c;同时也提供了一种不同的并发模型&#xff0c;即通…

python课程设计作业-TCP客户端-服务端通信

说明文档 目录 小组成员分工 作品功能介绍 使用的工具和方法 设计的步骤 课程设计中遇到的问题 结论 1. 小组成员分工 本次课程设计由以下小组成员完成&#xff1a; xxx 2. 作品功能介绍 本次课程设计的作品是一个简单的基于 TCP 协议的客户端-服务端通信示例。通过这个示…

Halcon 特征检测使用

一 Region area: 面积row: 中心的行坐标column: 中心的列坐标width: 区域的宽度(平行于坐标轴)height: 区域的高度(平行于坐标轴)row1: 左上角的行坐标column1: 左上角的列坐标row2: 右下角的行坐标column2: 右下角的列坐标‘ra’; 椭圆的长半轴…

【杂说咋说】中国历史上最古老的十大建筑​,看看你都去过几个?

【杂说咋说】中国历史上最古老的十大建筑​&#xff0c;看看你都去过几个&#xff1f; 中国作为世界四大文明古国之一&#xff0c;历史文化源远流长。在几千年的历史变迁中&#xff0c;中华先祖在神州大地上留下了无数遗迹&#xff0c;其中包括很多古建筑。本期就来介绍一下中…

C语言图书管理系统控制台程序

程序示例精选 C语言图书管理系统控制台程序 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《C语言图书管理系统控制台程序》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读…