1package org.bouncycastle.cert; 2 3import java.io.ByteArrayInputStream; 4import java.io.IOException; 5import java.io.InputStream; 6import java.io.OutputStream; 7import java.math.BigInteger; 8import java.util.ArrayList; 9import java.util.Collection; 10import java.util.Enumeration; 11import java.util.List; 12import java.util.Set; 13 14import org.bouncycastle.asn1.ASN1InputStream; 15import org.bouncycastle.asn1.ASN1ObjectIdentifier; 16import org.bouncycastle.asn1.DEROutputStream; 17import org.bouncycastle.asn1.x500.X500Name; 18import org.bouncycastle.asn1.x509.CertificateList; 19import org.bouncycastle.asn1.x509.Extension; 20import org.bouncycastle.asn1.x509.Extensions; 21import org.bouncycastle.asn1.x509.GeneralName; 22import org.bouncycastle.asn1.x509.GeneralNames; 23import org.bouncycastle.asn1.x509.IssuingDistributionPoint; 24import org.bouncycastle.asn1.x509.TBSCertList; 25import org.bouncycastle.operator.ContentVerifier; 26import org.bouncycastle.operator.ContentVerifierProvider; 27 28/** 29 * Holding class for an X.509 CRL structure. 30 */ 31public class X509CRLHolder 32{ 33 private CertificateList x509CRL; 34 private boolean isIndirect; 35 private Extensions extensions; 36 private GeneralNames issuerName; 37 38 private static CertificateList parseStream(InputStream stream) 39 throws IOException 40 { 41 try 42 { 43 return CertificateList.getInstance(new ASN1InputStream(stream, true).readObject()); 44 } 45 catch (ClassCastException e) 46 { 47 throw new CertIOException("malformed data: " + e.getMessage(), e); 48 } 49 catch (IllegalArgumentException e) 50 { 51 throw new CertIOException("malformed data: " + e.getMessage(), e); 52 } 53 } 54 55 private static boolean isIndirectCRL(Extensions extensions) 56 { 57 if (extensions == null) 58 { 59 return false; 60 } 61 62 Extension ext = extensions.getExtension(Extension.issuingDistributionPoint); 63 64 return ext != null && IssuingDistributionPoint.getInstance(ext.getParsedValue()).isIndirectCRL(); 65 } 66 67 /** 68 * Create a X509CRLHolder from the passed in bytes. 69 * 70 * @param crlEncoding BER/DER encoding of the CRL 71 * @throws IOException in the event of corrupted data, or an incorrect structure. 72 */ 73 public X509CRLHolder(byte[] crlEncoding) 74 throws IOException 75 { 76 this(parseStream(new ByteArrayInputStream(crlEncoding))); 77 } 78 79 /** 80 * Create a X509CRLHolder from the passed in InputStream. 81 * 82 * @param crlStream BER/DER encoded InputStream of the CRL 83 * @throws IOException in the event of corrupted data, or an incorrect structure. 84 */ 85 public X509CRLHolder(InputStream crlStream) 86 throws IOException 87 { 88 this(parseStream(crlStream)); 89 } 90 91 /** 92 * Create a X509CRLHolder from the passed in ASN.1 structure. 93 * 94 * @param x509CRL an ASN.1 CertificateList structure. 95 */ 96 public X509CRLHolder(CertificateList x509CRL) 97 { 98 this.x509CRL = x509CRL; 99 this.extensions = x509CRL.getTBSCertList().getExtensions(); 100 this.isIndirect = isIndirectCRL(extensions); 101 this.issuerName = new GeneralNames(new GeneralName(x509CRL.getIssuer())); 102 } 103 104 /** 105 * Return the ASN.1 encoding of this holder's CRL. 106 * 107 * @return a DER encoded byte array. 108 * @throws IOException if an encoding cannot be generated. 109 */ 110 public byte[] getEncoded() 111 throws IOException 112 { 113 return x509CRL.getEncoded(); 114 } 115 116 /** 117 * Return the issuer of this holder's CRL. 118 * 119 * @return the CRL issuer. 120 */ 121 public X500Name getIssuer() 122 { 123 return X500Name.getInstance(x509CRL.getIssuer()); 124 } 125 126 public X509CRLEntryHolder getRevokedCertificate(BigInteger serialNumber) 127 { 128 GeneralNames currentCA = issuerName; 129 for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements();) 130 { 131 TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)en.nextElement(); 132 133 if (entry.getUserCertificate().getValue().equals(serialNumber)) 134 { 135 return new X509CRLEntryHolder(entry, isIndirect, currentCA); 136 } 137 138 if (isIndirect && entry.hasExtensions()) 139 { 140 Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer); 141 142 if (currentCaName != null) 143 { 144 currentCA = GeneralNames.getInstance(currentCaName.getParsedValue()); 145 } 146 } 147 } 148 149 return null; 150 } 151 152 /** 153 * Return a collection of X509CRLEntryHolder objects, giving the details of the 154 * revoked certificates that appear on this CRL. 155 * 156 * @return the revoked certificates as a collection of X509CRLEntryHolder objects. 157 */ 158 public Collection getRevokedCertificates() 159 { 160 TBSCertList.CRLEntry[] entries = x509CRL.getRevokedCertificates(); 161 List l = new ArrayList(entries.length); 162 GeneralNames currentCA = issuerName; 163 164 for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements();) 165 { 166 TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)en.nextElement(); 167 X509CRLEntryHolder crlEntry = new X509CRLEntryHolder(entry, isIndirect, currentCA); 168 169 l.add(crlEntry); 170 171 currentCA = crlEntry.getCertificateIssuer(); 172 } 173 174 return l; 175 } 176 177 /** 178 * Return whether or not the holder's CRL contains extensions. 179 * 180 * @return true if extension are present, false otherwise. 181 */ 182 public boolean hasExtensions() 183 { 184 return extensions != null; 185 } 186 187 /** 188 * Look up the extension associated with the passed in OID. 189 * 190 * @param oid the OID of the extension of interest. 191 * 192 * @return the extension if present, null otherwise. 193 */ 194 public Extension getExtension(ASN1ObjectIdentifier oid) 195 { 196 if (extensions != null) 197 { 198 return extensions.getExtension(oid); 199 } 200 201 return null; 202 } 203 204 /** 205 * Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the 206 * extensions contained in this holder's CRL. 207 * 208 * @return a list of extension OIDs. 209 */ 210 public List getExtensionOIDs() 211 { 212 return CertUtils.getExtensionOIDs(extensions); 213 } 214 215 /** 216 * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the 217 * critical extensions contained in this holder's CRL. 218 * 219 * @return a set of critical extension OIDs. 220 */ 221 public Set getCriticalExtensionOIDs() 222 { 223 return CertUtils.getCriticalExtensionOIDs(extensions); 224 } 225 226 /** 227 * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the 228 * non-critical extensions contained in this holder's CRL. 229 * 230 * @return a set of non-critical extension OIDs. 231 */ 232 public Set getNonCriticalExtensionOIDs() 233 { 234 return CertUtils.getNonCriticalExtensionOIDs(extensions); 235 } 236 237 /** 238 * Return the underlying ASN.1 structure for the CRL in this holder. 239 * 240 * @return a CertificateList object. 241 */ 242 public CertificateList toASN1Structure() 243 { 244 return x509CRL; 245 } 246 247 /** 248 * Validate the signature on the CRL. 249 * 250 * @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature. 251 * @return true if the signature is valid, false otherwise. 252 * @throws CertException if the signature cannot be processed or is inappropriate. 253 */ 254 public boolean isSignatureValid(ContentVerifierProvider verifierProvider) 255 throws CertException 256 { 257 TBSCertList tbsCRL = x509CRL.getTBSCertList(); 258 259 if (!tbsCRL.getSignature().equals(x509CRL.getSignatureAlgorithm())) 260 { 261 throw new CertException("signature invalid - algorithm identifier mismatch"); 262 } 263 264 ContentVerifier verifier; 265 266 try 267 { 268 verifier = verifierProvider.get((tbsCRL.getSignature())); 269 270 OutputStream sOut = verifier.getOutputStream(); 271 DEROutputStream dOut = new DEROutputStream(sOut); 272 273 dOut.writeObject(tbsCRL); 274 275 sOut.close(); 276 } 277 catch (Exception e) 278 { 279 throw new CertException("unable to process signature: " + e.getMessage(), e); 280 } 281 282 return verifier.verify(x509CRL.getSignature().getBytes()); 283 } 284 285 public boolean equals( 286 Object o) 287 { 288 if (o == this) 289 { 290 return true; 291 } 292 293 if (!(o instanceof X509CRLHolder)) 294 { 295 return false; 296 } 297 298 X509CRLHolder other = (X509CRLHolder)o; 299 300 return this.x509CRL.equals(other.x509CRL); 301 } 302 303 public int hashCode() 304 { 305 return this.x509CRL.hashCode(); 306 } 307} 308