2020_1课程设计—基于BC的证书格式转换工具的设计与实现—第二周进展

本周任务

  • 收集相关资料,学习BouncyCastle的使用方法
  • 使用BouncyCastle编程实现证书格式的转换

完成情况

BouncyCastle的配置

  • 将下载的两个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
  • 从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:使用ByteArrayInputStreamByteArrayOutputStream类读取字节数组

问题4:无法将PEM证书转换为P7B证书

尚未解决:因为证书链操作缺少方法,并且搜索不到相关资料

未来展望

  • 探索更多的格式转换方式
  • 完善已有代码,将代码写的更整洁,更符合java代码规范
posted @ 2020-04-23 11:37  20175217wyf  阅读(495)  评论(0编辑  收藏  举报