南昌网站建设哪家最好,网页设计公司163企业邮箱,电商网站开发的项目描述,此网站不支持下载视频怎么办简介
SM2是非对称加密算法#xff0c;一提非对称加密算法#xff0c;第一想到的是RSA#xff0c;没错#xff0c;这个就是替代RSA的。它是基于椭圆曲线密码的公钥密码算法标准#xff0c;其秘钥长度256bit#xff0c;包含数字签名、密钥交换和公钥加密#xff0c;用于替…简介
SM2是非对称加密算法一提非对称加密算法第一想到的是RSA没错这个就是替代RSA的。它是基于椭圆曲线密码的公钥密码算法标准其秘钥长度256bit包含数字签名、密钥交换和公钥加密用于替换RSA/DH/ECDSA/ECDH等国际算法。可以满足电子认证服务系统等应用需求由国家密码管理局于2010年12月17号发布。SM2采用的是ECC 256位的一种其安全强度比RSA 2048位高且运算速度快于RSA。随着密码技术和计算技术的发展目前常用的1024位RSA算法面临严重的安全威胁我们国家密码管理部门经过研究决定采用SM2椭圆曲线算法替换RSA算法。SM2算法在安全性、性能上都具有优势。
用途
可以用于前后端传输数据加密解密。可以用于对数据加签验签确保报文的安全性和完整性。比如生成一套前端公私钥密钥对生成一套后端服务器公私钥密钥对。前端把参数json字符串通过服务器公钥用sm2算法加密服务器后端接收到请求后用服务器私钥解密拿到原始参数处理数据并生成响应数据把响应数据用前端公钥加密前端接收到响应加密后数据用前端私钥解密拿到响应json。这个过程是快速且安全的。一般这个过程在网关上公共实现
代码实现
引入依赖maven引入bcprov-jdk15on jar包,截止发文最新版本是1.70
!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on --
dependencygroupIdorg.bouncycastle/groupIdartifactIdbcprov-jdk15on/artifactIdversion1.70/version
/dependency
新建StandardSM2Engine实体类
package com.zhaohy.app.utils;import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.engines.SM2Engine.Mode;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.Memoable;
import org.bouncycastle.util.Pack;/*** 自定义SM2Engine类对加密解密数据进行了ASN.1编码*/
public class StandardSM2Engine {private final Digest digest;private final Mode mode;private boolean forEncryption;private ECKeyParameters ecKey;private ECDomainParameters ecParams;private int curveLength;private SecureRandom random;public StandardSM2Engine() {this(new SM3Digest());}public StandardSM2Engine(Mode mode) {this(new SM3Digest(), mode);}public StandardSM2Engine(Digest digest) {this(digest, Mode.C1C2C3);}public StandardSM2Engine(Digest digest, Mode mode) {if (mode null) {throw new IllegalArgumentException(mode cannot be NULL);}this.digest digest;this.mode mode;}public void init(boolean forEncryption, CipherParameters param) {this.forEncryption forEncryption;if (forEncryption) {ParametersWithRandom rParam (ParametersWithRandom) param;ecKey (ECKeyParameters) rParam.getParameters();ecParams ecKey.getParameters();ECPoint s ((ECPublicKeyParameters) ecKey).getQ().multiply(ecParams.getH());if (s.isInfinity()) {throw new IllegalArgumentException(invalid key: [h]Q at infinity);}random rParam.getRandom();} else {ecKey (ECKeyParameters) param;ecParams ecKey.getParameters();}curveLength (ecParams.getCurve().getFieldSize() 7) / 8;}public byte[] processBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException {if (forEncryption) {return encrypt(in, inOff, inLen);} else {return decrypt(in, inOff, inLen);}}public int getOutputSize(int inputLen) {return (1 2 * curveLength) inputLen digest.getDigestSize();}protected ECMultiplier createBasePointMultiplier() {return new FixedPointCombMultiplier();}/*** 加密* * param in* param inOff* param inLen* return* throws InvalidCipherTextException*/private byte[] encrypt(byte[] in, int inOff, int inLen) throws InvalidCipherTextException {byte[] c2 new byte[inLen];System.arraycopy(in, inOff, c2, 0, c2.length);ECMultiplier multiplier createBasePointMultiplier();ECPoint c1P;ECPoint kPB;do {BigInteger k nextK();c1P multiplier.multiply(ecParams.getG(), k).normalize();kPB ((ECPublicKeyParameters) ecKey).getQ().multiply(k).normalize();kdf(digest, kPB, c2);} while (notEncrypted(c2, in, inOff));byte[] c3 new byte[digest.getDigestSize()];addFieldElement(digest, kPB.getAffineXCoord());digest.update(in, inOff, inLen);addFieldElement(digest, kPB.getAffineYCoord());digest.doFinal(c3, 0);return convertToASN1(c1P, c2, c3);}/*** 解密* * param in* param inOff* param inLen* return* throws InvalidCipherTextException*/private byte[] decrypt(byte[] in, int inOff, int inLen) throws InvalidCipherTextException {byte[] decryptData new byte[inLen];System.arraycopy(in, inOff, decryptData, 0, decryptData.length);BigInteger x;BigInteger y;byte[] originC3;byte[] c2;ECPoint c1P;byte[] c1;try (ASN1InputStream aIn new ASN1InputStream(decryptData)) {ASN1Sequence seq;try {seq (ASN1Sequence) aIn.readObject();} catch (IOException e) {throw new InvalidCipherTextException();}x ASN1Integer.getInstance(seq.getObjectAt(0)).getValue();y ASN1Integer.getInstance(seq.getObjectAt(1)).getValue();c1P ecParams.getCurve().validatePoint(x, y);c1 c1P.getEncoded(false);if (mode Mode.C1C3C2) {originC3 ASN1OctetString.getInstance(seq.getObjectAt(2)).getOctets();c2 ASN1OctetString.getInstance(seq.getObjectAt(3)).getOctets();} else {c2 ASN1OctetString.getInstance(seq.getObjectAt(2)).getOctets();originC3 ASN1OctetString.getInstance(seq.getObjectAt(3)).getOctets();}} catch (IOException e) {throw new InvalidCipherTextException();}ECPoint s c1P.multiply(ecParams.getH());if (s.isInfinity()) {throw new InvalidCipherTextException([h]C1 at infinity);}c1P c1P.multiply(((ECPrivateKeyParameters) ecKey).getD()).normalize();kdf(digest, c1P, c2);byte[] c3 new byte[digest.getDigestSize()];addFieldElement(digest, c1P.getAffineXCoord());digest.update(c2, 0, c2.length);addFieldElement(digest, c1P.getAffineYCoord());digest.doFinal(c3, 0);int check 0;for (int i 0; i ! c3.length; i) {check | c3[i] ^ originC3[i];}Arrays.fill(c1, (byte) 0);Arrays.fill(c3, (byte) 0);if (check ! 0) {Arrays.fill(c2, (byte) 0);throw new InvalidCipherTextException(invalid cipher text);}return c2;}private boolean notEncrypted(byte[] encData, byte[] in, int inOff) {for (int i 0; i ! encData.length; i) {if (encData[i] ! in[inOff i]) {return false;}}return true;}private void kdf(Digest digest, ECPoint c1, byte[] encData) {int digestSize digest.getDigestSize();byte[] buf new byte[Math.max(4, digestSize)];int off 0;Memoable memo null;Memoable copy null;if (digest instanceof Memoable) {addFieldElement(digest, c1.getAffineXCoord());addFieldElement(digest, c1.getAffineYCoord());memo (Memoable) digest;copy memo.copy();}int ct 0;while (off encData.length) {if (memo ! null) {memo.reset(copy);} else {addFieldElement(digest, c1.getAffineXCoord());addFieldElement(digest, c1.getAffineYCoord());}Pack.intToBigEndian(ct, buf, 0);digest.update(buf, 0, 4);digest.doFinal(buf, 0);int xorLen Math.min(digestSize, encData.length - off);xor(encData, buf, off, xorLen);off xorLen;}}private void xor(byte[] data, byte[] kdfOut, int dOff, int dRemaining) {for (int i 0; i ! dRemaining; i) {data[dOff i] ^ kdfOut[i];}}private BigInteger nextK() {int qBitLength ecParams.getN().bitLength();BigInteger k;do {k BigIntegers.createRandomBigInteger(qBitLength, random);} while (k.equals(BigIntegers.ZERO) || k.compareTo(ecParams.getN()) 0);return k;}private void addFieldElement(Digest digest, ECFieldElement v) {byte[] p BigIntegers.asUnsignedByteArray(curveLength, v.toBigInteger());digest.update(p, 0, p.length);}private byte[] convertToASN1(ECPoint c1P, byte[] c2, byte[] c3) {ASN1Integer x new ASN1Integer(c1P.getXCoord().toBigInteger());ASN1Integer y new ASN1Integer(c1P.getYCoord().toBigInteger());DEROctetString derDig new DEROctetString(c3);DEROctetString derEnc new DEROctetString(c2);ASN1EncodableVector v new ASN1EncodableVector();switch (mode) {case C1C3C2:v.add(x);v.add(y);v.add(derDig);v.add(derEnc);break;default:v.add(x);v.add(y);v.add(derEnc);v.add(derDig);}DERSequence seq new DERSequence(v);try {return seq.getEncoded();} catch (IOException e) {throw new RuntimeException(e);}}}
新建Sm2Utils工具类
package com.zhaohy.app.utils;import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;/*** SM2工具类*/
public class Sm2Utils {/*** 加签* param plainText* return*/public static String sign(String plainText, String privateKeyStr) {BouncyCastleProvider provider new BouncyCastleProvider();try { // 获取椭圆曲线KEY生成器KeyFactory keyFactory KeyFactory.getInstance(EC, provider);byte[] privateKeyData Base64.getDecoder().decode(privateKeyStr);PKCS8EncodedKeySpec privateKeySpec new PKCS8EncodedKeySpec(privateKeyData);Signature rsaSignature Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), provider);rsaSignature.initSign(keyFactory.generatePrivate(privateKeySpec));rsaSignature.update(plainText.getBytes());byte[] signed rsaSignature.sign();return Base64.getEncoder().encodeToString(signed);} catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException | SignatureException e) {throw new RuntimeException(e);}}/*** 验签* param plainText* param signatureValue* return*/public static boolean verify(String plainText, String signatureValue, String publicKeyStr) {BouncyCastleProvider provider new BouncyCastleProvider();try {// 获取椭圆曲线KEY生成器KeyFactory keyFactory KeyFactory.getInstance(EC, provider);byte[] publicKeyData Base64.getDecoder().decode(publicKeyStr);X509EncodedKeySpec publicKeySpec new X509EncodedKeySpec(publicKeyData);// 初始化为验签状态Signature signature Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), provider);signature.initVerify(keyFactory.generatePublic(publicKeySpec));signature.update(Hex.decodeHex(plainText.toCharArray()));return signature.verify(Hex.decodeHex(signatureValue.toCharArray()));} catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException | SignatureException e) {throw new RuntimeException(e);} catch (IllegalArgumentException e) {LogUtils.error(验签失败, e);return false;} catch (DecoderException e) {LogUtils.error(验签失败, e);return false;}}/*** 加密* param plainText* return*/public static byte[] encrypt(String plainText, String publicKeyStr) throws Exception {Security.addProvider(new BouncyCastleProvider());try { // 获取椭圆曲线KEY生成器KeyFactory keyFactory KeyFactory.getInstance(EC);byte[] publicKeyData Base64.getDecoder().decode(publicKeyStr);X509EncodedKeySpec publicKeySpec new X509EncodedKeySpec(publicKeyData);PublicKey publicKey keyFactory.generatePublic(publicKeySpec);CipherParameters publicKeyParamerters ECUtil.generatePublicKeyParameter(publicKey);//数据加密StandardSM2Engine engine new StandardSM2Engine(new SM3Digest(), SM2Engine.Mode.C1C3C2);engine.init(true, new ParametersWithRandom(publicKeyParamerters));byte[] encryptData engine.processBlock(plainText.getBytes(), 0, plainText.getBytes().length);return encryptData;} catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException | InvalidCipherTextException e) {throw new RuntimeException(e);}}/*** 解密* param encryptedText* return*/public static String decrypt(byte[] encryptedData, String privateKeyStr) {Security.addProvider(new BouncyCastleProvider());try { // 获取椭圆曲线KEY生成器KeyFactory keyFactory KeyFactory.getInstance(EC);byte[] privateKeyData Base64.getDecoder().decode(privateKeyStr);PKCS8EncodedKeySpec privateKeySpec new PKCS8EncodedKeySpec(privateKeyData);PrivateKey privateKey keyFactory.generatePrivate(privateKeySpec);CipherParameters privateKeyParamerters ECUtil.generatePrivateKeyParameter(privateKey);//数据解密StandardSM2Engine engine new StandardSM2Engine(new SM3Digest(), SM2Engine.Mode.C1C3C2);engine.init(false, privateKeyParamerters);byte[] plainText engine.processBlock(encryptedData, 0, encryptedData.length);return new String(plainText);} catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException | InvalidCipherTextException e) {throw new RuntimeException(e);}}/*** SM2算法生成密钥对* return 密钥对信息*/public static KeyPair generateSm2KeyPair() {try {final ECGenParameterSpec sm2Spec new ECGenParameterSpec(sm2p256v1);// 获取一个椭圆曲线类型的密钥对生成器final KeyPairGenerator kpg KeyPairGenerator.getInstance(EC, new BouncyCastleProvider());SecureRandom random new SecureRandom();// 使用SM2的算法区域初始化密钥生成器kpg.initialize(sm2Spec, random);// 获取密钥对KeyPair keyPair kpg.generateKeyPair();return keyPair;} catch (Exception e) {LogUtils.error(generate sm2 key pair failed:{}, e.getMessage(), e);return null;}}public static void main(String[] args) throws Exception {KeyPair keyPair generateSm2KeyPair();String privateKey Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());String publicKey Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());String data {\daId\:\123456\};String encryptedJsonStr Hex.encodeHexString(encrypt(data, publicKey)) ;//16进制字符串String decryptedJsonStr decrypt(Hex.decodeHex(encryptedJsonStr), privateKey);String sign Hex.encodeHexString(Base64.getDecoder().decode(sign(data, privateKey)));boolean flag verify(Hex.encodeHexString(data.getBytes()), sign, publicKey);System.out.println(base64后privateKey: privateKey);System.out.println(base64后publicKey: publicKey);System.out.println(加密前数据: data);System.out.println(公钥加密后16进制字符串: encryptedJsonStr);System.out.println(私钥解密后数据 decryptedJsonStr);System.out.println(私钥加签后数据(16进制) sign);System.out.println(公钥验签结果 flag);}}
工具类里已经写好了调用测试main方法运行结果如下 base64后privateKey:MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgmze2hmNoi3XpeMPmLNIQBOC7P1iMbwGMMAdQXko0VWgCgYIKoEcz1UBgi2hRANCAATyF9jHsCvFaLkR4DSviX9CShZEXc7Nc1OueDaIpzZx/DRTRejSpTOcFmb0B9TsyYutnRWAx46nfkQ289NVXjg
base64后publicKey:MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE8hfYx7ArxWi5EeA0vr4l/QkoWRF3OzXNTrng2iKc2cfw0U0Xo0qUznBZm9AfU7MmLrZ0VgMeOp35ENvPTVV44A
加密前数据:{daId:123456}
公钥加密后16进制字符串:307b022100f1ab8b4fc8755d2ab84bf530f15ab14f5250d060d52679fd38e822a85eeceb860221009eef85a579ef9e2f27f44461a89046b95f8c630773be7ced1f3f3f48057f4777042064d9b5fb396591e022791858e334fc40b1ee9c93412442d403385266a00dd78704117b30ed38afcb361f8176acbbfbabad71e9
私钥解密后数据{daId:123456}
私钥加签后数据(16进制)304402201505caa731d9b70987a6c19f40618593e9acd8cad880dc001e52ef4b48638e2302204bdb5c5dc881789e716de7360aa387e5e4caa9f9f2fd6d1fbce35f1227b34440
公钥验签结果true 可以看到加密解密加签验签都是成功的。上面加密后生成的是byte[]转换成16进制字符串处理的也可以用base64实现byte[]和字符串的转换处理但是要保证对应加解密都是用base64实现byte[]和字符串的转换对应的上就行。