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