Java tutorial
/* * 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(); } }