pátek 30. července 2010

Jak na CRL a jejich URL

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]

Žádné komentáře: