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