简单描述:
在上一家做物流监管平台的公司需要用到国密加解密向北交委传输数据,但对接第三方只给了个jar依赖包,本人公司是PHP技术栈。网上找了很多范例都无法通过第三方的加密验证获得权限认证,也求教过很多Java的大佬,不是给科普文档就是驴唇不对马嘴地说教,跨技术栈真是挺难的。只好自己在网上找些源码,动手调试了,索性调试出来了。以此留个笔记,说不定谁以后用得着呢。给出调试后的源码和jar包执行示例。当然网上也有那种有偿求教的,一次三五百,我也打听过。算了吧,自己搞出来也算成长。SM2打成jar包了,通过java-php-bridge或者php直接调用linux下java命令执行获得SM2加密结果就好了。
代码目录:
依赖文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jrmf</groupId>
<artifactId>maven_jar</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>main.main</mainClass>
</manifest>
<manifestEntries>
<Class-Path>.</Class-Path>
</manifestEntries>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>main</mainClass> <!-- 此处为主入口-->
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk16 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
<scope>provided</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>central</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<layout>default</layout>
<!-- 是否开启发布版构件下载 -->
<releases>
<enabled>true</enabled>
</releases>
<!-- 是否开启快照版构件下载 -->
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
代码文件-Cipher.java
//package com.cmm4cloud.service.basic.util.sm;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
public class Cipher{
private int ct;
private ECPoint p2;
private SM3Digest sm3keybase;
private SM3Digest sm3c3;
private byte key[];
private byte keyOff;
public Cipher()
{
this.ct = 1;
this.key = new byte[32];
this.keyOff = 0;
}
private void Reset()
{
this.sm3keybase = new SM3Digest();
this.sm3c3 = new SM3Digest();
// byte p[] = Util.byteConvert32Bytes(p2.getXCoord().toBigInteger());
this.sm3keybase.update(p, 0, p.length);
this.sm3c3.update(p, 0, p.length);
p = Util.byteConvert32Bytes(p2.getYCoord().toBigInteger());
this.sm3keybase.update(p, 0, p.length);
this.ct = 1;
NextKey();
byte p[]=Util.byteConvert32Bytes(p2.getX().toBigInteger());
this.sm3keybase.update(p,0,p.length);
this.sm3c3.update(p,0,p.length);
p = Util.byteConvert32Bytes(p2.getY().toBigInteger());
this.sm3keybase.update(p,0,p.length);
this.ct=1;
NextKey();
}
private void NextKey()
{
SM3Digest sm3keycur = new SM3Digest(this.sm3keybase);
sm3keycur.update((byte) (ct >> 24 & 0xff));
sm3keycur.update((byte) (ct >> 16 & 0xff));
sm3keycur.update((byte) (ct >> 8 & 0xff));
sm3keycur.update((byte) (ct & 0xff));
sm3keycur.doFinal(key, 0);
this.keyOff = 0;
this.ct++;
}
public ECPoint Init_enc(SM2 sm2, ECPoint userKey)
{
AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();
ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
BigInteger k = ecpriv.getD();
ECPoint c1 = ecpub.getQ();
this.p2 = userKey.multiply(k);
Reset();
return c1;
}
public void Encrypt(byte data[])
{
this.sm3c3.update(data, 0, data.length);
for (int i = 0; i < data.length; i++)
{
if (keyOff == key.length)
{
NextKey();
}
data[i] ^= key[keyOff++];
}
}
public void Init_dec(BigInteger userD, ECPoint c1)
{
this.p2 = c1.multiply(userD);
Reset();
}
public void Decrypt(byte data[])
{
for (int i = 0; i < data.length; i++)
{
if (keyOff == key.length)
{
NextKey();
}
data[i] ^= key[keyOff++];
}
this.sm3c3.update(data, 0, data.length);
}
public void Dofinal(byte c3[])
{
byte p[] = Util.byteConvert32Bytes(p2.getY().toBigInteger());
this.sm3c3.update(p, 0, p.length);
this.sm3c3.doFinal(c3, 0);
Reset();
}
}
代码文件-SM2:
//package com.cmm4cloud.service.basic.util.sm;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECFieldElement.Fp;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.security.SecureRandom;
public class SM2 {
//测试参数
// public static final String[] ecc_param = {
// "8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3",
// "787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498",
// "63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A",
// "8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7",
// "421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D",
// "0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2"
// };
//正式参数
public static String[] ecc_param = {
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
"28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"
};
public static SM2 Instance()
{
return new SM2();
}
public final BigInteger ecc_p;
public final BigInteger ecc_a;
public final BigInteger ecc_b;
public final BigInteger ecc_n;
public final BigInteger ecc_gx;
public final BigInteger ecc_gy;
public final ECCurve ecc_curve;
public final ECPoint ecc_point_g;
public final ECDomainParameters ecc_bc_spec;
public final ECKeyPairGenerator ecc_key_pair_generator;
public final ECFieldElement ecc_gx_fieldelement;
public final ECFieldElement ecc_gy_fieldelement;
public SM2()
{
this.ecc_p = new BigInteger(ecc_param[0], 16);
this.ecc_a = new BigInteger(ecc_param[1], 16);
this.ecc_b = new BigInteger(ecc_param[2], 16);
this.ecc_n = new BigInteger(ecc_param[3], 16);
this.ecc_gx = new BigInteger(ecc_param[4], 16);
this.ecc_gy = new BigInteger(ecc_param[5], 16);
this.ecc_gx_fieldelement = new Fp(this.ecc_p, this.ecc_gx);
this.ecc_gy_fieldelement = new Fp(this.ecc_p, this.ecc_gy);
this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b);
this.ecc_point_g = new ECPoint.Fp(this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement);
this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.ecc_n);
ECKeyGenerationParameters ecc_ecgenparam;
ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom());
this.ecc_key_pair_generator = new ECKeyPairGenerator();
this.ecc_key_pair_generator.init(ecc_ecgenparam);
}
}
代码文件-SM2Util :
//package com.cmm4cloud.service.basic.util.sm;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
public class SM2Util {
//数据加密
public static String encrypt(String publicKey, String data) throws IOException {
return encrypt(Util.hexToByte(publicKey), data.getBytes(StandardCharsets.UTF_8));
}
//数据加密
public static String encrypt(byte[] publicKey, byte[] data) throws IOException {
if (publicKey == null || publicKey.length == 0) {
return null;
}
if (data == null || data.length == 0) {
return null;
}
byte[] source = new byte[data.length];
System.arraycopy(data, 0, source, 0, data.length);
Cipher cipher = new Cipher();
SM2 sm2 = SM2.Instance();
ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);
ECPoint c1 = cipher.Init_enc(sm2, userKey);
cipher.Encrypt(source);
byte[] c3 = new byte[32];
cipher.Dofinal(c3);
// System.out.println("C1 " + Util.byteToHex(c1.getEncoded()));
// System.out.println("C2 " + Util.byteToHex(source));
// System.out.println("C3 " + Util.byteToHex(c3));
//C1 C2 C3拼装成加密字串
return Util.byteToHex(c1.getEncoded()) + Util.byteToHex(source) + Util.byteToHex(c3);
}
public static String decrypt(String privateKey, String encryptedData) {
byte[] decrypt = decrypt(Util.hexToByte(privateKey), Util.hexToByte(encryptedData));
return new String(decrypt, StandardCharsets.UTF_8);
}
//数据解密