bouncycastle安装配置
bouncycastle简介
bouncy castle(轻量级密码术包)是一种用于 Java 平台的开放源码的轻量级密码术包;它支持大量的密码术算法,并提供JCE 1.2.1的实现。
bouncycastle安装
环境搭建
maven下载
在进行bouncycastle安装之前先搭建好maven项目环境为后续算法实现打好基础
在这里推荐一篇博客对maven安装提供参考
我们选择apache-maven-3.8.6-bin.zip
进行下载
将压缩包解压到自己预安装路径。
随后进行环境变量配置
环境变量配置
首先先新建一个MAVEN_HOME的系统变量
随后编辑PATH环境变量,添加%MAVEN_HOME%\bin
随后在cmd中即可查看自己是否配置成功啦,若mvn -version能识别即成功
配置本地仓库
在maven路径处配置本地仓库文件夹maven-repository
在maven路径下的conf中找到setting.xml,在文件中找到localRepository结点添加仓库路径。
添加字段为:
<localRepository>仓库地址</localRepository>
通过网上资料我也了解到
localRepository节点用于配置本地仓库,本地仓库其实起到了一个缓存的作用,它的默认地址是 C:\Users\用户名.m2。
当我们从maven中获取jar包的时候,maven首先会在本地仓库中查找,如果本地仓库有则返回;如果没有则从远程仓库中获取包,并在本地库中保存。
此外,我们在maven项目中运行mvn install,项目将会自动打包并安装到本地仓库中。
配置镜像
因为国外的服务器下载jar包很慢所以我们改为阿里云服务器
在setting.xml文件中找到mirrors节点
添加如下配置信息
<!-- 阿里云仓库 -->
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
</mirror>
在这里我们也不必担心mirror有多个,因为有一个是maven默认配置的镜像地址,我们不用删除,因为每次maven运行寻找的都是第一个mirror,只要我们把阿里云配置在第一个位置就行啦!
配置JDK
依然是在setting.xml文件中进行配置,找到profiles节点,添加以下配置信息
<!-- java版本 -->
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
在配置完成之后我们就要见证一下成果啦
添加本地仓库配置
在cmd中运行mvn help:system
进行配置本地仓库。
运行成功之后就可以看到仓库里面有了一些文件
安装bouncycastle
网上的资料和博客说是去bouncycastle官网下载相关jar包
由于我们前面配置的JDK版本为1.8所以我们要下的jar包为:
bcprov-jdk15to18-169.jar
bcprov-ext-jdk15to18-169.jar
但是我发现我无法进入bouncycastle的官网,后面在一片博客中了解到直接在pom.xml中插入一条依赖即可以让idea自动导入jar包
国密算法实现
sm2实现
代码
SM2_Util.java
package sm2;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.encoders.Hex;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
public class SM2Util {
/**
* SM2加密算法
* @param publicKey 公钥
* @param data 明文数据
* @return
*/
public String encrypt(PublicKey publicKey, String data) {
ECPublicKeyParameters ecPublicKeyParameters = null;
if (publicKey instanceof BCECPublicKey) {
BCECPublicKey bcecPublicKey = (BCECPublicKey) publicKey;
ECParameterSpec ecParameterSpec = bcecPublicKey.getParameters();
ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
ecParameterSpec.getG(), ecParameterSpec.getN());
ecPublicKeyParameters = new ECPublicKeyParameters(bcecPublicKey.getQ(), ecDomainParameters);
}
SM2Engine sm2Engine = new SM2Engine();
sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
byte[] arrayOfBytes = null;
try {
byte[] in = data.getBytes("utf-8");
arrayOfBytes = sm2Engine.processBlock(in,0, in.length);
} catch (Exception e) {
System.out.println("SM2加密时出现异常:");
}
return Hex.toHexString(arrayOfBytes);
}
/**
* SM2解密算法
* @param privateKey 私钥
* @param cipherData 密文数据
* @return
*/
public String decrypt(PrivateKey privateKey, String cipherData) {
byte[] cipherDataByte = Hex.decode(cipherData);
BCECPrivateKey bcecPrivateKey = (BCECPrivateKey) privateKey;
ECParameterSpec ecParameterSpec = bcecPrivateKey.getParameters();
ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
ecParameterSpec.getG(), ecParameterSpec.getN());
ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(bcecPrivateKey.getD(),
ecDomainParameters);
SM2Engine sm2Engine = new SM2Engine();
sm2Engine.init(false, ecPrivateKeyParameters);
String result = null;
try {
byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
return new String(arrayOfBytes, "utf-8");
} catch (Exception e) {
System.out.println("SM2解密时出现异常");
}
return result;
}
}
sm2_demo.java
package sm2;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
public class sm2_demo {
private static String M="20201325xjr";
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
SM2Util sm2 = new SM2Util();
final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
// 获取一个椭圆曲线类型的密钥对生成器
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
// 使用SM2参数初始化生成器
kpg.initialize(sm2Spec);
// 获取密钥对
KeyPair keyPair = kpg.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
String data = sm2.encrypt(publicKey,M);
System.out.println("明文:"+M);
System.out.println("密文:"+data);
String text=sm2.decrypt(privateKey,data);
System.out.println("解密结果"+text);
}
}
添加configuration
随后运行即可啦!
sm3算法实现
代码
sm3主类测试代码
package sm3;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
public class sm3_demo {
private static String test="20201325xjr";
public static void main(String[] args) throws NoSuchAlgorithmException,
InvalidAlgorithmParameterException {
SM3Util sm3 = new SM3Util();
String data = sm3.plainEncrypt(test);
System.out.println("明文:"+test);
System.out.println("哈希值:"+data);
}
}
SM3Util
package sm3;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Hex;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
/**
* @Author: dzy
* @Date: 2018/10/19 16:36
* @Describe: SM3工具类(杂凑算法-hash算法)
*/
public class SM3Util {
/**
* 16进制字符串SM3生成HASH签名值算法
* @param hexString 16进制字符串
* @return
*/
public static String hexEncrypt(String hexString) {
byte[] srcData = Hex.decode(hexString);
byte[] encrypt = encrypt(srcData);
String cipherStr = Hex.toHexString(encrypt);
return cipherStr;
}
/**
* 16进制字符串SM3生成HASH签名值算法
* @param hexKey 16进制密钥
* @param hexString 16进制字符串
* @return
*/
public static String hexEncrypt(String hexKey, String hexString) {
byte[] key = Hex.decode(hexKey);
byte[] srcData = Hex.decode(hexString);
byte[] encrypt = encrypt(key, srcData);
String cipherStr = Hex.toHexString(encrypt);
return cipherStr;
}
/**
* 普通文本SM3生成HASH签名算法
* @param plain 待签名数据
* @return
*/
public static String plainEncrypt(String plain) {
// 将返回的hash值转换成16进制字符串
String cipherStr = null;
try {
//将字符串转换成byte数组
byte[] srcData = plain.getBytes(StandardCharsets.UTF_8);
//调用encrypt计算hash
byte[] encrypt = encrypt(srcData);
//将返回的hash值转换成16进制字符串
cipherStr = Hex.toHexString(encrypt);
} catch (Exception e) {
//log.error("将字符串转换为字节时出现异常:", e);
}
return cipherStr;
}
/**
* 普通文本SM3生成HASH签名算法
* @param hexKey 密钥
* @param plain 待签名数据
* @return
*/
public static String plainEncrypt(String hexKey, String plain) {
// 将返回的hash值转换成16进制字符串
String cipherStr = null;
try {
//将字符串转换成byte数组
byte[] srcData = plain.getBytes(StandardCharsets.UTF_8);
//密钥
byte[] key = Hex.decode(hexKey);
//调用encrypt计算hash
byte[] encrypt = encrypt(key, srcData);
//将返回的hash值转换成16进制字符串
cipherStr = Hex.toHexString(encrypt);
} catch (Exception e) {
//log.error("将字符串转换为字节时出现异常:", e);
}
return cipherStr;
}
/**
* SM3计算hashCode
* @param srcData 待计算数据
* @return
*/
public static byte[] encrypt(byte[] srcData) {
SM3Digest sm3Digest = new SM3Digest();
sm3Digest.update(srcData, 0, srcData.length);
byte[] encrypt = new byte[sm3Digest.getDigestSize()];
sm3Digest.doFinal(encrypt, 0);
return encrypt;
}
/**
* 通过密钥进行加密
* @param key 密钥byte数组
* @param srcData 被加密的byte数组
* @return
*/
public static byte[] encrypt(byte[] key, byte[] srcData) {
KeyParameter keyParameter = new KeyParameter(key);
SM3Digest digest = new SM3Digest();
HMac mac = new HMac(digest);
mac.init(keyParameter);
mac.update(srcData, 0, srcData.length);
byte[] result = new byte[mac.getMacSize()];
mac.doFinal(result, 0);
return result;
}
/**
* SM3计算hashCode
* @param srcData 待计算数据
* @return
* @throws Exception
*/
public static byte[] encrypt_0(byte[] srcData) throws Exception {
MessageDigest messageDigest = MessageDigest.getInstance("SM3", "BC");
byte[] digest = messageDigest.digest(srcData);
return digest;
}
}
运行结果
正确性验证
经过验证证明结果正确
sm4实现
代码
sm4主测试类代码
package sm4;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
public class sm4_demo {
private static String test= "20201325xjr_sm4_";
private static String mykey= "6cef39eb85614df44f6b0f6babcd89b6";
public static void main(String[] args) throws NoSuchAlgorithmException,
InvalidAlgorithmParameterException {
SM4Util sm4 = new SM4Util();
//SM3Util sm3 = new SM3Util();
//String data_in = sm3.plainEncrypt(test);
//String data = sm4.encryptByEcb(data_in,mykey);
String x16 = sm4.strToHexadecimal(test);
//System.out.println(x16);
String data = sm4.encryptByEcb(x16,mykey);
System.out.println("明文:"+test);
//System.out.println("摘 要:"+data_in);
System.out.println("密文:"+data);
String text_16 = sm4.decryptByEcb(data,mykey);
//System.out.println(text_16);
//System.out.println(x16.equals(text_16.toUpperCase()));
String text = sm4.hexadecimalToStr(text_16.toUpperCase());
System.out.println("解密结果:"+text);
}
}
SM4Util
package sm4;
import org.bouncycastle.crypto.engines.SM4Engine;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Hex;
/**
* @Author: dzy
* @Date: 2018/10/9 16:41
* @Describe: SM4算法
*/
public class SM4Util {
//加解密的字节快大小
public static final int BLOCK_SIZE = 16;
/**
* SM4ECB加密算法
* @param in 待加密内容
* @param keyBytes 密钥
* @return
*/
public static byte[] encryptByEcb0(byte[] in, byte[] keyBytes) {
SM4Engine sm4Engine = new SM4Engine();
sm4Engine.init(true, new KeyParameter(keyBytes));
int inLen = in.length;
byte[] out = new byte[inLen];
int times = inLen / BLOCK_SIZE;
for (int i = 0; i < times; i++) {
sm4Engine.processBlock(in, i * BLOCK_SIZE, out, i * BLOCK_SIZE);
}
return out;
}
/**
* SM4ECB加密算法
* @param in 待加密内容
* @param keyBytes 密钥
* @return
*/
public static String encryptByEcb(byte[] in, byte[] keyBytes) {
byte[] out = encryptByEcb0(in, keyBytes);
String cipher = Hex.toHexString(out);
return cipher;
}
/**
* SM4的ECB加密算法
* @param content 待加密内容
* @param key 密钥
* @return
*/
public static String encryptByEcb(String content, String key) {
byte[] in = Hex.decode(content);
byte[] keyBytes = Hex.decode(key);
String cipher = encryptByEcb(in, keyBytes);
return cipher;
}
/**
* SM4的ECB解密算法
* @param in 密文内容
* @param keyBytes 密钥
* @return
*/
public static byte[] decryptByEcb0(byte[] in, byte[] keyBytes) {
SM4Engine sm4Engine = new SM4Engine();
sm4Engine.init(false, new KeyParameter(keyBytes));
int inLen = in.length;
byte[] out = new byte[inLen];
int times = inLen / BLOCK_SIZE;
for (int i = 0; i < times; i++) {
sm4Engine.processBlock(in, i * BLOCK_SIZE, out, i * BLOCK_SIZE);
}
return out;
}
/**
* SM4的ECB解密算法
* @param in 密文内容
* @param keyBytes 密钥
* @return
*/
public static String decryptByEcb(byte[] in, byte[] keyBytes) {
byte[] out = decryptByEcb0(in, keyBytes);
String plain = Hex.toHexString(out);
return plain;
}
/**
* SM4的ECB解密算法
* @param cipher 密文内容
* @param key 密钥
* @return
*/
public static String decryptByEcb(String cipher, String key) {
byte[] in = Hex.decode(cipher);
byte[] keyBytes = Hex.decode(key);
String plain = decryptByEcb(in, keyBytes);
return plain;
}
public static String strToHexadecimal(String str) {
char[] chars = "0123456789ABCDEF".toCharArray();
StringBuilder sb = new StringBuilder("");
byte[] bs = str.getBytes();
int bit;
for (int i = 0; i < bs.length; i++) {
bit = (bs[i] & 0x0f0) >> 4;
sb.append(chars[bit]);
bit = bs[i] & 0x0f;
sb.append(chars[bit]);
}
return sb.toString().trim();
}
public static String hexadecimalToStr(String hexStr) {
String str = "0123456789ABCDEF";
char[] hexs = hexStr.toCharArray();
byte[] bytes = new byte[hexStr.length() / 2];
int n;
for (int i = 0; i < bytes.length; i++) {
n = str.indexOf(hexs[2 * i]) * 16;
n += str.indexOf(hexs[2 * i + 1]);
bytes[i] = (byte) (n & 0xff);
}
return new String(bytes);
}
}
运行结果:
导出jar包
添加plugin依赖
在pom.xml中添加如下配置
这里采用的是
maven-assembly-plugin
工具进行导出
随后在terminal中运行mvn package
指令
当看见BUILD SUCCESS的时候就代表jar构建成功啦
此时targe目录下也会出现jar包
接下来使用java -jar java -jar target\sm2test-jar-with-dependencies.jar
指令执行jar包得到以下结果
openEuler中运行jar包
将jar传入openEuler虚拟机中,并使用java -jar java -jar sm2test-jar-with-dependencies.jar
指令执行即可
方法二
在同学那里拷来bouncycastle的资源包后打算试试直接在linux系统命令行下编译运行实现
直接将包导入jdk下的ext拓展包文件夹,然后直接在命令行进行编译运行
创建项目文件夹
创建sm2文件夹,并编写代码文件,(这里以sm2为例):
编译文件
使用命令javac sm2/*.java
进行编译两个文件生成两个class类
运行文件
使用命令java sm2/sm2_demo
运行主类
至于sm3、sm4算法实现在这里不过多赘述。
所遇问题及解决方法
- 在跟着网上教程构建maven环境时要导入bouncycastle的jar包,但是无法进入官网下载地址
后面在根据网上资料了解后发现,idea其实可以通过导入依赖的方法进行导入包,只需在pom.xml中添加
- 在sm4算法实现的时候发现了下面的问题
我发现密文全是0,跟我在python的snowland-smx库中所遇的问题类似,因为sm4分组长度为128bit,后面我尝试着将密文换成了十六位的数字和字母即可啦,这也让我对sm4算法有了更深的印象。
-
导出的包名字不对,后面发现时在pom.xml文件中写的finalName不对,写成了sm2test,随后改成自己预想的名字就像啦
-
我在导出包的时候发生了一个很严重的问题,发生了找不到主类的情况,但是我们从pom.xml依赖中明显可以看出我设置了mainclass为sm2.sm2_demo,随后我们用360压缩打开jar包看一下
我发现没有Main-Class字段,所以我就加上了 Main-Class: sm2.sm2_demo
,但是还是不如意,它发生了如下报错
错误: 无法初始化主类 sm2.sm2_demo
原因: java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider
这到底是怎么回事?我在网上搜了这个报错原因,网上有人说是在Terminal端执行没有idea中导入的bouncycastle相关包,那么问题又回到了原点,我需要下载bouncycastle包,但是我进不去官方网站,并且我在jdk18的jre文件夹下找不到ext文件夹,这个文件夹是存放拓展包的,我没有就算下载下来也不知道放哪,这个问题牵涉着我要换jdk,要重新配idea,重新导出包,意味着从头再来。但是我没有放弃,在后面一篇博客中了解到mvn package执行完之后会生成两个jar包,如下:
而我只有一个sm2test.jar,那我就将问题的研究方向变为了怎么生成dependencies包,我也跟网上找了很多方法都没有作用,后面我决定换一个pom.xml文件,具体如下:
<build>
<finalName>sm2test</finalName><!--修改编译出来的jar包名,仅为{artifactId}.jar-->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.5</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<archive>
<manifest>
<mainClass>
sm2.sm2_demo
</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
从这个pom.xml文件就可以看出来,我加了一项
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
然后就成功生成了 jar-with-dependencies.jar
后面在网上也了解学习到了此插件的一些知识:
此插件使用内置的Assembly Descriptor
要使用maven-assembly-plugin,需要指定至少一个要使用的assembly descriptor 文件。默认情况下,maven-assembly-plugin内置了几个可以用的assembly descriptor:
bin : 类似于默认打包,会将bin目录下的文件打到包中;
jar-with-dependencies : 会将所有依赖都解压打包到生成物中;
src :只将源码目录下的文件打包;
project : 将整个project资源打包。
此外要想生成一个带有依赖的包就应该要执行mvn package assembly:single
,但由于我没有执行,然后我们加上的那段配置代码中就是代替了这段代码的作用,其中大致是创建一个phase容器,但是这个容器必须要有内容呀,那就是goal对象了,也就是single。一个goal可以链接多个phase,也可以不链接,但是phase必须要链接才行。
具体关于maven-assembly-plugin插件的知识可以看看下面这个链接的资料进行学习
反思
在这次库的搭建过程中,虽然有组员提前已经做出来了,但是我所遇到的问题与他们截然不同,都很奇怪,他们能执行的命令我不能执行,我只能用其他的方式来实现同一效果(就比如那个assembly:single我就识别不了),虽然花费了挺多时间但是我觉得还是有所收获的,不管是对于maven的学习还是对于maven-assembly-plugin插件的学习都更加深刻了,也对bouncycastle算法库有了一些了解。但是我觉得自己的自学能力还有待提高,自己应该去探索去提高自己的自学能力,也希望自己未来能在自学能力方面能得到更好的提高吧!