/* * 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 */ 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.internal.nls.Messages; 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<X509CRLEntryImpl> 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 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)); } /** * Creates X.509 CRL by wrapping of the specified CertificateList object. */ public X509CRLImpl(CertificateList crl) { this.crl = crl; tbsCertList = crl.getTbsCertList(); 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 crl = (CertificateList) CertificateList.ASN1.decode(in); tbsCertList = crl.getTbsCertList(); extensions = tbsCertList.getCrlExtensions(); } catch (final IOException e) { throw new CRLException(e); } } // --------------------------------------------------------------------- // ----- java.security.cert.X509CRL abstract method implementations ---- // --------------------------------------------------------------------- /** * @see java.security.cert.X509Extension#getCriticalExtensionOIDs() method * documentation for more info */ @Override public Set<String> getCriticalExtensionOIDs() { if (extensions == null) { return null; } return extensions.getCriticalExtensions(); } /** * @see java.security.cert.X509CRL#getEncoded() method documentation for * more info */ @Override public byte[] getEncoded() throws CRLException { if (encoding == null) { encoding = crl.getEncoded(); } final byte[] result = new byte[encoding.length]; System.arraycopy(encoding, 0, result, 0, encoding.length); return result; } /** * @see java.security.cert.X509Extension#getExtensionValue(String) method * documentation for more info */ @Override public byte[] getExtensionValue(String oid) { if (extensions == null) { return null; } final Extension ext = extensions.getExtensionByOID(oid); return (ext == null) ? null : ext.getRawExtnValue(); } /** * @see java.security.cert.X509CRL#getIssuerDN() method documentation for * more info */ @Override public Principal getIssuerDN() { if (issuer == null) { issuer = tbsCertList.getIssuer().getX500Principal(); } return issuer; } /** * @see java.security.cert.X509CRL#getIssuerX500Principal() method * documentation for more info */ @Override public X500Principal getIssuerX500Principal() { if (issuer == null) { issuer = tbsCertList.getIssuer().getX500Principal(); } return issuer; } /** * @see java.security.cert.X509CRL#getNextUpdate() method documentation for * more info */ @Override public Date getNextUpdate() { return tbsCertList.getNextUpdate(); } /** * @see java.security.cert.X509Extension#getNonCriticalExtensionOIDs() * method documentation for more info */ @Override public Set<String> getNonCriticalExtensionOIDs() { if (extensions == null) { return null; } return extensions.getNonCriticalExtensions(); } /** * 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 */ @Override public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) { if (!entriesRetrieved) { retrieveEntries(); } if (entries == null) { return null; } for (int i = 0; i < nonIndirectEntriesSize; i++) { final X509CRLEntry entry = entries.get(i); if (serialNumber.equals(entry.getSerialNumber())) { return entry; } } return null; } /** * 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 */ @Override public X509CRLEntry getRevokedCertificate(X509Certificate certificate) { if (certificate == null) { throw new NullPointerException(); } if (!entriesRetrieved) { retrieveEntries(); } if (entries == null) { return null; } final 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++) { final X509CRLEntry entry = entries.get(i); // check the serial number of revoked certificate if (serialN.equals(entry.getSerialNumber())) { // revoked certificate issuer final 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++) { final X509CRLEntry entry = entries.get(i); if (serialN.equals(entry.getSerialNumber())) { return entry; } } } return null; } /** * @see java.security.cert.X509CRL#getRevokedCertificates() method * documentation for more info */ @Override public Set<? extends X509CRLEntry> getRevokedCertificates() { if (!entriesRetrieved) { retrieveEntries(); } if (entries == null) { return null; } return new HashSet<X509CRLEntryImpl>(entries); } /** * @see java.security.cert.X509CRL#getSigAlgName() method documentation for * more info */ @Override 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 */ @Override 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 */ @Override 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#getSignature() method documentation for * more info */ @Override public byte[] getSignature() { if (signature == null) { signature = crl.getSignatureValue(); } final byte[] result = new byte[signature.length]; System.arraycopy(signature, 0, result, 0, signature.length); return result; } /** * @see java.security.cert.X509CRL#getTBSCertList() method documentation for * more info */ @Override public byte[] getTBSCertList() throws CRLException { if (tbsCertListEncoding == null) { tbsCertListEncoding = tbsCertList.getEncoded(); } final byte[] result = new byte[tbsCertListEncoding.length]; System.arraycopy(tbsCertListEncoding, 0, result, 0, tbsCertListEncoding.length); return result; } /** * @see java.security.cert.X509CRL#getThisUpdate() method documentation for * more info */ @Override public Date getThisUpdate() { return tbsCertList.getThisUpdate(); } /** * @see java.security.cert.X509CRL#getVersion() method documentation for * more info */ @Override public int getVersion() { return tbsCertList.getVersion(); } // --------------------------------------------------------------------- // ------ java.security.cert.CRL abstract method implementations ------- // --------------------------------------------------------------------- /** * @see java.security.cert.X509Extension#hasUnsupportedCriticalExtension() * method documentation for more info */ @Override public boolean hasUnsupportedCriticalExtension() { if (extensions == null) { return false; } return extensions.hasUnsupportedCritical(); } /** * @see java.security.cert.CRL#isRevoked(Certificate) method documentation * for more info */ @Override public boolean isRevoked(Certificate cert) { if (!(cert instanceof X509Certificate)) { return false; } return getRevokedCertificate((X509Certificate) cert) != null; } // --------------------------------------------------------------------- // ------ java.security.cert.X509Extension method implementations ------ // --------------------------------------------------------------------- /* * Retrieves the crl entries (TBSCertList.RevokedCertificate objects) from * the TBSCertList structure and converts them to the X509CRLEntryImpl * objects */ private void retrieveEntries() { entriesRetrieved = true; final List rcerts = tbsCertList.getRevokedCertificates(); if (rcerts == null) { return; } entriesSize = rcerts.size(); entries = new ArrayList<X509CRLEntryImpl>(entriesSize); // null means that revoked certificate issuer is the same as CRL issuer X500Principal rcertIssuer = null; for (int i = 0; i < entriesSize; i++) { final TBSCertList.RevokedCertificate rcert = (TBSCertList.RevokedCertificate) rcerts .get(i); final 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)); } } /** * @see java.security.cert.CRL#toString() method documentation for more info */ @Override public String toString() { return crl.toString(); } /** * @see java.security.cert.X509CRL#verify(PublicKey key) method * documentation for more info */ @Override public void verify(PublicKey key) throws CRLException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { final Signature signature = Signature.getInstance(getSigAlgName()); signature.initVerify(key); final byte[] tbsEncoding = tbsCertList.getEncoded(); signature.update(tbsEncoding, 0, tbsEncoding.length); if (!signature.verify(crl.getSignatureValue())) { throw new SignatureException(Messages.getString("security.15C")); //$NON-NLS-1$ } } /** * @see java.security.cert.X509CRL#verify(PublicKey key, String sigProvider) * method documentation for more info */ @Override public void verify(PublicKey key, String sigProvider) throws CRLException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { final Signature signature = Signature.getInstance(getSigAlgName(), sigProvider); signature.initVerify(key); final byte[] tbsEncoding = tbsCertList.getEncoded(); signature.update(tbsEncoding, 0, tbsEncoding.length); if (!signature.verify(crl.getSignatureValue())) { throw new SignatureException(Messages.getString("security.15C")); //$NON-NLS-1$ } } }