mitm.common.security.certificate.X509CertificateInspector.java Source code

Java tutorial

Introduction

Here is the source code for mitm.common.security.certificate.X509CertificateInspector.java

Source

/*
 * Copyright (c) 2008-2012, Martijn Brinkers, Djigzo.
 * 
 * This file is part of Djigzo email encryption.
 *
 * Djigzo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License 
 * version 3, 19 November 2007 as published by the Free Software 
 * Foundation.
 *
 * Djigzo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public 
 * License along with Djigzo. If not, see <http://www.gnu.org/licenses/>
 *
 * Additional permission under GNU AGPL version 3 section 7
 * 
 * If you modify this Program, or any covered work, by linking or 
 * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, 
 * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, 
 * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, 
 * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, 
 * wsdl4j-1.6.1.jar (or modified versions of these libraries), 
 * containing parts covered by the terms of Eclipse Public License, 
 * tyrex license, freemarker license, dom4j license, mx4j license,
 * Spice Software License, Common Development and Distribution License
 * (CDDL), Common Public License (CPL) the licensors of this Program grant 
 * you additional permission to convey the resulting work.
 */
package mitm.common.security.certificate;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import mitm.common.security.digest.Digest;
import mitm.common.security.digest.Digests;
import mitm.common.util.BigIntegerUtils;

import org.apache.commons.lang.text.StrBuilder;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;

/**
 * Convenience class wrapping a X509Certificate to have some 'higher level' getters
 * which are not directly supported by the X509Certificate class. 
 * 
 * @author Martijn Brinkers
 *
 */
public class X509CertificateInspector extends X509ExtensionInspector {
    private final X509Certificate certificate;

    private final AltNamesInspector subjectAltNames;
    private final X500PrincipalInspector subjectInspector;
    private final X500PrincipalInspector issuerInspector;

    public X509CertificateInspector(X509Certificate certificate) throws CertificateParsingException, IOException {
        super(certificate);

        this.certificate = certificate;

        subjectAltNames = new AltNamesInspector(certificate);
        subjectInspector = new X500PrincipalInspector(certificate.getSubjectX500Principal());
        issuerInspector = new X500PrincipalInspector(certificate.getIssuerX500Principal());
    }

    /**
     * Returns the serial number as a String (to get the BigInteger serial number use 
     * X509Certificate directly)
     * @return the serial number as hex string
     */
    public String getSerialNumberHex() {
        return getSerialNumberHex(this.certificate);
    }

    /**
     * Returns the serial number as a String (to get the BigInteger serial number use 
     * X509Certificate directly)
     * @param certificate
     * @return
     */
    public static String getSerialNumberHex(X509Certificate certificate) {
        return BigIntegerUtils.hexEncode(certificate.getSerialNumber());
    }

    /**
     * Returns the issuer DN in a canonical RFC2253 format
     * @return the issuer DN in a canonical RFC2253 format
     */
    public String getIssuerCanonical() {
        return getIssuerCanonical(this.certificate);
    }

    /**
     * Returns the issuer DN in a canonical RFC2253 format
     * @param certificate
     * @return
     */
    public static String getIssuerCanonical(X509Certificate certificate) {
        return X500PrincipalInspector.getCanonical(certificate.getIssuerX500Principal());
    }

    /**
     * Returns the issuer DN in a friendly format
     * @return the issuer DN in a friendly format
     */
    public String getIssuerFriendly() {
        return getIssuerFriendly(this.certificate);
    }

    /**
     * Returns the issuer DN in a friendly format
     * @param certificate
     * @return
     */
    public static String getIssuerFriendly(X509Certificate certificate) {
        return X500PrincipalInspector.getFriendly(certificate.getIssuerX500Principal());
    }

    /**
     * Returns the issuer as X500Name
     * @return
     */
    public X500Name getIssuerX500Name() {
        return issuerInspector.getX500Name();
    }

    /**
     * Returns the subject DN in a canonical RFC2253 format
     * @return the subject DN in a canonical RFC2253 format
     */
    public String getSubjectCanonical() {
        return getSubjectCanonical(this.certificate);
    }

    /**
     * Returns the subject DN in a canonical RFC2253 format
     * @param certificate
     * @return
     */
    public static String getSubjectCanonical(X509Certificate certificate) {
        return X500PrincipalInspector.getCanonical(certificate.getSubjectX500Principal());
    }

    /**
     * Returns the subject DN in a friendly format
     * @return the subject DN in a friendly format
     */
    public String getSubjectFriendly() {
        return getSubjectFriendly(this.certificate);
    }

    /**
     * Returns the subject DN in a friendly format
     * @param certificate
     * @return
     */
    public static String getSubjectFriendly(X509Certificate certificate) {
        return X500PrincipalInspector.getFriendly(certificate.getSubjectX500Principal());
    }

    /**
     * Returns the subject as X500Name
     * @return
     */
    public X500Name getSubjectX500Name() {
        return subjectInspector.getX500Name();
    }

    /**
     * Returns all the RFC822 items from the altNames. Duplicate
     * items are possible and there is no guarantee that the
     * email addresses are valid email addresses
     * @return list of RFC822 strings
     */
    public List<String> getEmailFromAltNames() {
        return new ArrayList<String>(subjectAltNames.getRFC822Names());
    }

    /**
     * Returns all the EmailAddress items from the subject DN. Duplicate
     * items are possible and there is no guarantee that the
     * email addresses are valid email addresses
     * @return list of EmailAddress strings
     */
    public List<String> getEmailFromDN() {
        return subjectInspector.getEmail();
    }

    /**
     * Returns all the email addresses from the certificate (DN and AltName
     * combined). Duplicate items are possible and there is no guarantee 
     * that the email addresses are valid email addresses
     * @return list of email addresses strings
     */
    public List<String> getEmail() {
        List<String> addresses = new LinkedList<String>();

        addresses.addAll(getEmailFromAltNames());
        addresses.addAll(getEmailFromDN());

        return addresses;
    }

    /**
     * Returns a (possibly empty) set of key usages of the given certificate
     */
    public Set<KeyUsageType> getKeyUsage() {
        return getKeyUsage(this.certificate);
    }

    /**
     * Returns a (possibly empty) set of key usages of the given certificate or
     * null if there are no KeyUsages.
     */
    public static Set<KeyUsageType> getKeyUsage(X509Certificate certificate) {
        Set<KeyUsageType> keyUsages = null;

        boolean[] keyUsageArray = certificate.getKeyUsage();

        if (keyUsageArray != null) {
            keyUsages = new HashSet<KeyUsageType>();

            for (int tag = 0; tag < keyUsageArray.length; tag++) {
                if (keyUsageArray[tag] == true) {
                    KeyUsageType keyUsage = KeyUsageType.fromTag(tag);

                    if (keyUsage != null) {
                        keyUsages.add(keyUsage);
                    }
                }
            }
        }

        return keyUsages;
    }

    /**
     * Returns a (possibly empty) set of extended key usage objects. 
     * @throws CertificateParsingException
     */
    public Set<ExtendedKeyUsageType> getExtendedKeyUsage() throws CertificateParsingException {
        return getExtendedKeyUsage(this.certificate);
    }

    /**
     * Returns a (possibly empty) set of extended key usage objects, null if there are
     * no extended key usages. 
     * @throws CertificateParsingException
     */
    public static Set<ExtendedKeyUsageType> getExtendedKeyUsage(X509Certificate certificate)
            throws CertificateParsingException {
        Set<ExtendedKeyUsageType> extendedKeyUsages = null;

        List<String> extendedKeyUsageOIDs = certificate.getExtendedKeyUsage();

        if (extendedKeyUsageOIDs != null) {
            extendedKeyUsages = new HashSet<ExtendedKeyUsageType>();

            for (String oid : extendedKeyUsageOIDs) {
                ExtendedKeyUsageType extKeyUsage = ExtendedKeyUsageType.fromOID(oid);

                if (extKeyUsage != null) {
                    extendedKeyUsages.add(extKeyUsage);
                }
            }
        }

        return extendedKeyUsages;
    }

    /**
     * Calculates the thumbprint of the certificate using the given digest algorithm. 
     * @param digest
     * @return
     * @throws CertificateEncodingException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     */
    public String getThumbprint(Digest digest)
            throws CertificateEncodingException, NoSuchAlgorithmException, NoSuchProviderException {
        return CertificateInspector.getThumbprint(this.certificate, digest);
    }

    /**
     * Calculates the thumbprint of the certificate using the given digest algorithm. 
     * @param certificate
     * @param digest
     * @return
     * @throws CertificateEncodingException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     */
    public static String getThumbprint(Certificate certificate, Digest digest)
            throws CertificateEncodingException, NoSuchAlgorithmException, NoSuchProviderException {
        return CertificateInspector.getThumbprint(certificate, digest);
    }

    /**
     * Calculates the thumbprint of the certificate using SHA-512. 
     * @return Hex representation of the SHA-512 thumbprint
     * @throws CertificateEncodingException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     */
    public String getThumbprint()
            throws CertificateEncodingException, NoSuchAlgorithmException, NoSuchProviderException {
        return CertificateInspector.getThumbprint(certificate);
    }

    /**
     * Calculates the thumbprint of the certificate using SHA-512. 
     * @param certificate
     * @return
     * @throws CertificateEncodingException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     */
    public static String getThumbprint(Certificate certificate)
            throws CertificateEncodingException, NoSuchAlgorithmException, NoSuchProviderException {
        return CertificateInspector.getThumbprint(certificate);
    }

    /**
     * Returns true if the certificate is self signed ie. subject == issuer.
     */
    public static boolean isSelfSigned(X509Certificate certificate) {
        return certificate.getSubjectX500Principal().equals(certificate.getIssuerX500Principal());
    }

    /**
     * Returns true if the certificate is self signed ie. subject == issuer.
     */
    public boolean isSelfSigned() {
        return certificate.getSubjectX500Principal().equals(certificate.getIssuerX500Principal());
    }

    /**
     * Returns true if the current date falls outside the validity date of the certificate.
     */
    public boolean isExpired() {
        return isExpired(certificate);
    }

    /**
     * Returns true if the current date falls outside the validity date of the certificate.
     */
    public static boolean isExpired(X509Certificate certificate) {
        boolean expired = true;

        try {
            certificate.checkValidity();

            expired = false;
        } catch (CertificateExpiredException e) {
            /* ignored */
        } catch (CertificateNotYetValidException e) {
            /* ignored */
        }

        return expired;
    }

    /**
     * Returns true if the certificate is a CA certificate (returns isCA from the basic constraints)
     */
    public boolean isCA() throws IOException {
        BasicConstraints bc = getBasicConstraints(certificate);

        return bc != null && bc.isCA();
    }

    /**
     * Returns true if the certificate is a CA certificate (returns isCA from the basic constraints)
     */
    public static boolean isCA(X509Certificate certificate) throws IOException {
        BasicConstraints bc = getBasicConstraints(certificate);

        return bc != null && bc.isCA();
    }

    /**
     * Generates a SubjectKeyIdentifier by calculating the SHA1 hash over the BIT STRING
     * from SubjectPublicKeyInfo as defined in RFC2459.
     */
    public SubjectKeyIdentifier calculateSubjectKeyIdentifier() throws NoSuchAlgorithmException {
        return calculateSubjectKeyIdentifier(certificate);
    }

    /**
     * Generates a SubjectKeyIdentifier by calculating the SHA1 hash over the BIT STRING
     * from SubjectPublicKeyInfo as defined in RFC2459.
     * @throws NoSuchAlgorithmException 
     */
    public static SubjectKeyIdentifier calculateSubjectKeyIdentifier(X509Certificate certificate)
            throws NoSuchAlgorithmException {
        return new JcaX509ExtensionUtils().createSubjectKeyIdentifier(certificate.getPublicKey());
    }

    /**
     * Generates a SubjectKeyIdentifier by calculating the SHA1 hash over encoded public key. Outlook 2010
     * uses this method to calculate the SubjectKeyIdentifier when the certificate does not have a 
     * SubjectKeyIdentifier.
     * 
     * Note: this is not RFC compliant! and is a Microsoft invention.
     *  
     * See https://bugzilla.mozilla.org/show_bug.cgi?id=559243 and 
     * http://www.ietf.org/mail-archive/web/smime/current/msg18730.html for more information why this is needed for
     * messages sent by Outlook 2010
     */
    public byte[] calculateSubjectKeyIdentifierMicrosoft() throws IOException {
        return calculateSubjectKeyIdentifierMicrosoft(certificate);
    }

    /**
     * Generates a SubjectKeyIdentifier by calculating the SHA1 hash over encoded public key. Outlook 2010
     * uses this method to calculate the SubjectKeyIdentifier when the certificate does not have a 
     * SubjectKeyIdentifier.
     * 
     * Note: this is not RFC compliant! and is a Microsoft invention.
     *  
     * See https://bugzilla.mozilla.org/show_bug.cgi?id=559243 and 
     * http://www.ietf.org/mail-archive/web/smime/current/msg18730.html for more information why this is needed for
     * messages sent by Outlook 2010
     */
    public static byte[] calculateSubjectKeyIdentifierMicrosoft(X509Certificate certificate) throws IOException {
        try {
            return Digests.digest(certificate.getPublicKey().getEncoded(), Digest.SHA1);
        } catch (NoSuchAlgorithmException e) {
            throw new IOException(e);
        } catch (NoSuchProviderException e) {
            throw new IOException(e);
        }
    }

    /**
     * Returns a string representation of the certificate which can be used for logging etc.
     */
    @Override
    public String toString() {
        return toString(certificate);
    }

    /**
     * Returns a string representation of the certificate which can be used for logging etc.
     */
    public static String toString(X509Certificate certificate) {
        StrBuilder sb = new StrBuilder(1024);

        sb.append("Issuer: ");
        sb.append(getIssuerFriendly(certificate));
        sb.appendSeparator("; ");
        sb.append("Subject: ");
        sb.append(getSubjectFriendly(certificate));
        sb.appendSeparator("; ");
        sb.append("Serial: ");
        sb.append(getSerialNumberHex(certificate));
        sb.appendSeparator("; ");
        sb.append("Thumbprint: ");
        try {
            sb.append(getThumbprint(certificate));
        } catch (CertificateEncodingException e) {
            /* ignored */
        } catch (NoSuchAlgorithmException e) {
            /* ignored */
        } catch (NoSuchProviderException e) {
            /* ignored */
        }
        sb.appendSeparator("; ");
        sb.append("SHA1: ");
        try {
            sb.append(getThumbprint(certificate, Digest.SHA1));
        } catch (CertificateEncodingException e) {
            /* ignored */
        } catch (NoSuchAlgorithmException e) {
            /* ignored */
        } catch (NoSuchProviderException e) {
            /* ignored */
        }

        return sb.toString();
    }
}