做淘宝导航网站,深圳31设计,潍坊网站建设哪家便宜,四川建设网站官网文章目录 前言一. 代码 前言
最近还要深度研究hutools底层实现#xff0c;一定要搞透澈#xff0c;本章将会是持续更新 参考资料#xff1a; Java代码实现SM2算法以及注意点总结#xff08;踩坑记录#xff09; 国密算法工具Smutil
一. 代码
import cn.hutool.core.uti… 文章目录 前言一. 代码 前言
最近还要深度研究hutools底层实现一定要搞透澈本章将会是持续更新 参考资料 Java代码实现SM2算法以及注意点总结踩坑记录 国密算法工具Smutil
一. 代码
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.ECKeyUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.SM4;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;import java.nio.charset.StandardCharsets;
import java.security.KeyPair;/*** author YuanJie* date 2024/8/6 上午8:44* 加密方式* 加密方式里边有两种 一种是 C1C3C2另一种是C1C2C3* 这两种加密方式不同当时找的资料说是旧版本的标准上* 使用的是C1C2C3但是后续应该是更新过使用的是C1C3C2* 其他语言比如Python或者Golang或者C的实现大多直接就是C1C3C2的* 但是如果java中使用的bouncycastle的包默认使用的是C1C2C3* 就会发生与其他语言的加密结果不能相互解密的情况但是可能你跟其他人的Java系统加解密又没有问题。* p* 导出* Java生成的密钥在导出的时候最好使用 PrivateKey.getD().toByteArray() 和 PublicKey.getQ().getEncoded() 导出密钥然后再转成Base64或者Hex给其他系统* p* 导入* 导入的时候请使用我上边的代码转换成对应的公钥或者私钥的对象* 这里有另一个容易出错的地方在私钥byte[]转私钥对象的时候* 有些人会使用new BigInteger(byte[])这个方法将byte[]转为BigInteger* 然后调用 keyFactory.generatePrivate(new ECPrivateKeySpec(BigInteger, ECParameterSpec))* 但是实测这样在一些情况下会报错这个时间长了记不清具体原因了好像是因为第一位为负数的情况下会报错* 我的方法是把byte[]转为Hex然后再使用new BigInteger(hexStr, int)这种方式转为BigInteger这个需要注意。* p* 公钥压缩* publicKey.getQ().getEncoded()这个方法* 有一个boolean参数控制输出的密钥是否为压缩后的密钥* 输出内容转为Hex之后02和03开头的为压缩后的密钥04表示未经压缩的密钥。* Java里边压缩为压缩的调用同一个方法就能转为公钥对象但是其他语言的目前不清楚所以导出的时候最好标注一下压缩或者未压缩。* p* Sm2签名时有一个userId的概念这个东西一般直接用CipherParameters对象是带不进去的* 如果没有默认是 Hex.decodeStrict(“31323334353637383132333435363738”) * 也就是1234567812345678的Ascii值如果要使用自定义的userId则需要使用ParametersWithID这个对象调用Signer的init* 这个对象可以传入一个CipherParameters然后再传入一个userId可以把制定的userId带进去。* p* 签名验签RS* 我们平常接触的算法一般我们调用加解密算法只返回一个值* 但是Sm2算法签名其实是有两个值一个R一个S* 两个值构成一个签名结果Java中bouncycastle的返回虽然也是一个值* 但是大概看了一下算法的实现代码其实得出的结果也是两个值* 一个R一个S然后通过一个方法拼接成一个值不确定这个方法的转换方式是不是有标准的。* 在我们与C程序一块测试的时候他们反馈了这个问题* 然后我对着bouncycastle包内的org.bouncycastle.crypto.signers.SM2Signer类* 摘出来了R、S和单一返回值的转换代码已经提取到上边的代码里可以直接使用。*/
Configuration
Slf4j
public class NationalSecretsUtils {public static String privateKey2;public static String publicKey2;public static String secretKey4;public NationalSecretsUtils(Value(${sm2.privateKey}) String privateKey2,Value(${sm2.publicKey}) String publicKey2,Value(${sm4.secretKey}) String secretKey4) {NationalSecretsUtils.privateKey2 privateKey2;NationalSecretsUtils.publicKey2 publicKey2;NationalSecretsUtils.secretKey4 secretKey4;}public static void main(String[] args) {KeyPair keyPair SecureUtil.generateKeyPair(SM2);// 2. 获取公私钥//这里公钥不压缩 公钥的第一个字节用于表示是否压缩 可以不要 65字节变64字节byte[] publicKey ((BCECPublicKey) keyPair.getPublic()).getQ().getEncoded(false);byte[] privateKey ((BCECPrivateKey) keyPair.getPrivate()).getD().toByteArray();String sm2pub HexUtil.encodeHexStr(publicKey);String sm2pri HexUtil.encodeHexStr(privateKey);NationalSecretsUtils.publicKey2 sm2pub;NationalSecretsUtils.privateKey2 sm2pri;NationalSecretsUtils.secretKey4 HexUtil.encodeHexStr(SecureUtil.generateKey(SM4).getEncoded());log.info(sm2公钥:{}, sm2pub);log.info(sm2私钥:{}, sm2pri);log.info(sm4密钥:{}, NationalSecretsUtils.secretKey4);// 测试sm2加解密log.info(sm2签名:{}, HexUtil.encodeHexStr(generateSM2Sign(我是sm2数据)));log.info(sm2验证:{}, verifySM2Sign(我是sm2数据, generateSM2Sign(我是sm2数据)));// 测试sm4加解密log.info(sm4加密:{}, HexUtil.encodeHexStr(generateSM4Sign(我是sm4数据)));log.info(sm4解密:{}, decryptSM4Sign(generateSM4Sign(我是sm4数据)));// 测试sm3哈希算法log.info(sm3哈希算法:{}, generateSM3Sign(我是sm3数据));log.info(sm3验证:{}, verifySM3Sign(我是sm3数据, generateSM3Sign(我是sm3数据)));}/*** SM2签名 使用私钥D值签名* 默认C1C3C2** param text* return*/public static byte[] generateSM2Sign(String text) {SM2 sm2 new SM2(privateKey2, null, null);sm2.usePlainEncoding();return sm2.sign(text.getBytes(StandardCharsets.UTF_8), null);}/*** SM2验证 使用公钥Q值验证签名** param text* param sign* return*/public static boolean verifySM2Sign(String text, byte[] sign) {SM2 sm2 new SM2(null, ECKeyUtil.toSm2PublicParams(publicKey2));sm2.usePlainEncoding();return sm2.verify(text.getBytes(StandardCharsets.UTF_8), sign);}/*** SM3 哈希算法 3是能够计算出256比特的散列值的单向散列函数主要用于数字签名和消息认证码。*/public static String generateSM3Sign(String text) {return SmUtil.sm3(text);}/*** SM3验证** param text* param sign* return*/public static boolean verifySM3Sign(String text, String sign) {return SmUtil.sm3(text).equals(sign);}/*** SM4 是一种对称加密算法它使用128位密钥使用分组密码模式使用CBC模式加密。* SM4加密*/public static String generateSM4Sign(String text) {SM4 sm4 SmUtil.sm4(HexUtil.decodeHex(secretKey4));return sm4.encryptHex(text);}/*** SM4解密*/public static String decryptSM4Sign(String text) {SM4 sm4 SmUtil.sm4(HexUtil.decodeHex(secretKey4));return sm4.decryptStr(text, StandardCharsets.UTF_8);}
}