2020_1课程设计—基于BC的证书格式转换工具的设计与实现—Week2
目录
任务要求
- 清楚.pem .pfx /.keystore .crt .cer .der 这些格式的文件用openssl如何产生
- 使用OpenSSL命令行查看证书,并实现证书格式转换
- 清楚哪些格式可以互相转换,并使用BouncyCastle编程实现
Week2 任务安排
- 收集相关资料,学习BouncyCastle的使用方法
- 使用BouncyCastle编程实现证书格式的转换
实践过程
我好像不知道什么是BouncyCastle
- 学java的时候知道,java标准库提供了一系列常用的哈希算法
- 但是呢,如果我们要用的某种算法,Java标准库没有怎么办捏
- 方法一:自己写一个。。。这也太为难我
- 方法二:找一个现成的第三方库,直接使用就好啦
- Bouncy Castle 就是一个这样神奇的提供了很多哈希算法和加密算法的第三方库,提供了Java标准库没有的一些算法,例如RipeMD160哈希算法
- 使用BouncyCastle的目的就是为了扩充算法支持
- ouncy Castle除了提供Provider本身以外,还包括了一个S/MIME和一个open pgp的jar,包只有Provider本身是必要的,后两个包是方便编程而提供的。
- 例如有了S/MIME包后,就不再需要为诸如"加密一个字符串或者一篇文章然后签名"之类的很现实的应用程序写上一大堆代码了,只要引用S/MIME包中的某几个类,很快就可以搞定。
- 而open pgp的存在,使你在用Java编写和PGP/GPG交互的程序时方便多了.
那怎么使用BouncyCastle嘞
- 下载最新加密组件包:The Legion of the Bouncy Castle
- 下载下来的加密组件包有以下两个:
-
bcprov-ext-jdk15to18-165.jar
#用于配置方式使用 -
bcprov-jdk15to18-165.jar
#用于调用方式使用
-
配置方式
通过配置JRE环境,使其作为提供者提供相应的算法支持,在代码层面只需要指定要扩展的算法名称
1、 修改D:\IDEA\Java\jdk\jre\lib\security\java.security
文件
security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider
2、 将文件导入D:\IDEA\Java\jdk\jre\lib\ext
目录下
3、 刚才上面修改了安装目录下的jdk文件夹里的内容,接下来对与jdk同目录下的jre文件夹进行相同的修改与导入
调用方式
1、 必须BouncyCastle提供的jar包bcprov-jdk15to18-165.jar
放到classpath中
2、 Java标准库的java.security包提供了一种标准机制,允许第三方提供商无缝接入。我们要使用BouncyCastle提供的RipeMD160算法,需要先把BouncyCastle注册一下:
2、 在需要使用加密的代码中导入以下两个类
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
3、 在初始化密钥工厂、密钥生成器等引擎前调用如下代码:
//加入BouncyCastleProvider的支持
Security.addProvider(new BouncyCastleProviderrr());
###############或者使用以下方式###############
MessageDigest md = MessageDigest.getInstant("MD4","BC");
//每个提供者都有简称,Bouncy Castle提供者的简称为BC
据说使用BC Provider建CA无敌宇宙巨简单
1、 在所有的此类程序的开头,都都必须有:Security.addProvider(new BouncyCastleProvider())
;
这是为啥捏?将BouncyCaslte的Provider添加到系统中,这样以后系统在运行相关程序的时候调用的就是这个Provider中的加密算法。
2、 建立CA首先要有自己的一对公钥和私钥叭
- 我们使用
KeyPairGenerator
对象就可以噻 - 调用
KeyPairGenerator.getInstance
方法可以根据要生成的密钥类型来产生一个合适的实例,例如常用的RSA等 - 然后调用该对象的
initialize
方法和generateKeyPair
方法就可以产生一个KeyPair
对象 - 然后调用
KeyPair
对象中的相应方法,就可以获取生成的密钥对中的公钥和私钥啦
3、 怎么安全地存储公私钥嘞?
- 通常我们要对这些安全对象,如公钥、私钥、证书等先进行编码
- 编码的目的是为了把结构复杂的安全对象变成字节流以便存储和读取,如DER编码
- 另外,通常还把DER编码后的字节流再次进行base64编码,以便使字节流中所有的字节都变成可打印的字节
- 在Java语言中,这些安全对象基本上都用
getEncoded()
方法,将私钥进行DER编码后保存到一个byte数组中
//公私钥的存储
byte[] priBytes = privateCrtKey.getEncoded();
- 还可以使用BC Provider中的Base64编码解码器类进行编码
//使用Base64再次进行编码
String priData = Base64.encode(priBytes);
-
那如何读取私钥嘞
- 对RSA私钥进行编码就会自动地按照PKCS8进行,因此读取的时候将包含编码的字节数组作为PKCS8EncodedKeySpec对象的构造函数的参数就可以生成一个该类型的对象
- 然后创建一个密钥工厂对象就可以按照要求生成一个RSA私钥
- 这里的DpriBytes应该是和上面的priBytes内容相同.
-
之后使用第一周生成的根证书签发就好啦(这里好像不是重点。。。)
回归到正题:证书转换!
PEM->PFX
前提摘要
- 第一周进行证书转换时,使用命令
openssl pkcs12 -nocerts -nodes -in test.pfx -out test.pem
进行证书转换时,并未导出私钥 - 而且PEM是使用Base64编码
- 但是PFX格式的证书中含有私钥,且使用二进制编码
- 因此需要调用产生证书时偷偷保存的私钥
具体思路
- 用Bouncy Castle转换证书的话,首先应该加载BC哦
Security.addProvider( new BouncyCastleProvider() );
- 既然要转换格式,那肯定得想方设法把PEM证书内容给读出来叭
- 到bouncycastle官网找一些资料,发现
- bouncycastle提供了PemReader/PemWriter可以读写证书
- 那就看看
PemReader
到底是个什么样的方法呗,机智的我找到了具体描述- 首先使用
PemReader
创建个对象 - 再使用其中
readPemObject
方法,读取证书就可以啦
- 首先使用
//使用BC提供的Pemreader读证书
PemReader pemReader = new PemReader( new FileReader( "E:\\认真的侯颖好好看(课内)\\大三下\\课设\\certificate\\test.pem" ) );
PemObject pemObject = pemReader.readPemObject();
byte cert[] = pemObject.getContent();
- PEM证书读出的内容可以直接写入PFX证书嘛,应该是不能
- 我觉得应该用X.509标准给他规范一下
- 果然,输出的就能看懂了
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");//获取工厂实例
Certificate certificate = certificateFactory.generateCertificate(new ByteArrayInputStream(cert));//用文件流读入证书
X509Certificate x509Certificate = (X509Certificate)certificate;
System.out.println(x509Certificate);
- PFX证书里面还有私钥,但是PEM中没有,所以还要读取一波私钥
- 这个过程就和读取证书的过程很像啦
PemReader pemReader_key = new PemReader(new FileReader( "E:\\认真的侯颖好好看(课内)\\大三下\\课设\\certificate\\test_key.pem" ));
PemObject pemObject1 = pemReader_key.readPemObject();
byte[] keyByte = pemObject1.getContent();
- 私钥有了,但也不能干巴巴明明白白写到证书里吧,所以!得加密
//加密私钥
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyByte);//使用给定的编码密钥创建新的 PKCS8EncodedKeySpec
KeyFactory keyFactory = KeyFactory.getInstance("RSA");//创建一个密钥工厂对象就可以按照要求生成一个RSA私钥
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
- 好像需要的东西都准备好啦,这就可以写进PFX证书了呗
-
PFX中有私钥,那肯定不能直接就让你读,所以一般有密码保护,转换时需要输入PFX文件的加密密码
char[] storePwd = "Hy981103".toCharArray();
-
好像还得解决一个证书链的问题
-
X509Certificate[] chain = new X509Certificate[1];//证书链的长度为1
chain[0]= x509Certificate;
-
变身第一步:将给定的证书分配给给定的别名
-
变身第二步:将给定的密钥(已经被保护)分配给给定的别名
-
变身第三步:写入文件
FileOutputStream out = new FileOutputStream( "E:\\认真的侯颖好好看(课内)\\大三下\\课设\\certificate\\PemToPfx.pfx" );
-
变身最后一步:将加密密码和证书绑定起来鸭
关键代码
遇到问题
- 这不写代码不知道,一动手就把以前学的java全还给老师了,好多地方都写得很冗余,完全没有达到“高内聚,低耦合”,而且很多地方写的都很“固定”,比如文件名直接嵌到代码里,不能动态输入等等。
- 而且自己本周的进度较慢,查资料理解代码花了大量的时间,但是在这个过程中收货非常大,对证书格式的理解更加清晰系统。
- 希望接下来一段时间,可以解决代码的冗余问题、更加灵活。
参考链接
类 X509Certificate
Bouncy Castle 配置
BouncyCastle配置