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