1package org.bouncycastle.jce.provider; 2 3import java.io.ByteArrayOutputStream; 4import java.io.IOException; 5import java.math.BigInteger; 6import java.security.InvalidKeyException; 7import java.security.NoSuchAlgorithmException; 8import java.security.NoSuchProviderException; 9import java.security.Principal; 10import java.security.PublicKey; 11import java.security.Signature; 12import java.security.SignatureException; 13import java.security.cert.CRLException; 14import java.security.cert.Certificate; 15import java.security.cert.X509CRL; 16import java.security.cert.X509CRLEntry; 17import java.security.cert.X509Certificate; 18import java.util.Collections; 19import java.util.Date; 20import java.util.Enumeration; 21import java.util.HashSet; 22import java.util.Iterator; 23import java.util.Set; 24 25import javax.security.auth.x500.X500Principal; 26 27import org.bouncycastle.asn1.ASN1Encodable; 28import org.bouncycastle.asn1.ASN1InputStream; 29import org.bouncycastle.asn1.ASN1OutputStream; 30import org.bouncycastle.asn1.ASN1Sequence; 31import org.bouncycastle.asn1.DERInteger; 32import org.bouncycastle.asn1.DERObjectIdentifier; 33import org.bouncycastle.asn1.util.ASN1Dump; 34import org.bouncycastle.asn1.x509.CRLDistPoint; 35import org.bouncycastle.asn1.x509.CRLNumber; 36import org.bouncycastle.asn1.x509.CertificateList; 37import org.bouncycastle.asn1.x509.IssuingDistributionPoint; 38import org.bouncycastle.asn1.x509.TBSCertList; 39import org.bouncycastle.asn1.x509.X509Extension; 40import org.bouncycastle.asn1.x509.X509Extensions; 41import org.bouncycastle.jce.X509Principal; 42import org.bouncycastle.util.encoders.Hex; 43import org.bouncycastle.x509.extension.X509ExtensionUtil; 44 45/** 46 * The following extensions are listed in RFC 2459 as relevant to CRLs 47 * 48 * Authority Key Identifier 49 * Issuer Alternative Name 50 * CRL Number 51 * Delta CRL Indicator (critical) 52 * Issuing Distribution Point (critical) 53 */ 54public class X509CRLObject 55 extends X509CRL 56{ 57 private CertificateList c; 58 private String sigAlgName; 59 private byte[] sigAlgParams; 60 private boolean isIndirect; 61 62 public X509CRLObject( 63 CertificateList c) 64 throws CRLException 65 { 66 this.c = c; 67 68 try 69 { 70 this.sigAlgName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm()); 71 72 if (c.getSignatureAlgorithm().getParameters() != null) 73 { 74 this.sigAlgParams = ((ASN1Encodable)c.getSignatureAlgorithm().getParameters()).getDEREncoded(); 75 } 76 else 77 { 78 this.sigAlgParams = null; 79 } 80 81 this.isIndirect = isIndirectCRL(); 82 } 83 catch (Exception e) 84 { 85 throw new CRLException("CRL contents invalid: " + e); 86 } 87 } 88 89 /** 90 * Will return true if any extensions are present and marked 91 * as critical as we currently dont handle any extensions! 92 */ 93 public boolean hasUnsupportedCriticalExtension() 94 { 95 Set extns = getCriticalExtensionOIDs(); 96 97 if (extns == null) 98 { 99 return false; 100 } 101 102 extns.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT); 103 extns.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR); 104 105 return !extns.isEmpty(); 106 } 107 108 private Set getExtensionOIDs(boolean critical) 109 { 110 if (this.getVersion() == 2) 111 { 112 X509Extensions extensions = c.getTBSCertList().getExtensions(); 113 114 if (extensions != null) 115 { 116 Set set = new HashSet(); 117 Enumeration e = extensions.oids(); 118 119 while (e.hasMoreElements()) 120 { 121 DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement(); 122 X509Extension ext = extensions.getExtension(oid); 123 124 if (critical == ext.isCritical()) 125 { 126 set.add(oid.getId()); 127 } 128 } 129 130 return set; 131 } 132 } 133 134 return null; 135 } 136 137 public Set getCriticalExtensionOIDs() 138 { 139 return getExtensionOIDs(true); 140 } 141 142 public Set getNonCriticalExtensionOIDs() 143 { 144 return getExtensionOIDs(false); 145 } 146 147 public byte[] getExtensionValue(String oid) 148 { 149 X509Extensions exts = c.getTBSCertList().getExtensions(); 150 151 if (exts != null) 152 { 153 X509Extension ext = exts.getExtension(new DERObjectIdentifier(oid)); 154 155 if (ext != null) 156 { 157 try 158 { 159 return ext.getValue().getEncoded(); 160 } 161 catch (Exception e) 162 { 163 throw new IllegalStateException("error parsing " + e.toString()); 164 } 165 } 166 } 167 168 return null; 169 } 170 171 public byte[] getEncoded() 172 throws CRLException 173 { 174 try 175 { 176 return c.getEncoded(ASN1Encodable.DER); 177 } 178 catch (IOException e) 179 { 180 throw new CRLException(e.toString()); 181 } 182 } 183 184 public void verify(PublicKey key) 185 throws CRLException, NoSuchAlgorithmException, 186 InvalidKeyException, NoSuchProviderException, SignatureException 187 { 188 verify(key, BouncyCastleProvider.PROVIDER_NAME); 189 } 190 191 public void verify(PublicKey key, String sigProvider) 192 throws CRLException, NoSuchAlgorithmException, 193 InvalidKeyException, NoSuchProviderException, SignatureException 194 { 195 if (!c.getSignatureAlgorithm().equals(c.getTBSCertList().getSignature())) 196 { 197 throw new CRLException("Signature algorithm on CertificateList does not match TBSCertList."); 198 } 199 200 Signature sig = Signature.getInstance(getSigAlgName(), sigProvider); 201 202 sig.initVerify(key); 203 sig.update(this.getTBSCertList()); 204 if (!sig.verify(this.getSignature())) 205 { 206 throw new SignatureException("CRL does not verify with supplied public key."); 207 } 208 } 209 210 public int getVersion() 211 { 212 return c.getVersion(); 213 } 214 215 public Principal getIssuerDN() 216 { 217 return new X509Principal(c.getIssuer()); 218 } 219 220 public X500Principal getIssuerX500Principal() 221 { 222 try 223 { 224 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 225 ASN1OutputStream aOut = new ASN1OutputStream(bOut); 226 227 aOut.writeObject(c.getIssuer()); 228 229 return new X500Principal(bOut.toByteArray()); 230 } 231 catch (IOException e) 232 { 233 throw new IllegalStateException("can't encode issuer DN"); 234 } 235 } 236 237 public Date getThisUpdate() 238 { 239 return c.getThisUpdate().getDate(); 240 } 241 242 public Date getNextUpdate() 243 { 244 if (c.getNextUpdate() != null) 245 { 246 return c.getNextUpdate().getDate(); 247 } 248 249 return null; 250 } 251 252 private Set loadCRLEntries() 253 { 254 Set entrySet = new HashSet(); 255 Enumeration certs = c.getRevokedCertificateEnumeration(); 256 257 X500Principal previousCertificateIssuer = getIssuerX500Principal(); 258 while (certs.hasMoreElements()) 259 { 260 TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement(); 261 X509CRLEntryObject crlEntry = new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer); 262 entrySet.add(crlEntry); 263 previousCertificateIssuer = crlEntry.getCertificateIssuer(); 264 } 265 266 return entrySet; 267 } 268 269 public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) 270 { 271 Enumeration certs = c.getRevokedCertificateEnumeration(); 272 273 X500Principal previousCertificateIssuer = getIssuerX500Principal(); 274 while (certs.hasMoreElements()) 275 { 276 TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement(); 277 X509CRLEntryObject crlEntry = new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer); 278 279 if (serialNumber.equals(entry.getUserCertificate().getValue())) 280 { 281 return crlEntry; 282 } 283 284 previousCertificateIssuer = crlEntry.getCertificateIssuer(); 285 } 286 287 return null; 288 } 289 290 public Set getRevokedCertificates() 291 { 292 Set entrySet = loadCRLEntries(); 293 294 if (!entrySet.isEmpty()) 295 { 296 return Collections.unmodifiableSet(entrySet); 297 } 298 299 return null; 300 } 301 302 public byte[] getTBSCertList() 303 throws CRLException 304 { 305 try 306 { 307 return c.getTBSCertList().getEncoded("DER"); 308 } 309 catch (IOException e) 310 { 311 throw new CRLException(e.toString()); 312 } 313 } 314 315 public byte[] getSignature() 316 { 317 return c.getSignature().getBytes(); 318 } 319 320 public String getSigAlgName() 321 { 322 return sigAlgName; 323 } 324 325 public String getSigAlgOID() 326 { 327 return c.getSignatureAlgorithm().getObjectId().getId(); 328 } 329 330 public byte[] getSigAlgParams() 331 { 332 if (sigAlgParams != null) 333 { 334 byte[] tmp = new byte[sigAlgParams.length]; 335 336 System.arraycopy(sigAlgParams, 0, tmp, 0, tmp.length); 337 338 return tmp; 339 } 340 341 return null; 342 } 343 344 /** 345 * Returns a string representation of this CRL. 346 * 347 * @return a string representation of this CRL. 348 */ 349 public String toString() 350 { 351 StringBuffer buf = new StringBuffer(); 352 String nl = System.getProperty("line.separator"); 353 354 buf.append(" Version: ").append(this.getVersion()).append( 355 nl); 356 buf.append(" IssuerDN: ").append(this.getIssuerDN()) 357 .append(nl); 358 buf.append(" This update: ").append(this.getThisUpdate()) 359 .append(nl); 360 buf.append(" Next update: ").append(this.getNextUpdate()) 361 .append(nl); 362 buf.append(" Signature Algorithm: ").append(this.getSigAlgName()) 363 .append(nl); 364 365 byte[] sig = this.getSignature(); 366 367 buf.append(" Signature: ").append( 368 new String(Hex.encode(sig, 0, 20))).append(nl); 369 for (int i = 20; i < sig.length; i += 20) 370 { 371 if (i < sig.length - 20) 372 { 373 buf.append(" ").append( 374 new String(Hex.encode(sig, i, 20))).append(nl); 375 } 376 else 377 { 378 buf.append(" ").append( 379 new String(Hex.encode(sig, i, sig.length - i))).append(nl); 380 } 381 } 382 383 X509Extensions extensions = c.getTBSCertList().getExtensions(); 384 385 if (extensions != null) 386 { 387 Enumeration e = extensions.oids(); 388 389 if (e.hasMoreElements()) 390 { 391 buf.append(" Extensions: ").append(nl); 392 } 393 394 while (e.hasMoreElements()) 395 { 396 DERObjectIdentifier oid = (DERObjectIdentifier) e.nextElement(); 397 X509Extension ext = extensions.getExtension(oid); 398 399 if (ext.getValue() != null) 400 { 401 byte[] octs = ext.getValue().getOctets(); 402 ASN1InputStream dIn = new ASN1InputStream(octs); 403 buf.append(" critical(").append( 404 ext.isCritical()).append(") "); 405 try 406 { 407 if (oid.equals(X509Extensions.CRLNumber)) 408 { 409 buf.append( 410 new CRLNumber(DERInteger.getInstance( 411 dIn.readObject()).getPositiveValue())) 412 .append(nl); 413 } 414 else if (oid.equals(X509Extensions.DeltaCRLIndicator)) 415 { 416 buf.append( 417 "Base CRL: " 418 + new CRLNumber(DERInteger.getInstance( 419 dIn.readObject()).getPositiveValue())) 420 .append(nl); 421 } 422 else if (oid 423 .equals(X509Extensions.IssuingDistributionPoint)) 424 { 425 buf.append( 426 new IssuingDistributionPoint((ASN1Sequence) dIn 427 .readObject())).append(nl); 428 } 429 else if (oid 430 .equals(X509Extensions.CRLDistributionPoints)) 431 { 432 buf.append( 433 new CRLDistPoint((ASN1Sequence) dIn 434 .readObject())).append(nl); 435 } 436 else if (oid.equals(X509Extensions.FreshestCRL)) 437 { 438 buf.append( 439 new CRLDistPoint((ASN1Sequence) dIn 440 .readObject())).append(nl); 441 } 442 else 443 { 444 buf.append(oid.getId()); 445 buf.append(" value = ").append( 446 ASN1Dump.dumpAsString(dIn.readObject())) 447 .append(nl); 448 } 449 } 450 catch (Exception ex) 451 { 452 buf.append(oid.getId()); 453 buf.append(" value = ").append("*****").append(nl); 454 } 455 } 456 else 457 { 458 buf.append(nl); 459 } 460 } 461 } 462 Set set = getRevokedCertificates(); 463 if (set != null) 464 { 465 Iterator it = set.iterator(); 466 while (it.hasNext()) 467 { 468 buf.append(it.next()); 469 buf.append(nl); 470 } 471 } 472 return buf.toString(); 473 } 474 475 /** 476 * Checks whether the given certificate is on this CRL. 477 * 478 * @param cert the certificate to check for. 479 * @return true if the given certificate is on this CRL, 480 * false otherwise. 481 */ 482 public boolean isRevoked(Certificate cert) 483 { 484 if (!cert.getType().equals("X.509")) 485 { 486 throw new RuntimeException("X.509 CRL used with non X.509 Cert"); 487 } 488 489 TBSCertList.CRLEntry[] certs = c.getRevokedCertificates(); 490 491 if (certs != null) 492 { 493 BigInteger serial = ((X509Certificate)cert).getSerialNumber(); 494 495 for (int i = 0; i < certs.length; i++) 496 { 497 if (certs[i].getUserCertificate().getValue().equals(serial)) 498 { 499 return true; 500 } 501 } 502 } 503 504 return false; 505 } 506 507 private boolean isIndirectCRL() 508 throws CRLException 509 { 510 byte[] idp = getExtensionValue(X509Extensions.IssuingDistributionPoint.getId()); 511 boolean isIndirect = false; 512 try 513 { 514 if (idp != null) 515 { 516 isIndirect = IssuingDistributionPoint.getInstance( 517 X509ExtensionUtil.fromExtensionValue(idp)) 518 .isIndirectCRL(); 519 } 520 } 521 catch (Exception e) 522 { 523 throw new ExtCRLException( 524 "Exception reading IssuingDistributionPoint", e); 525 } 526 527 return isIndirect; 528 } 529} 530 531