blob: 134d8f98d4f9f80268d0a8504a54d5e0e28f39a7 [file] [log] [blame]
/*
* 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();
}
}