引入:
在密码学中,证书是一个非常重要的概念,我这里不想展开了, 通常的证书都是基于X.509规范的,有兴趣的同学可以去看对应介绍:http://en.wikipedia.org/wiki/X509
实践:
其实证书无处不在,我们的浏览器里面一般都会可以看到一些证书,有些是自动添加进去的,有些可以手动添加进去,比如我自己机器上用Chrome:在chrome://settings/advanced里面
你往下看到HTTPS/SSL ,点击Manage Certificates...按钮: 就会看出被管理的证书列表:
我们选出其中某个证书,比如Alibaba.com,然后导出到本地,然后用java提供的Certificate类来分析这个证书。
为了分析证书,我们写了一个工具类:
package com.charles.certificatestudy; import java.io.FileInputStream; import java.math.BigInteger; import java.security.PublicKey; import java.security.cert.CRL; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Date; import javax.security.auth.x500.X500Principal; import sun.misc.BASE64Encoder; /** * * Description: 这个工具类提供了证书的一般操作 * * @author charles.wang * @created Oct 29, 2013 2:57:58 PM * */ public class CertificateUtil { public static X509Certificate getX509certFromCertificatePath(String certificateName) throws Exception{ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); // 获得证书文件的输入流 FileInputStream in = new FileInputStream(certificateName); // 获得证书 Certificate certificate = certificateFactory.generateCertificate(in); // 获取证书的类型 String certType = certificate.getType(); System.out.println("证书类型:" + certType); X509Certificate x509cert = (X509Certificate) certificate; // 关闭流 in.close(); return x509cert; } /** * 分析证书文件 * @param certficateName 被分析的证书路径名 * @throws Exception */ public static void parseX509Certificate(X509Certificate x509cert) throws Exception { // 开始用证书的API提取相关信息: // 读取证书版本号,这个证书的版本号识别用于该证书的X.509标准的版本,它可以用来影响证书所能指定的信息 // 迄今为止,已经定义了3个版本 int version = x509cert.getVersion(); System.out.println("\n证书版本号为:" + version); // 读取证书序列号 BigInteger serialNumber = x509cert.getSerialNumber(); System.out.println("\n证书序列号为:" + (new BASE64Encoder()).encode(serialNumber.toByteArray())); // 读取证书的签名算法名,CA用此算法来签名证书 String algName = x509cert.getSigAlgName(); System.out.println("\n证书的签名算法名为:" + algName); // 获得证书的签发者,其名字采用X.500标准,并且给出信息 // 这个证书的签发者通常是一个CA,使用该证书意味着信任签写该证书的实体 X500Principal issuerPrincipal = x509cert.getIssuerX500Principal(); System.out.println("\n证书的发布者为:" + issuerPrincipal.getName()); // 读取出证书的有效期 Date notAfter = x509cert.getNotAfter(); Date notBefore = x509cert.getNotBefore(); System.out.println("\n证书的有效期为:" + notBefore.toLocaleString() + " 之后," + notAfter.toLocaleString() + " 之前"); // 读取证书的主体,它代表的是公钥的实体,其名字仍然使用X.500标准 X500Principal subjectPrincipal = x509cert.getSubjectX500Principal(); System.out.println("证书主题为:" + subjectPrincipal.getName()); // 读取证书的公钥 PublicKey publicKey = x509cert.getPublicKey(); System.out.println("\n获取证书的公钥信息"); System.out.println("证书的公钥的算法为:" + publicKey.getAlgorithm()); System.out.println("证书公钥的格式为:" + publicKey.getFormat()); // 获得公钥的字节数组 byte[] publicKeyBytes = publicKey.getEncoded(); System.out.println("证书公钥为:" + (new BASE64Encoder()).encode(publicKeyBytes)); // 读取证书的基本约束 System.out.println("\n证书路径的长度为:" + x509cert.getBasicConstraints()); // 证书所含公钥所能完成的功能或者服务 boolean[] keyUsages = x509cert.getKeyUsage(); // KeyUsage ::= BIT STRING { // digitalSignature (0), // nonRepudiation (1), // keyEncipherment (2), // dataEncipherment (3), // keyAgreement (4), // keyCertSign (5), // cRLSign (6), // encipherOnly (7), // decipherOnly (8) } if (keyUsages[0]) System.out.println("此证书的公钥可以用来数字签名"); if (keyUsages[1]) System.out.println("此证书的公钥具有不可否认性"); if (keyUsages[2]) System.out.println("此证书的公钥可以用于加密"); if (keyUsages[3]) System.out.println("此证书的公钥用于将用户数据加密"); if (keyUsages[4]) System.out.println("此证书的公钥用于密钥协议"); if (keyUsages[5]) System.out.println("此证书的公钥用于验证在证书上的签名"); if (keyUsages[6]) System.out.println("此证书的公钥用于验证有关撤销消息"); if (keyUsages[7]) System.out.println("此证书的公钥只可以用于加密,并且履行密钥协议"); if (keyUsages[8]) System.out.println("此证书的公钥只可以用于解密,并且履行密钥协议"); // 读取证书的签名算法的OID字符串 String algOIDString = x509cert.getSigAlgOID(); System.out.println("\n证书的签名算法OID字符串为:" + algOIDString); x509cert.getSigAlgParams(); // 读取证书的签名值 byte[] certSignature = x509cert.getSignature(); System.out.println("\n证书的签名值为:" + (new BASE64Encoder()).encode(certSignature)); x509cert.getSubjectAlternativeNames(); // 读取证书的DER编码的二进制证书信息 byte[] tbsCertificate = x509cert.getTBSCertificate(); System.out.println("\n证书的DER编码的二进制证书信息为:" + (new BASE64Encoder()).encode(tbsCertificate)); } /** * 获取证书的撤销列表 * @param certificateName * @return * @throws Exception */ public static CRL getCRLForCertifate(String certificateName) throws Exception { //实例化证书,并且指定证书的类型是X.509 CertificateFactory certifateFactory = CertificateFactory.getInstance("X.509"); //获取证书的输入流 FileInputStream in = new FileInputStream(certificateName); //获取证书的撤销列表 CRL crl = certifateFactory.generateCRL(in); in.close(); return crl; } }
然后我们写一个测试类来测试这些方法,它会先读取证书文件,然后把其中的信息分离出来:
package com.charles.certificatestudy; import java.security.cert.X509Certificate; import sun.misc.BASE64Encoder; /** * * Description: 这个类用于演示Certificate的一般使用 * * @author charles.wang * @created Oct 29, 2013 12:03:51 PM * */ public class CertificateDemo { /** * @param args */ public static void main(String[] args) throws Exception { String certificateFilePath="alibaba.cer"; //获取证书对象 X509Certificate x509cert=CertificateUtil.getX509certFromCertificatePath(certificateFilePath); //分析证书 CertificateUtil.parseX509Certificate(x509cert); } }
我们运行例子,测试,会打印出指定的证书信息:
我们比较选择的证书文件:
可以看到,这信息和我们用API读取出来的信息是完全一致的。