Uso de Tencent National Secret Suite KonaCrypto (cifrado SM2)

introducir

Tencent Kona Crypto es una implementación de proveedor de seguridad de Java y su nombre de proveedor es KonaCrypto. Sigue los estándares nacionales relevantes para implementar el siguiente algoritmo básico secreto nacional:

SM2, que es un algoritmo de cifrado de clave pública basado en la curva elíptica (ECC), sigue los siguientes estándares nacionales al implementar el algoritmo:

  • GB/T 32918.1-2016 Parte 1: Disposiciones generales
  • GB/T 32918.2-2016 Parte 2: Firma digital
  • GB/T32918.3-2016 Parte 3: Protocolo de intercambio de claves
  • GB/T 32918.4-2016 Parte 4: algoritmo de cifrado de clave pública
  • GB/T 32918.5-2017 Parte 5: Definición de parámetros

SM3, que es un algoritmo hash criptográficamente seguro, sigue los siguientes estándares nacionales al implementar el algoritmo:

  • Algoritmo hash criptográfico GB/T 32905-2016 SM3

SM4, que es un algoritmo de cifrado de bloques, sigue los siguientes estándares nacionales al implementar el algoritmo:

  • Algoritmo de cifrado de bloque GB/T 32907-2016 SM4

Para proporcionar las características anteriores, KonaCrypto implementa la interfaz de proveedor de servicios (SPI) como KeyPairGeneratorSpi, SignatureSpi, CipherSpi, MessageDigestSpi, MacSpi y KeyAgreementSpi definida por JDK basada en el marco de la arquitectura de criptografía Java (JCA) del estándar JDK.

usar

Dado que KonaCrypto se basa en el marco JCA, su estilo de uso es el mismo que el de otras implementaciones JCA (como SunJCE y SunEC que vienen con JDK). Normalmente, la aplicación no necesita acceder directamente a la clase de implementación del algoritmo en KonaCrypto, sino que llama a la implementación del algoritmo especificado a través de la API de JDK correspondiente. Comprender los principios de diseño y el estilo de código de JCA es muy útil para la aplicación de KonaCrypto, lea la [Guía de referencia] oficial.

Introducir dependencias

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

carga

Antes de usar cualquier función en KonaCrypto, se debe cargar KonaCryptoProvider,

Security.addProvider(new KonaCryptoProvider());

El método anterior agregará este proveedor al último lugar en toda la lista de proveedores, y su prioridad es la más baja. Si es necesario, se pueden insertar en la posición especificada de la lista de proveedores utilizando el siguiente método,

Security.insertProviderAt(new KonaCryptoProvider(), position);

Cuanto menor sea el valor de la posición, mayor será la prioridad del representante y el valor mínimo puede ser 1.

SM2

Par de claves

Generar un par de claves SM2 es exactamente lo mismo que generar un par de claves para otros algoritmos (como EC) que viene con el JDK. Solo necesita llamar a la API estándar de JDK para generar un par de claves.

Cree una instancia de KeyPairGenerator.

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("SM2");

Genere un par de claves.

KeyPair keyPair = keyPairGenerator.generateKeyPair();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();

El par de claves de SM2 es esencialmente un par de claves EC, por lo que la clave pública y la clave privada también se ajustan a los atributos de ECPublicKey y ECPrivateKey respectivamente.

La longitud de codificación de la clave pública SM2 se fija en 65 bytes, y su formato es 04|x|y, donde 04 representa el formato no comprimido, y x e y son la abscisa afín y la ordenada del punto de clave pública en el curva elíptica, respectivamente.

byte[] encodedPublicKey = publicKey.getEncoded();

La longitud de codificación de la clave privada SM2 se fija en 32 bytes y no hay formato de codificación.

byte[] encodedPrivateKey = privateKey.getEncoded();

Para obtener un uso más detallado de la API del generador de pares de claves, consulte la documentación oficial de KeyPairGenerator.

Preparar claves públicas y privadas.

En general, en las operaciones de firma y encriptación se utiliza el par de claves existente y no es necesario generarlo temporalmente. Por lo tanto, debe leer los datos de la clave pública y la clave privada de la siguiente manera para generar objetos PublicKey y PrivateKey respectivamente.

byte[] encodedPublicKey = <编码形式的公钥>;
byte[] encodedPrivateKey = <编码形式的私钥>;

KeyFactory keyFactory = KeyFactory.getInstance("SM2");

SM2PublicKeySpec publicKeySpec = new SM2PublicKeySpec(encodedPublicKey);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);

SM2PrivateKeySpec privateKeySpec = new SM2PrivateKeySpec(encodedPrivateKey);
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);

firmar

El uso del algoritmo de firma SM2 es muy similar al uso de otros algoritmos de firma (como ECDSA) que vienen con JDK, pero necesita usar una API personalizada para la configuración de parámetros.

Cree una instancia de Signature.

Signature signature = Signature.getInstance("SM2);

Inicializado con una clave privada en preparación para las operaciones de firma.

signature.initSign(privateKey);

Lo anterior es una inicialización minimalista que utiliza el ID de SM2 predeterminado, que es 1234567812345678. También utilizará la clave privada para calcular la clave pública, porque según la especificación, la clave pública también participa en el cálculo del valor de la firma, lo cual es una gran diferencia con el algoritmo de firma internacional (como ECDSA). Sin embargo, calcular la clave pública tendrá una cierta sobrecarga, lo que tendrá un impacto negativo en el rendimiento.

Si desea utilizar un ID no predeterminado o no desea calcular la clave pública adicionalmente, debe configurar adicionalmente una instancia personalizada de AlgorithmParameterSpec, a saber, SM2SignatureParameterSpec, antes de la inicialización.

byte[] altID = <定制化的ID>;
ECPublicKey publicKey = <公钥>;
SM2SignatureParameterSpec paramSpec = new SM2SignatureParameterSpec(altID, publicKey);
signature.setParameter(paramSpec);
signature.initSign(privateKey);

Una vez completada la configuración y la inicialización de los parámetros, se pueden pasar los datos del mensaje firmado.

byte[] message = <被签名的消息数据>;
signature.update(message);

Genera un valor con signo.

byte[] sign = signature.sign();

El valor de la firma SM2 se codifica utilizando el formato ASN.1 y su longitud oscila entre 71 y 73 bytes.

Inicialice con la clave pública en preparación para la verificación de la firma.

signature.initVerify(publicKey);

Pase los datos del mensaje firmado.

byte[] message = <被签名的消息数据>;
signature.update(message);

Luego pase el valor de la firma generada para su verificación,

boolean verified = signature.verify(sign);

Si la autenticación es exitosa, devuelve verdadero, de lo contrario, devuelve falso.

Cabe señalar que la clave privada se usa para firmar y la clave pública se usa para verificar la firma. Para un uso más detallado de la API del algoritmo de firma, consulte la documentación oficial de Signature.

cifrado

Por consideraciones de rendimiento, al igual que otros algoritmos de cifrado asimétrico (como RSA y EC), el algoritmo de cifrado SM2 generalmente solo se usa para cifrar una pequeña cantidad de datos críticos.

Cree una instancia de cifrado,

Cipher cipher = Cipher.getInstance("SM2");

Utilice la clave pública para inicializar Cipher y especificar su modo de cifrado.

cipher.init(Cipher.ENCRYPT_MODE, publicKey);

Los datos de mensajes entrantes generan texto cifrado.

byte[] message = <被加密的消息数据>;
byte[] ciphertext = cipher.doFinal(message);

Utilice la clave privada para inicializar Cipher y especificar su modo de descifrado.

cipher.init(Cipher.DECRYPT_MODE, privateKey);

Pase el texto cifrado para generar texto sin formato.

byte[] cleartext = cipher.doFinal(ciphertext);

Es importante tener en cuenta que la clave pública se utiliza para el cifrado y la clave privada se utiliza para el descifrado. Para un uso más detallado de la API del algoritmo de cifrado, consulte la documentación oficial de Cipher.

Los siguientes son algunos códigos de aplicación simples, cómo usarlos depende de la situación de su propio proyecto.

     // 在使用KonaCrypto中的任何特性之前,必须要加载KonaCryptoProvider,
            Security.addProvider(new KonaCryptoProvider());
            KeyFactory keyFactory = KeyFactory.getInstance("SM2");
            // 创建KeyPairGenerator实例,
            SM2KeyPairGenerator sm2KeyPairGenerator = new SM2KeyPairGenerator();
            // 生成随机密钥对。
            KeyPair keyPair = sm2KeyPairGenerator.generateKeyPair();
            // SM2的密钥对本质还是EC密钥对,所以其中的公钥与私钥也分别符合ECPublicKey与ECPrivateKey的属性。
            //SM2公钥的编码长度固定为65字节,其格式为04|x|y,其中04表示非压缩格式,x和y分别为该公钥点在椭圆曲线上的仿射横坐标和纵坐标的值。
            ECPublicKey publicKey1 = (ECPublicKey) keyPair.getPublic();
//            // SM2私钥的编码长度固定为32字节,无编码格式。
            ECPrivateKey privateKey1 = (ECPrivateKey) keyPair.getPrivate();

            byte[] encodedPublicKey = publicKey1.getEncoded();
            byte[] encodedPrivateKey = privateKey1.getEncoded();
            // 将公钥私钥转换输出
            String PrivateKey = Base64.getEncoder().encodeToString(encodedPrivateKey);
            String PublicKey = Base64.getEncoder().encodeToString(encodedPublicKey);
            System.out.println("Public:"+ PublicKey);
            System.out.println("Private:"+ PrivateKey);
            // 公钥
            SM2PublicKeySpec publicKeyKeySpec = new SM2PublicKeySpec(encodedPublicKey);
            PublicKey publicKey = keyFactory.generatePublic(publicKeyKeySpec);
            // 私钥
            SM2PrivateKeySpec privateKeySpec = new SM2PrivateKeySpec(encodedPrivateKey);
            PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);

            // 要加密的数据2
            byte[] data = ("这里是要加密的内容").getBytes("UTF-8");
            // 创建Cipher实例,
            Cipher cipher = Cipher.getInstance("SM2");
            // 使用公钥对Cipher进行初始化,指定其使用加密模式。
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            // 传入密文生成明文。
            byte[] ciphertext = cipher.doFinal(data);


            // 使用私钥对Cipher进行初始化,指定其使用解密模式。
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            String jiamijieguo = Base64.getEncoder().encodeToString(ciphertext);
            System.out.println("加密结果:"+Base64.getEncoder().encodeToString(ciphertext));
            // 传入密文生成明文。
            byte[] cleartext = cipher.doFinal(Base64.getDecoder().decode(jiamijieguo));
            System.out.println("解密结果:"+ new String(cleartext, StandardCharsets.UTF_8));


            // 签名
            Signature signature = Signature.getInstance("SM2");
            signature.initSign(privateKey);
            signature.update(cleartext);
            byte[] sign = signature.sign();
            System.out.println("sign:"+Base64.getEncoder().encodeToString(sign));

            // 验签
            signature.initVerify(publicKey);
            signature.update(cleartext);
            boolean verified = signature.verify(sign);
            System.out.println("验签结果:"+verified);

Guess you like

Origin blog.csdn.net/Soncat2000/article/details/130807625