/* * Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved. * See LICENCE file for licensing information. */ package eu.emi.security.authn.x509.impl; import java.math.BigInteger; import java.security.PublicKey; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.security.interfaces.DSAPublicKey; import java.security.interfaces.RSAPublicKey; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import static eu.emi.security.authn.x509.impl.FormatMode.*; /** * Utility allowing for converting certificates to various text representations. * * @author K. Benedyczak */ public class X509Formatter { static { CertificateUtils.configureSecProvider(); } private final FormatMode mode; private static final String[] USAGES = { "digitalSignature", "nonRepudiation", "keyEncipherment", "dataEncipherment", "keyAgreement", "keyCertSign", "CRLSign", "encipherOnly", "decipherOnly"}; private static final String EKU = "1.3.6.1.5.5.7.3."; private static final Map<String, String> EXT_USAGES = new HashMap<String, String>(16); static { EXT_USAGES.put("2.5.29.37.0", "anyExtendedKeyUsage"); EXT_USAGES.put(EKU+"1", "serverAuth"); EXT_USAGES.put(EKU+"2", "clientAuth"); EXT_USAGES.put(EKU+"3", "codeSigning"); EXT_USAGES.put(EKU+"4", "emailProtection"); EXT_USAGES.put(EKU+"5", "ipsecEndSystem"); EXT_USAGES.put(EKU+"6", "ipsecTunnel"); EXT_USAGES.put(EKU+"7", "ipsecUser"); EXT_USAGES.put(EKU+"8", "timeStamping"); EXT_USAGES.put(EKU+"9", "OCSPSigning"); EXT_USAGES.put(EKU+"10", "dvcs"); EXT_USAGES.put(EKU+"11", "sbgpCertAAServerAuth"); EXT_USAGES.put(EKU+"12", "scvp_responder"); EXT_USAGES.put(EKU+"13", "eapOverPPP"); EXT_USAGES.put(EKU+"14", "eapOverLAN"); EXT_USAGES.put(EKU+"15", "scvpServer"); EXT_USAGES.put(EKU+"16", "scvpClient"); EXT_USAGES.put(EKU+"17", "ipsecIKE"); EXT_USAGES.put(EKU+"18", "capwapAC"); EXT_USAGES.put(EKU+"19", "capwapWTP"); EXT_USAGES.put("1.3.6.1.4.1.311.20.2.2", "smartcardlogon"); }; /** * Creates a new X509Formatter object * @param mode the formatting mode that will be used by this object. */ public X509Formatter(FormatMode mode) { this.mode = mode; } /** * Produces a human readable text representation of the provided certificate. * @param cert input certificate * @return the text representation */ public String format(X509Certificate cert) { String sep = "\n"; if (mode.equals(COMPACT_ONE_LINE) || mode.equals(MEDIUM_ONE_LINE)) sep = ", "; StringBuilder sb = new StringBuilder(256); String subject = X500NameUtils.getReadableForm( cert.getSubjectX500Principal()); String issuer = X500NameUtils.getReadableForm( cert.getIssuerX500Principal()); int version = cert.getVersion(); sb.append(cert.getType()).append(" v").append(version); sb.append(" certificate").append(sep); sb.append("Subject: ").append(subject).append(sep); sb.append("Issuer: ").append(issuer); if (mode.equals(COMPACT) || mode.equals(COMPACT_ONE_LINE)) return sb.toString(); sb.append(sep); sb.append("Valid from: " + cert.getNotBefore()).append(sep); sb.append("Valid to: " + cert.getNotAfter()); if (mode.equals(MEDIUM) || mode.equals(MEDIUM_ONE_LINE)) return sb.toString(); sb.append(sep); Collection<List<?>> issuerAltNames; Collection<List<?>> subjAltNames; List<String> extKeyUsage; try { issuerAltNames = cert.getIssuerAlternativeNames(); subjAltNames = cert.getSubjectAlternativeNames(); extKeyUsage = cert.getExtendedKeyUsage(); } catch (CertificateParsingException e) { throw new IllegalArgumentException( "The certificate can not be sucessfuly parsed", e); } if (issuerAltNames != null) appendAltNames(sb, "Issuer alternative names", sep, issuerAltNames); if (subjAltNames != null) appendAltNames(sb, "Subject alternative names", sep, subjAltNames); boolean isCA = cert.getBasicConstraints() == Integer.MAX_VALUE; sb.append("CA: ").append(isCA).append(sep); PublicKey pubKey = cert.getPublicKey(); String bits = ""; if (pubKey instanceof RSAPublicKey) bits = " " + ((RSAPublicKey)pubKey).getModulus().bitLength() + "bit"; if (pubKey instanceof DSAPublicKey) bits = " " + ((DSAPublicKey)pubKey).getParams().getG().bitLength() + "bit"; String sigAlg = cert.getSigAlgName(); sb.append("Signature alg: ").append(sigAlg).append(sep); sb.append("Public key type: ").append(pubKey.getAlgorithm()).append(bits).append(sep); boolean []keyUsage = cert.getKeyUsage(); if (keyUsage != null) { sb.append("Allowed usage:"); for (int i=0; i<keyUsage.length; i++) if (keyUsage[i]) sb.append(" ").append(USAGES[i]); sb.append(sep); } if (extKeyUsage != null) { sb.append("Allowed extended usage:"); for (String oid: extKeyUsage) { String val = EXT_USAGES.get(oid); if (val == null) val = oid; sb.append(" ").append(val); } sb.append(sep); } BigInteger serial = cert.getSerialNumber(); sb.append("Serial number: ").append(serial); return sb.toString(); } private void appendAltNames(StringBuilder sb, String info, String sep, Collection<List<?>> altNames) { sb.append(info).append(": ").append(sep); for (List<?> altNamesL: altNames) { sb.append(" "); Integer i = (Integer) altNamesL.get(0); Object rVal = altNamesL.get(1); String val; if (i == 0 || i == 3 || i == 5) val = Arrays.toString((byte[])rVal); else val = (String) rVal; switch (i) { case 1: sb.append("email: ").append(val).append(sep); break; case 2: sb.append("DNS: ").append(val).append(sep); break; case 4: sb.append("DN: ").append(val).append(sep); break; case 6: sb.append("URI: ").append(val).append(sep); break; case 7: sb.append("IP: ").append(val).append(sep); break; case 8: sb.append("OID: ").append(val).append(sep); break; case 0: sb.append("other: ").append(val).append(sep); break; case 3: sb.append("X.400: ").append(val).append(sep); break; case 5: sb.append("EDI party: ").append(val).append(sep); break; } } } /** * Produces a human readable text representation of the provided certificate chain. * @param certChain input certificates * @return the text representation */ public String format(X509Certificate[] certChain) { return format(certChain, true); } /** * Produces a human readable text representation of the provided certificate chain. * @param certChain input certificates * @param preamble whether to print a first line with an information on * the number of elements. * @return the text representation */ public String format(X509Certificate[] certChain, boolean preamble) { StringBuilder sb = new StringBuilder(); if (preamble) sb.append("Certificate chain, ").append(certChain.length). append(" elements:\n"); for (int i=0; i<certChain.length; i++) { sb.append("-----Certificate ").append(i+1).append("-----\n"); sb.append(format(certChain[i])).append("\n"); if (i+1<certChain.length) sb.append("\n"); } return sb.toString(); } }