| package org.bouncycastle.jce.provider; |
| |
| 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.DEROutputStream; |
| 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; |
| |
| import javax.security.auth.x500.X500Principal; |
| 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.Date; |
| import java.util.Enumeration; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| import java.util.Collections; |
| |
| /** |
| * The following extensions are listed in RFC 2459 as relevant to CRLs |
| * |
| * Authority Key Identifier |
| * Issuer Alternative Name |
| * CRL Number |
| * Delta CRL Indicator (critical) |
| * Issuing Distribution Point (critical) |
| */ |
| 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); |
| } |
| } |
| |
| /** |
| * Will return true if any extensions are present and marked |
| * as critical as we currently dont handle any extensions! |
| */ |
| 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, "BC"); |
| } |
| |
| 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; |
| } |
| |
| /** |
| * Returns a string representation of this CRL. |
| * |
| * @return a string representation of this CRL. |
| */ |
| 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(); |
| } |
| |
| /** |
| * Checks whether the given certificate is on this CRL. |
| * |
| * @param cert the certificate to check for. |
| * @return true if the given certificate is on this CRL, |
| * false otherwise. |
| */ |
| 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; |
| } |
| } |
| |