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