2020_1课程设计—基于BC的证书格式转换工具的设计与实现—第二周进展
本周任务
- 收集相关资料,学习BouncyCastle的使用方法
- 使用BouncyCastle编程实现证书格式的转换
完成情况
BouncyCastle的配置
- 在BC官网下载相关文件
- 将下载的两个jar包拷贝到
$JAVA_HOME$\jre\lib\ext
目录下面
- 修改配置文件
\jre\lib\security\java.security
,在末尾添加security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider
- 测试成功
- 使用时需要在项目中导入jar包
- 然后在代码中导入类
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
- 在初始化密钥工厂、密钥生成器等引擎前调用如下代码
Security.addProvider(new BouncyCastleProvider());
使用BouncyCastle编程实现证书格式的转换
PEM——>PFX
- 因为PEM证书中无私钥,而PFX证书中有加密的公钥和私钥,所以这一步需要调用产生证书时保留的私钥
整体思路
- 使用BC提取PEM证书、私钥,再使用BC生成PFX证书
编程思路
- 先使用BouncyCastle的
PemReader
类- 从PEM证书文件中提取出
PemObject
对象certPEM - 从PEM私钥文件中提取出
PemObject
对象keyPEM
- 从PEM证书文件中提取出
- 从certPEM中提取字节数组,并使用类
java.security.cert.CertificateFactory
,将提取出的的字节数组转化为X509Certificate
对象 - 从keyPEM中提取字节数组,并使用类
KeyFactory
,生成PrivateKey
文件 - 使用BouncyCastle的
PKCS12KeyStore
类,导入加密的密码,和之前生成的私钥、证书对象 - 输出PFX证书
初步代码(功能已实现)
import java.io.*;
import java.lang.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import sun.security.pkcs12.PKCS12KeyStore;
public class PEMToPFX {
public static void main(String[] args) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, InvalidKeySpecException {
{
Security.addProvider( new BouncyCastleProvider() );
PemReader reader = new PemReader( new FileReader( "C:\\Users\\Administrator\\eclipse-workspace\\课程设计_1\\src\\ca.pem" ) );
Object pemObject = reader.readPemObject();
PemObject certPEM = (PemObject)pemObject;
byte b[] = certPEM.getContent();
X509Certificate cert=(X509Certificate)CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(b));
System.out.println(cert);
PemReader reader1 = new PemReader(new FileReader( "C:\\Users\\Administrator\\eclipse-workspace\\课程设计_1\\src\\pfx_pri.pem" ));
Object keyPem =reader1.readPemObject();
PemObject pm = (PemObject) keyPem;
byte[] keyByte = pm.getContent();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyByte);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
char[] StorePasswd = "jinhuan888".toCharArray();
PKCS12KeyStore store = new PKCS12KeyStore();
X509Certificate[] chain = new X509Certificate[1];
chain[0]= cert;
store.engineSetCertificateEntry( "CA's Primary Certificate", cert );
store.engineSetKeyEntry( "CA's Primary Certificate",privateKey, StorePasswd,chain);
FileOutputStream out = new FileOutputStream( "C:\\Users\\Administrator\\eclipse-workspace\\课程设计_1\\src\\ca.pfx" );
store.engineStore( out,StorePasswd );
out.close();
}
}
}
PFX——>PEM
整体思路
- 使用BC提取PFX证书,经过Base64编码,然后使用BC生成PEM证书
编程思路
- 使用BouncyCastle的
PKCS12KeyStore
类,通过密码,加载PFX证书 - 提取
PKCS12KeyStore
的别名alias
- 通过别名
alias
访问其中的证书,并将其转换为X509Certificate
对象 - 使用
X509Certificate
类的getEncode()
方法,获取PEM文件所需的Base64编码 - 使用该Base64编码,生成
PemObject
对象 - 使用BouncyCastle的
PemWriter
类,生成PEM证书
初步代码(功能已实现)
import java.io.*;
import java.lang.*;
import java.security.cert.*;
import java.security.cert.Certificate;
import java.security.*;
import java.util.Enumeration;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
import sun.security.pkcs12.PKCS12KeyStore;
public class PFXToPEM {
public static void main(String[] args) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException {
FileInputStream fs = new FileInputStream( "C:\\Users\\Administrator\\eclipse-workspace\\课程设计_1\\src\\ca.pfx" );
char[] passwd = "jinhuan888".toCharArray();
PKCS12KeyStore store = new PKCS12KeyStore();
store.engineLoad(fs,passwd);
fs.close();
Enumeration enumas = store.engineAliases();
String keyAlias = null;
if (enumas.hasMoreElements())
{
keyAlias = (String)enumas.nextElement();
System.out.println("alias=[" + keyAlias + "]");
}
Certificate cert = store.engineGetCertificate(keyAlias);
X509Certificate certificate = (X509Certificate)cert;
System.out.println(certificate);
PemObject pemCSR = new PemObject("CERTIFICATE", certificate.getEncoded());
StringWriter str = new StringWriter();
PemWriter pemWriter = new PemWriter(str);
pemWriter.writeObject(pemCSR);
pemWriter.close();
str.close();
FileOutputStream certOut = new FileOutputStream("C:\\Users\\Administrator\\eclipse-workspace\\课程设计_1\\src\\pfx_ca.cer");
certOut.write(str.toString().getBytes());
}
}
PEM——>DER
整体思路
- 使用BC提取PEM证书,将其字节数组输出到DER文件
编程思路
- 使用BC的
PemReader
类提取PEM证书,获得PemObject
对象 - 从
PemObject
对象中提取字节数组 - 将字节数组输出到DER文件,得到DER证书
初步代码(功能已实现)
import java.io.*;
import java.lang.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.*;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import sun.security.pkcs12.PKCS12KeyStore;
import sun.security.util.DerOutputStream;
public class PEMToDER_2 {
public static void main(String[] args) throws IOException {
{
Security.addProvider( new BouncyCastleProvider() );
PemReader reader = new PemReader( new FileReader( "C:\\Users\\Administrator\\eclipse-workspace\\课程设计_1\\src\\ca.pem" ) );
Object pemObject = reader.readPemObject();
PemObject p = (PemObject)pemObject;
byte b[] = p.getContent();
File file=new File("C:\\Users\\Administrator\\eclipse-workspace\\课程设计_1\\src\\ca.der");
if(!file.exists()){
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
fos.write(b);
fos.close();
}
}
}
DER——>PEM
整体思路
- 提取DER证书字节数组,使用BC生成PEM证书
编程思路
- 读取DER证书的字节数组
- 使用该字节数组生成
PemObject
对象 - 使用BC的
PemWriter
类,输出PEM文件
初步代码(功能已实现)
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
import java.io.*;
public class DERToPEM {
public static void main(String[] args) throws IOException {
File src = new File("C:\\Users\\Administrator\\eclipse-workspace\\课程设计_1\\src\\ca.der");
byte[] datas = null;
try (InputStream is = new BufferedInputStream(new FileInputStream(src));
ByteArrayOutputStream baos = new ByteArrayOutputStream();) {
byte[] flush = new byte[1024];
int len = -1;
while ((len = is.read(flush)) != -1) {
baos.write(flush, 0, len);
}
baos.flush();
datas = baos.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
PemObject pemCSR = new PemObject("CERTIFICATE", datas);
StringWriter str = new StringWriter();
PemWriter pemWriter = new PemWriter(str);
pemWriter.writeObject(pemCSR);
pemWriter.close();
str.close();
FileOutputStream certOut = new FileOutputStream("C:\\Users\\Administrator\\eclipse-workspace\\课程设计_1\\src\\der_ca.cer");
certOut.write(str.toString().getBytes());
}
}
P7B——>PEM
整体思路
- 读取P7B证书的证书链,从证书链中获取
X509Certificate
证书,再使用BC生成PEM证书
编程思路
- 读取P7B证书的字节数组
- 从该字节数组中提取证书链
- 使用BC的
PemWriter
类,输出PEM文件(方法同之前)、
初步代码(功能已实现)
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
import java.io.*;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class P7BToPEM {
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
X509Certificate cert;
File file = new File("C:\\Users\\Administrator\\eclipse-workspace\\课程设计_1\\src\\ca.p7b");
byte[] buffer = new byte[(int) file.length()];
DataInputStream in = new DataInputStream(new FileInputStream(file));
in.readFully(buffer);
in.close();
Certificate[] chain = readCertificatesFromPKCS7(buffer);
cert = (X509Certificate) chain[0];
System.out.println(cert);
PemObject pemCSR = new PemObject("CERTIFICATE", cert.getEncoded());
StringWriter str = new StringWriter();
PemWriter pemWriter = new PemWriter(str);
pemWriter.writeObject(pemCSR);
pemWriter.close();
str.close();
FileOutputStream certOut = new FileOutputStream("C:\\Users\\Administrator\\eclipse-workspace\\课程设计_1\\src\\p7b_ca.cer");
certOut.write(str.toString().getBytes());
}
public static final Certificate[] readCertificatesFromPKCS7(byte[] binaryPKCS7Store) throws Exception
{
try (ByteArrayInputStream bais = new ByteArrayInputStream(binaryPKCS7Store))
{
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Collection<?> c = cf.generateCertificates(bais);
List<Certificate> certList = new ArrayList<Certificate>();
if (c.isEmpty())
{
// If there are now certificates found, the p7b file is probably not in binary format.
// It may be in base64 format.
// The generateCertificates method only understands raw data.
}
else
{
Iterator<?> i = c.iterator();
while (i.hasNext())
{
certList.add((Certificate) i.next());
}
}
Certificate[] certArr = new Certificate[certList.size()];
return certList.toArray(certArr);
}
}
}
遇到的问题与解决过程
问题1:在提取PEM文件到X509Certificate
对象的过程中,尝试直接强转,报错
解决1:先生成PemObject
对象,提取字节数组,并使用CertificateFactory
进行转换
问题2:加载PKCS12KeyStore
对象时,发现缺少私钥
解决2:PEM文件中没有私钥,需要读入生成证书时保存的私钥文件中的私钥
问题3:在读取DER文件时,刚开始直接进行读取,在转为字节数组,出现转换错误的问题
解决3:使用ByteArrayInputStream
、ByteArrayOutputStream
类读取字节数组
问题4:无法将PEM证书转换为P7B证书
尚未解决:因为证书链操作缺少方法,并且搜索不到相关资料
未来展望
- 探索更多的格式转换方式
- 完善已有代码,将代码写的更整洁,更符合java代码规范