/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Alexander Y. Kleymenov * @version $Revision$ */ package org.apache.harmony.security.provider.cert; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Principal; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CRLException; import java.security.cert.Certificate; import java.security.cert.X509CRL; import java.security.cert.X509CRLEntry; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.security.auth.x500.X500Principal; import org.apache.harmony.security.utils.AlgNameMapper; import org.apache.harmony.security.x509.CertificateList; import org.apache.harmony.security.x509.Extension; import org.apache.harmony.security.x509.Extensions; import org.apache.harmony.security.x509.TBSCertList; /** * This class is an implementation of X509CRL. It wraps * the instance of org.apache.harmony.security.x509.CertificateList * built on the base of provided ASN.1 DER encoded form of * CertificateList structure (as specified in RFC 3280 * http://www.ietf.org/rfc/rfc3280.txt). * Implementation supports work with indirect CRLs. * @see org.apache.harmony.security.x509.CertificateList * @see java.security.cert.X509CRL */ public class X509CRLImpl extends X509CRL { // the core object to be wrapped in X509CRL private final CertificateList crl; // To speed up access to the info, the following fields // cache values retrieved from the CertificateList object private final TBSCertList tbsCertList; private byte[] tbsCertListEncoding; private final Extensions extensions; private X500Principal issuer; private ArrayList entries; private int entriesSize; private byte[] signature; private String sigAlgOID; private String sigAlgName; private byte[] sigAlgParams; // encoded form of crl private byte[] encoding; // indicates whether the signature algorithm parameters are null private boolean nullSigAlgParams; // indicates whether the crl entries have already been retrieved // from CertificateList object (crl) private boolean entriesRetrieved; // indicates whether this X.509 CRL is direct or indirect // (see rfc 3280 http://www.ietf.org/rfc/rfc3280.txt, p 5.) private boolean isIndirectCRL; // if crl is indirect, this field holds an info about how // many of the leading certificates in the list are issued // by the same issuer as CRL. private int nonIndirectEntriesSize; /** * Creates X.509 CRL by wrapping of the specified CertificateList object. */ public X509CRLImpl(CertificateList crl) { this.crl = crl; this.tbsCertList = crl.getTbsCertList(); this.extensions = tbsCertList.getCrlExtensions(); } /** * Creates X.509 CRL on the base of ASN.1 DER encoded form of * the CRL (CertificateList structure described in RFC 3280) * provided via input stream. * @throws CRLException if decoding errors occur. */ public X509CRLImpl(InputStream in) throws CRLException { try { // decode CertificateList structure this.crl = (CertificateList) CertificateList.ASN1.decode(in); this.tbsCertList = crl.getTbsCertList(); this.extensions = tbsCertList.getCrlExtensions(); } catch (IOException e) { throw new CRLException(e); } } /** * Creates X.509 CRL on the base of ASN.1 DER encoded form of * the CRL (CertificateList structure described in RFC 3280) * provided via array of bytes. * @throws IOException if decoding errors occur. */ public X509CRLImpl(byte[] encoding) throws IOException { this((CertificateList) CertificateList.ASN1.decode(encoding)); } // --------------------------------------------------------------------- // ----- java.security.cert.X509CRL abstract method implementations ---- // --------------------------------------------------------------------- /** * @see java.security.cert.X509CRL#getEncoded() * method documentation for more info */ public byte[] getEncoded() throws CRLException { if (encoding == null) { encoding = crl.getEncoded(); } byte[] result = new byte[encoding.length]; System.arraycopy(encoding, 0, result, 0, encoding.length); return result; } /** * @see java.security.cert.X509CRL#getVersion() * method documentation for more info */ public int getVersion() { return tbsCertList.getVersion(); } /** * @see java.security.cert.X509CRL#getIssuerDN() * method documentation for more info */ public Principal getIssuerDN() { if (issuer == null) { issuer = tbsCertList.getIssuer().getX500Principal(); } return issuer; } /** * @see java.security.cert.X509CRL#getIssuerX500Principal() * method documentation for more info */ public X500Principal getIssuerX500Principal() { if (issuer == null) { issuer = tbsCertList.getIssuer().getX500Principal(); } return issuer; } /** * @see java.security.cert.X509CRL#getThisUpdate() * method documentation for more info */ public Date getThisUpdate() { return tbsCertList.getThisUpdate(); } /** * @see java.security.cert.X509CRL#getNextUpdate() * method documentation for more info */ public Date getNextUpdate() { return tbsCertList.getNextUpdate(); } /* * Retrieves the crl entries (TBSCertList.RevokedCertificate objects) * from the TBSCertList structure and converts them to the * X509CRLEntryImpl objects */ private void retrieveEntries() { entriesRetrieved = true; List rcerts = tbsCertList.getRevokedCertificates(); if (rcerts == null) { return; } entriesSize = rcerts.size(); entries = new ArrayList(entriesSize); // null means that revoked certificate issuer is the same as CRL issuer X500Principal rcertIssuer = null; for (int i=0; i<entriesSize; i++) { TBSCertList.RevokedCertificate rcert = (TBSCertList.RevokedCertificate) rcerts.get(i); X500Principal iss = rcert.getIssuer(); if (iss != null) { // certificate issuer differs from CRL issuer // and CRL is indirect. rcertIssuer = iss; isIndirectCRL = true; // remember how many leading revoked certificates in the // list are issued by the same issuer as issuer of CRL // (these certificates are first in the list) nonIndirectEntriesSize = i; } entries.add(new X509CRLEntryImpl(rcert, rcertIssuer)); } } /** * Searches for certificate in CRL. * This method supports indirect CRLs: if CRL is indirect method takes * into account serial number and issuer of the certificate, * if CRL issued by CA (i.e. it is not indirect) search is done only * by serial number of the specified certificate. * @see java.security.cert.X509CRL#getRevokedCertificate(X509Certificate) * method documentation for more info */ public X509CRLEntry getRevokedCertificate(X509Certificate certificate) { if (certificate == null) { throw new NullPointerException(); } if (!entriesRetrieved) { retrieveEntries(); } if (entries == null) { return null; } BigInteger serialN = certificate.getSerialNumber(); if (isIndirectCRL) { // search in indirect crl X500Principal certIssuer = certificate.getIssuerX500Principal(); if (certIssuer.equals(getIssuerX500Principal())) { // certificate issuer is CRL issuer certIssuer = null; } for (int i=0; i<entriesSize; i++) { X509CRLEntry entry = (X509CRLEntry) entries.get(i); // check the serial number of revoked certificate if (serialN.equals(entry.getSerialNumber())) { // revoked certificate issuer X500Principal iss = entry.getCertificateIssuer(); // check the issuer of revoked certificate if (certIssuer != null) { // certificate issuer is not a CRL issuer, so // check issuers for equality if (certIssuer.equals(iss)) { return entry; } } else if (iss == null) { // both certificates was issued by CRL issuer return entry; } } } } else { // search in CA's (non indirect) crl: just look up the serial number for (int i=0; i<entriesSize; i++) { X509CRLEntry entry = (X509CRLEntry) entries.get(i); if (serialN.equals(entry.getSerialNumber())) { return entry; } } } return null; } /** * Method searches for CRL entry with specified serial number. * The method will search only certificate issued by CRL's issuer. * @see java.security.cert.X509CRL#getRevokedCertificate(BigInteger) * method documentation for more info */ public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) { if (!entriesRetrieved) { retrieveEntries(); } if (entries == null) { return null; } for (int i=0; i<nonIndirectEntriesSize; i++) { X509CRLEntry entry = (X509CRLEntry) entries.get(i); if (serialNumber.equals(entry.getSerialNumber())) { return entry; } } return null; } /** * @see java.security.cert.X509CRL#getRevokedCertificates() * method documentation for more info */ public Set<? extends X509CRLEntry> getRevokedCertificates() { if (!entriesRetrieved) { retrieveEntries(); } if (entries == null) { return null; } return new HashSet(entries); } /** * @see java.security.cert.X509CRL#getTBSCertList() * method documentation for more info */ public byte[] getTBSCertList() throws CRLException { if (tbsCertListEncoding == null) { tbsCertListEncoding = tbsCertList.getEncoded(); } byte[] result = new byte[tbsCertListEncoding.length]; System.arraycopy(tbsCertListEncoding, 0, result, 0, tbsCertListEncoding.length); return result; } /** * @see java.security.cert.X509CRL#getSignature() * method documentation for more info */ public byte[] getSignature() { if (signature == null) { signature = crl.getSignatureValue(); } byte[] result = new byte[signature.length]; System.arraycopy(signature, 0, result, 0, signature.length); return result; } /** * @see java.security.cert.X509CRL#getSigAlgName() * method documentation for more info */ public String getSigAlgName() { if (sigAlgOID == null) { sigAlgOID = tbsCertList.getSignature().getAlgorithm(); sigAlgName = AlgNameMapper.map2AlgName(sigAlgOID); if (sigAlgName == null) { sigAlgName = sigAlgOID; } } return sigAlgName; } /** * @see java.security.cert.X509CRL#getSigAlgOID() * method documentation for more info */ public String getSigAlgOID() { if (sigAlgOID == null) { sigAlgOID = tbsCertList.getSignature().getAlgorithm(); sigAlgName = AlgNameMapper.map2AlgName(sigAlgOID); if (sigAlgName == null) { sigAlgName = sigAlgOID; } } return sigAlgOID; } /** * @see java.security.cert.X509CRL#getSigAlgParams() * method documentation for more info */ public byte[] getSigAlgParams() { if (nullSigAlgParams) { return null; } if (sigAlgParams == null) { sigAlgParams = tbsCertList.getSignature().getParameters(); if (sigAlgParams == null) { nullSigAlgParams = true; return null; } } return sigAlgParams; } /** * @see java.security.cert.X509CRL#verify(PublicKey key) * method documentation for more info */ public void verify(PublicKey key) throws CRLException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { Signature signature = Signature.getInstance(getSigAlgName()); signature.initVerify(key); byte[] tbsEncoding = tbsCertList.getEncoded(); signature.update(tbsEncoding, 0, tbsEncoding.length); if (!signature.verify(crl.getSignatureValue())) { throw new SignatureException("Signature was not verified"); } } /** * @see java.security.cert.X509CRL#verify(PublicKey key, String sigProvider) * method documentation for more info */ public void verify(PublicKey key, String sigProvider) throws CRLException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { Signature signature = Signature.getInstance( getSigAlgName(), sigProvider); signature.initVerify(key); byte[] tbsEncoding = tbsCertList.getEncoded(); signature.update(tbsEncoding, 0, tbsEncoding.length); if (!signature.verify(crl.getSignatureValue())) { throw new SignatureException("Signature was not verified"); } } // --------------------------------------------------------------------- // ------ java.security.cert.CRL abstract method implementations ------- // --------------------------------------------------------------------- /** * @see java.security.cert.CRL#isRevoked(Certificate) * method documentation for more info */ public boolean isRevoked(Certificate cert) { if (!(cert instanceof X509Certificate)) { return false; } return getRevokedCertificate((X509Certificate) cert) != null; } /** * @see java.security.cert.CRL#toString() * method documentation for more info */ public String toString() { return crl.toString(); } // --------------------------------------------------------------------- // ------ java.security.cert.X509Extension method implementations ------ // --------------------------------------------------------------------- /** * @see java.security.cert.X509Extension#getNonCriticalExtensionOIDs() * method documentation for more info */ public Set getNonCriticalExtensionOIDs() { if (extensions == null) { return null; } return extensions.getNonCriticalExtensions(); } /** * @see java.security.cert.X509Extension#getCriticalExtensionOIDs() * method documentation for more info */ public Set getCriticalExtensionOIDs() { if (extensions == null) { return null; } return extensions.getCriticalExtensions(); } /** * @see java.security.cert.X509Extension#getExtensionValue(String) * method documentation for more info */ public byte[] getExtensionValue(String oid) { if (extensions == null) { return null; } Extension ext = extensions.getExtensionByOID(oid); return (ext == null) ? null : ext.getRawExtnValue(); } /** * @see java.security.cert.X509Extension#hasUnsupportedCriticalExtension() * method documentation for more info */ public boolean hasUnsupportedCriticalExtension() { if (extensions == null) { return false; } return extensions.hasUnsupportedCritical(); } }