引入:

在密码学中,证书是一个非常重要的概念,我这里不想展开了, 通常的证书都是基于X.509规范的,有兴趣的同学可以去看对应介绍:

实践:

其实证书无处不在,我们的浏览器里面一般都会可以看到一些证书,有些是自动添加进去的,有些可以手动添加进去,比如我自己机器上用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读取出来的信息是完全一致的。