package org.bouncycastle.jce.provider;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
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.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OutputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.util.ASN1Dump;
import org.bouncycastle.asn1.x509.CRLDistPoint;
import org.bouncycastle.asn1.x509.CRLNumber;
import org.bouncycastle.asn1.x509.CertificateList;
import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
import org.bouncycastle.asn1.x509.TBSCertList;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.x509.extension.X509ExtensionUtil;
public class X509CRLObject
extends X509CRL
{
private CertificateList c;
private String sigAlgName;
private byte[] sigAlgParams;
private boolean isIndirect;
public X509CRLObject(
CertificateList c)
throws CRLException
{
this.c = c;
try
{
this.sigAlgName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm());
if (c.getSignatureAlgorithm().getParameters() != null)
{
this.sigAlgParams = ((ASN1Encodable)c.getSignatureAlgorithm().getParameters()).getDEREncoded();
}
else
{
this.sigAlgParams = null;
}
this.isIndirect = isIndirectCRL();
}
catch (Exception e)
{
throw new CRLException("CRL contents invalid: " + e);
}
}
public boolean hasUnsupportedCriticalExtension()
{
Set extns = getCriticalExtensionOIDs();
if (extns == null)
{
return false;
}
extns.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT);
extns.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
return !extns.isEmpty();
}
private Set getExtensionOIDs(boolean critical)
{
if (this.getVersion() == 2)
{
X509Extensions extensions = c.getTBSCertList().getExtensions();
if (extensions != null)
{
Set set = new HashSet();
Enumeration e = extensions.oids();
while (e.hasMoreElements())
{
DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
X509Extension ext = extensions.getExtension(oid);
if (critical == ext.isCritical())
{
set.add(oid.getId());
}
}
return set;
}
}
return null;
}
public Set getCriticalExtensionOIDs()
{
return getExtensionOIDs(true);
}
public Set getNonCriticalExtensionOIDs()
{
return getExtensionOIDs(false);
}
public byte[] getExtensionValue(String oid)
{
X509Extensions exts = c.getTBSCertList().getExtensions();
if (exts != null)
{
X509Extension ext = exts.getExtension(new DERObjectIdentifier(oid));
if (ext != null)
{
try
{
return ext.getValue().getEncoded();
}
catch (Exception e)
{
throw new IllegalStateException("error parsing " + e.toString());
}
}
}
return null;
}
public byte[] getEncoded()
throws CRLException
{
try
{
return c.getEncoded(ASN1Encodable.DER);
}
catch (IOException e)
{
throw new CRLException(e.toString());
}
}
public void verify(PublicKey key)
throws CRLException, NoSuchAlgorithmException,
InvalidKeyException, NoSuchProviderException, SignatureException
{
verify(key, BouncyCastleProvider.PROVIDER_NAME);
}
public void verify(PublicKey key, String sigProvider)
throws CRLException, NoSuchAlgorithmException,
InvalidKeyException, NoSuchProviderException, SignatureException
{
if (!c.getSignatureAlgorithm().equals(c.getTBSCertList().getSignature()))
{
throw new CRLException("Signature algorithm on CertificateList does not match TBSCertList.");
}
Signature sig = Signature.getInstance(getSigAlgName(), sigProvider);
sig.initVerify(key);
sig.update(this.getTBSCertList());
if (!sig.verify(this.getSignature()))
{
throw new SignatureException("CRL does not verify with supplied public key.");
}
}
public int getVersion()
{
return c.getVersion();
}
public Principal getIssuerDN()
{
return new X509Principal(c.getIssuer());
}
public X500Principal getIssuerX500Principal()
{
try
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ASN1OutputStream aOut = new ASN1OutputStream(bOut);
aOut.writeObject(c.getIssuer());
return new X500Principal(bOut.toByteArray());
}
catch (IOException e)
{
throw new IllegalStateException("can't encode issuer DN");
}
}
public Date getThisUpdate()
{
return c.getThisUpdate().getDate();
}
public Date getNextUpdate()
{
if (c.getNextUpdate() != null)
{
return c.getNextUpdate().getDate();
}
return null;
}
private Set loadCRLEntries()
{
Set entrySet = new HashSet();
Enumeration certs = c.getRevokedCertificateEnumeration();
X500Principal previousCertificateIssuer = getIssuerX500Principal();
while (certs.hasMoreElements())
{
TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement();
X509CRLEntryObject crlEntry = new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer);
entrySet.add(crlEntry);
previousCertificateIssuer = crlEntry.getCertificateIssuer();
}
return entrySet;
}
public X509CRLEntry getRevokedCertificate(BigInteger serialNumber)
{
Enumeration certs = c.getRevokedCertificateEnumeration();
X500Principal previousCertificateIssuer = getIssuerX500Principal();
while (certs.hasMoreElements())
{
TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement();
X509CRLEntryObject crlEntry = new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer);
if (serialNumber.equals(entry.getUserCertificate().getValue()))
{
return crlEntry;
}
previousCertificateIssuer = crlEntry.getCertificateIssuer();
}
return null;
}
public Set getRevokedCertificates()
{
Set entrySet = loadCRLEntries();
if (!entrySet.isEmpty())
{
return Collections.unmodifiableSet(entrySet);
}
return null;
}
public byte[] getTBSCertList()
throws CRLException
{
try
{
return c.getTBSCertList().getEncoded("DER");
}
catch (IOException e)
{
throw new CRLException(e.toString());
}
}
public byte[] getSignature()
{
return c.getSignature().getBytes();
}
public String getSigAlgName()
{
return sigAlgName;
}
public String getSigAlgOID()
{
return c.getSignatureAlgorithm().getObjectId().getId();
}
public byte[] getSigAlgParams()
{
if (sigAlgParams != null)
{
byte[] tmp = new byte[sigAlgParams.length];
System.arraycopy(sigAlgParams, 0, tmp, 0, tmp.length);
return tmp;
}
return null;
}
public String toString()
{
StringBuffer buf = new StringBuffer();
String nl = System.getProperty("line.separator");
buf.append(" Version: ").append(this.getVersion()).append(
nl);
buf.append(" IssuerDN: ").append(this.getIssuerDN())
.append(nl);
buf.append(" This update: ").append(this.getThisUpdate())
.append(nl);
buf.append(" Next update: ").append(this.getNextUpdate())
.append(nl);
buf.append(" Signature Algorithm: ").append(this.getSigAlgName())
.append(nl);
byte[] sig = this.getSignature();
buf.append(" Signature: ").append(
new String(Hex.encode(sig, 0, 20))).append(nl);
for (int i = 20; i < sig.length; i += 20)
{
if (i < sig.length - 20)
{
buf.append(" ").append(
new String(Hex.encode(sig, i, 20))).append(nl);
}
else
{
buf.append(" ").append(
new String(Hex.encode(sig, i, sig.length - i))).append(nl);
}
}
X509Extensions extensions = c.getTBSCertList().getExtensions();
if (extensions != null)
{
Enumeration e = extensions.oids();
if (e.hasMoreElements())
{
buf.append(" Extensions: ").append(nl);
}
while (e.hasMoreElements())
{
DERObjectIdentifier oid = (DERObjectIdentifier) e.nextElement();
X509Extension ext = extensions.getExtension(oid);
if (ext.getValue() != null)
{
byte[] octs = ext.getValue().getOctets();
ASN1InputStream dIn = new ASN1InputStream(octs);
buf.append(" critical(").append(
ext.isCritical()).append(") ");
try
{
if (oid.equals(X509Extensions.CRLNumber))
{
buf.append(
new CRLNumber(DERInteger.getInstance(
dIn.readObject()).getPositiveValue()))
.append(nl);
}
else if (oid.equals(X509Extensions.DeltaCRLIndicator))
{
buf.append(
"Base CRL: "
+ new CRLNumber(DERInteger.getInstance(
dIn.readObject()).getPositiveValue()))
.append(nl);
}
else if (oid
.equals(X509Extensions.IssuingDistributionPoint))
{
buf.append(
new IssuingDistributionPoint((ASN1Sequence) dIn
.readObject())).append(nl);
}
else if (oid
.equals(X509Extensions.CRLDistributionPoints))
{
buf.append(
new CRLDistPoint((ASN1Sequence) dIn
.readObject())).append(nl);
}
else if (oid.equals(X509Extensions.FreshestCRL))
{
buf.append(
new CRLDistPoint((ASN1Sequence) dIn
.readObject())).append(nl);
}
else
{
buf.append(oid.getId());
buf.append(" value = ").append(
ASN1Dump.dumpAsString(dIn.readObject()))
.append(nl);
}
}
catch (Exception ex)
{
buf.append(oid.getId());
buf.append(" value = ").append("*****").append(nl);
}
}
else
{
buf.append(nl);
}
}
}
Set set = getRevokedCertificates();
if (set != null)
{
Iterator it = set.iterator();
while (it.hasNext())
{
buf.append(it.next());
buf.append(nl);
}
}
return buf.toString();
}
public boolean isRevoked(Certificate cert)
{
if (!cert.getType().equals("X.509"))
{
throw new RuntimeException("X.509 CRL used with non X.509 Cert");
}
TBSCertList.CRLEntry[] certs = c.getRevokedCertificates();
if (certs != null)
{
BigInteger serial = ((X509Certificate)cert).getSerialNumber();
for (int i = 0; i < certs.length; i++)
{
if (certs[i].getUserCertificate().getValue().equals(serial))
{
return true;
}
}
}
return false;
}
private boolean isIndirectCRL()
throws CRLException
{
byte[] idp = getExtensionValue(X509Extensions.IssuingDistributionPoint.getId());
boolean isIndirect = false;
try
{
if (idp != null)
{
isIndirect = IssuingDistributionPoint.getInstance(
X509ExtensionUtil.fromExtensionValue(idp))
.isIndirectCRL();
}
}
catch (Exception e)
{
throw new ExtCRLException(
"Exception reading IssuingDistributionPoint", e);
}
return isIndirect;
}
}