Občas se stane, že si při práci s X509 certifikáty nevystačíte jen s možnostmi, které nabízí balík java.security.cert a rádi byste vydolovali více informací. Já jsem například řešil požadavek na získání CRL (Certificate Revocation List) souboru pro zadaný certifikát. V takovýchto případech se hodí využít kryptografickou knihovnu Bouncy Castle a pomocí ní vypreparovat třeba i střeva z vašich certifikátů.
Jak tedy implementace vypadá? Nejdříve zjistíme, zda certifikát vůbec podporuje rozšíření CRLDistributionPoints a jesliže ano, máme vyhráno. Knihovna Bouncy Castle obsahuje už připravené třídy přímo pro toto rozšíření. Stačí se tedy ponořit do stromových struktur distribučních bodů a získat tak seznam URL, na kterých by měly být připraveny ke stažení CRL soubory.
import java.io.IOException; import java.security.KeyStore; import java.security.cert.X509Certificate; import java.util.HashSet; import java.util.Set; import org.bouncycastle.asn1.DERString; import org.bouncycastle.asn1.x509.CRLDistPoint; import org.bouncycastle.asn1.x509.DistributionPoint; import org.bouncycastle.asn1.x509.DistributionPointName; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.X509Extensions; import org.bouncycastle.x509.extension.X509ExtensionUtil; /** * Returns (initialized, but maybe empty) set of URLs of CRLs for given * certificate. * * @param aCert * X509 certificate. * @return */ public static Set<String> getCrlUrls(final X509Certificate aCert) { final Set<String> tmpResult = new HashSet<String>(); //get CRLDistributionPoints extension from X509 certificate final byte[] crlDPExtension = aCert.getExtensionValue(X509Extensions.CRLDistributionPoints.getId()); if (crlDPExtension != null) { CRLDistPoint crlDistPoints = null; try { //decode instance of CRLDistPoint crlDistPoints = CRLDistPoint.getInstance(X509ExtensionUtil.fromExtensionValue(crlDPExtension)); } catch (IOException e) { e.printStackTrace(); } if (crlDistPoints != null) { final DistributionPoint[] distPoints = crlDistPoints.getDistributionPoints(); distPoint: for (DistributionPoint dp : distPoints) { final DistributionPointName dpName = dp.getDistributionPoint(); final GeneralNames generalNames = (GeneralNames) dpName.getName(); if (generalNames != null) { final GeneralName[] generalNameArr = generalNames.getNames(); if (generalNameArr != null) { for (final GeneralName generalName : generalNameArr) { if (generalName.getTagNo() == GeneralName.uniformResourceIdentifier) { final DERString derString = (DERString) generalName.getName(); final String uri = derString.getString(); if (uri != null && uri.startsWith("http")) { // ||uri.startsWith("ftp") tmpResult.add(uri); continue distPoint; } } } } } } } } else { System.out.println("CRLDistributionPoints extension not supported by the certificate."); } return tmpResult; }
Úžasná posloupnost zavíracích závorek, co říkáte? To jsou ty zmiňované stromové struktúry. :-)
A jak to vlastně použít? S rozumem, pánové, s rozumem. :-) Malý příklad, jak to může fungovat, je zde:
/** * Sample usage of {@link #getCrlUrls(X509Certificate)}. * * @param args */ public static void main(String[] args) { try { // this works only on Windows with 32-bit Sun Java 6 final KeyStore ks = KeyStore.getInstance("WINDOWS-MY"); if (ks != null) { ks.load(null, null); // suppose, we have at least one certificate in the keystore final X509Certificate certificate = (X509Certificate) ks.getCertificate(ks.aliases().nextElement()); System.out.println("Certificate: " + certificate.getSubjectDN()); final Set<String> crlUrlSet = getCrlUrls(certificate); System.out.println("Set of CRL URLs: " + crlUrlSet); } } catch (Exception e) { e.printStackTrace(); } }
Což může vypsat například toto:
Certificate: SERIALNUMBER=PXXX, CN=Josef Cacek, OU=PZZZ, L=YYY, C=CZ Set of CRL URLs: [http://postsignum.ttc.cz/crl/psqualifiedca2.crl, http://www.postsignum.cz/crl/psqualifiedca2.crl, http://www2.postsignum.cz/crl/psqualifiedca2.crl]
Komentáře