J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun in the LICENSE file that accompanied this code. |
| 10 | * |
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 14 | * version 2 for more details (a copy is included in the LICENSE file that |
| 15 | * accompanied this code). |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License version |
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 20 | * |
| 21 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any questions. |
| 24 | */ |
| 25 | |
| 26 | package sun.security.x509; |
| 27 | |
| 28 | import java.io.InputStream; |
| 29 | import java.io.OutputStream; |
| 30 | import java.io.IOException; |
| 31 | import java.math.BigInteger; |
| 32 | import java.security.Principal; |
| 33 | import java.security.PublicKey; |
| 34 | import java.security.PrivateKey; |
| 35 | import java.security.Security; |
| 36 | import java.security.Signature; |
| 37 | import java.security.NoSuchAlgorithmException; |
| 38 | import java.security.InvalidKeyException; |
| 39 | import java.security.NoSuchProviderException; |
| 40 | import java.security.SignatureException; |
| 41 | import java.security.cert.Certificate; |
| 42 | import java.security.cert.X509CRL; |
| 43 | import java.security.cert.X509Certificate; |
| 44 | import java.security.cert.X509CRLEntry; |
| 45 | import java.security.cert.CRLException; |
| 46 | import java.util.*; |
| 47 | |
| 48 | import javax.security.auth.x500.X500Principal; |
| 49 | |
| 50 | import sun.security.provider.X509Factory; |
| 51 | import sun.security.util.*; |
| 52 | import sun.misc.HexDumpEncoder; |
| 53 | |
| 54 | /** |
| 55 | * <p> |
| 56 | * An implmentation for X509 CRL (Certificate Revocation List). |
| 57 | * <p> |
| 58 | * The X.509 v2 CRL format is described below in ASN.1: |
| 59 | * <pre> |
| 60 | * CertificateList ::= SEQUENCE { |
| 61 | * tbsCertList TBSCertList, |
| 62 | * signatureAlgorithm AlgorithmIdentifier, |
| 63 | * signature BIT STRING } |
| 64 | * </pre> |
| 65 | * More information can be found in |
| 66 | * <a href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: Internet X.509 |
| 67 | * Public Key Infrastructure Certificate and CRL Profile</a>. |
| 68 | * <p> |
| 69 | * The ASN.1 definition of <code>tbsCertList</code> is: |
| 70 | * <pre> |
| 71 | * TBSCertList ::= SEQUENCE { |
| 72 | * version Version OPTIONAL, |
| 73 | * -- if present, must be v2 |
| 74 | * signature AlgorithmIdentifier, |
| 75 | * issuer Name, |
| 76 | * thisUpdate ChoiceOfTime, |
| 77 | * nextUpdate ChoiceOfTime OPTIONAL, |
| 78 | * revokedCertificates SEQUENCE OF SEQUENCE { |
| 79 | * userCertificate CertificateSerialNumber, |
| 80 | * revocationDate ChoiceOfTime, |
| 81 | * crlEntryExtensions Extensions OPTIONAL |
| 82 | * -- if present, must be v2 |
| 83 | * } OPTIONAL, |
| 84 | * crlExtensions [0] EXPLICIT Extensions OPTIONAL |
| 85 | * -- if present, must be v2 |
| 86 | * } |
| 87 | * </pre> |
| 88 | * |
| 89 | * @author Hemma Prafullchandra |
| 90 | * @see X509CRL |
| 91 | */ |
| 92 | public class X509CRLImpl extends X509CRL { |
| 93 | |
| 94 | // CRL data, and its envelope |
| 95 | private byte[] signedCRL = null; // DER encoded crl |
| 96 | private byte[] signature = null; // raw signature bits |
| 97 | private byte[] tbsCertList = null; // DER encoded "to-be-signed" CRL |
| 98 | private AlgorithmId sigAlgId = null; // sig alg in CRL |
| 99 | |
| 100 | // crl information |
| 101 | private int version; |
| 102 | private AlgorithmId infoSigAlgId; // sig alg in "to-be-signed" crl |
| 103 | private X500Name issuer = null; |
| 104 | private X500Principal issuerPrincipal = null; |
| 105 | private Date thisUpdate = null; |
| 106 | private Date nextUpdate = null; |
| 107 | private Map<X509IssuerSerial,X509CRLEntry> revokedCerts = new LinkedHashMap<X509IssuerSerial,X509CRLEntry>(); |
| 108 | private CRLExtensions extensions = null; |
| 109 | private final static boolean isExplicit = true; |
| 110 | private static final long YR_2050 = 2524636800000L; |
| 111 | |
| 112 | private boolean readOnly = false; |
| 113 | |
| 114 | /** |
| 115 | * PublicKey that has previously been used to successfully verify |
| 116 | * the signature of this CRL. Null if the CRL has not |
| 117 | * yet been verified (successfully). |
| 118 | */ |
| 119 | private PublicKey verifiedPublicKey; |
| 120 | /** |
| 121 | * If verifiedPublicKey is not null, name of the provider used to |
| 122 | * successfully verify the signature of this CRL, or the |
| 123 | * empty String if no provider was explicitly specified. |
| 124 | */ |
| 125 | private String verifiedProvider; |
| 126 | |
| 127 | /** |
| 128 | * Not to be used. As it would lead to cases of uninitialized |
| 129 | * CRL objects. |
| 130 | */ |
| 131 | private X509CRLImpl() { } |
| 132 | |
| 133 | /** |
| 134 | * Unmarshals an X.509 CRL from its encoded form, parsing the encoded |
| 135 | * bytes. This form of constructor is used by agents which |
| 136 | * need to examine and use CRL contents. Note that the buffer |
| 137 | * must include only one CRL, and no "garbage" may be left at |
| 138 | * the end. |
| 139 | * |
| 140 | * @param crlData the encoded bytes, with no trailing padding. |
| 141 | * @exception CRLException on parsing errors. |
| 142 | */ |
| 143 | public X509CRLImpl(byte[] crlData) throws CRLException { |
| 144 | try { |
| 145 | parse(new DerValue(crlData)); |
| 146 | } catch (IOException e) { |
| 147 | signedCRL = null; |
| 148 | throw new CRLException("Parsing error: " + e.getMessage()); |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * Unmarshals an X.509 CRL from an DER value. |
| 154 | * |
| 155 | * @param val a DER value holding at least one CRL |
| 156 | * @exception CRLException on parsing errors. |
| 157 | */ |
| 158 | public X509CRLImpl(DerValue val) throws CRLException { |
| 159 | try { |
| 160 | parse(val); |
| 161 | } catch (IOException e) { |
| 162 | signedCRL = null; |
| 163 | throw new CRLException("Parsing error: " + e.getMessage()); |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | /** |
| 168 | * Unmarshals an X.509 CRL from an input stream. Only one CRL |
| 169 | * is expected at the end of the input stream. |
| 170 | * |
| 171 | * @param inStrm an input stream holding at least one CRL |
| 172 | * @exception CRLException on parsing errors. |
| 173 | */ |
| 174 | public X509CRLImpl(InputStream inStrm) throws CRLException { |
| 175 | try { |
| 176 | parse(new DerValue(inStrm)); |
| 177 | } catch (IOException e) { |
| 178 | signedCRL = null; |
| 179 | throw new CRLException("Parsing error: " + e.getMessage()); |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | /** |
| 184 | * Initial CRL constructor, no revoked certs, and no extensions. |
| 185 | * |
| 186 | * @param issuer the name of the CA issuing this CRL. |
| 187 | * @param thisUpdate the Date of this issue. |
| 188 | * @param nextUpdate the Date of the next CRL. |
| 189 | */ |
| 190 | public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate) { |
| 191 | this.issuer = issuer; |
| 192 | this.thisUpdate = thisDate; |
| 193 | this.nextUpdate = nextDate; |
| 194 | } |
| 195 | |
| 196 | /** |
| 197 | * CRL constructor, revoked certs, no extensions. |
| 198 | * |
| 199 | * @param issuer the name of the CA issuing this CRL. |
| 200 | * @param thisUpdate the Date of this issue. |
| 201 | * @param nextUpdate the Date of the next CRL. |
| 202 | * @param badCerts the array of CRL entries. |
| 203 | * |
| 204 | * @exception CRLException on parsing/construction errors. |
| 205 | */ |
| 206 | public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate, |
| 207 | X509CRLEntry[] badCerts) |
| 208 | throws CRLException |
| 209 | { |
| 210 | this.issuer = issuer; |
| 211 | this.thisUpdate = thisDate; |
| 212 | this.nextUpdate = nextDate; |
| 213 | if (badCerts != null) { |
| 214 | X500Principal crlIssuer = getIssuerX500Principal(); |
| 215 | X500Principal badCertIssuer = crlIssuer; |
| 216 | for (int i = 0; i < badCerts.length; i++) { |
| 217 | X509CRLEntryImpl badCert = (X509CRLEntryImpl)badCerts[i]; |
| 218 | try { |
| 219 | badCertIssuer = getCertIssuer(badCert, badCertIssuer); |
| 220 | } catch (IOException ioe) { |
| 221 | throw new CRLException(ioe); |
| 222 | } |
| 223 | badCert.setCertificateIssuer(crlIssuer, badCertIssuer); |
| 224 | X509IssuerSerial issuerSerial = new X509IssuerSerial |
| 225 | (badCertIssuer, badCert.getSerialNumber()); |
| 226 | this.revokedCerts.put(issuerSerial, badCert); |
| 227 | if (badCert.hasExtensions()) { |
| 228 | this.version = 1; |
| 229 | } |
| 230 | } |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | /** |
| 235 | * CRL constructor, revoked certs and extensions. |
| 236 | * |
| 237 | * @param issuer the name of the CA issuing this CRL. |
| 238 | * @param thisUpdate the Date of this issue. |
| 239 | * @param nextUpdate the Date of the next CRL. |
| 240 | * @param badCerts the array of CRL entries. |
| 241 | * @param crlExts the CRL extensions. |
| 242 | * |
| 243 | * @exception CRLException on parsing/construction errors. |
| 244 | */ |
| 245 | public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate, |
| 246 | X509CRLEntry[] badCerts, CRLExtensions crlExts) |
| 247 | throws CRLException |
| 248 | { |
| 249 | this(issuer, thisDate, nextDate, badCerts); |
| 250 | if (crlExts != null) { |
| 251 | this.extensions = crlExts; |
| 252 | this.version = 1; |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | /** |
| 257 | * Returned the encoding as an uncloned byte array. Callers must |
| 258 | * guarantee that they neither modify it nor expose it to untrusted |
| 259 | * code. |
| 260 | */ |
| 261 | public byte[] getEncodedInternal() throws CRLException { |
| 262 | if (signedCRL == null) { |
| 263 | throw new CRLException("Null CRL to encode"); |
| 264 | } |
| 265 | return signedCRL; |
| 266 | } |
| 267 | |
| 268 | /** |
| 269 | * Returns the ASN.1 DER encoded form of this CRL. |
| 270 | * |
| 271 | * @exception CRLException if an encoding error occurs. |
| 272 | */ |
| 273 | public byte[] getEncoded() throws CRLException { |
| 274 | return getEncodedInternal().clone(); |
| 275 | } |
| 276 | |
| 277 | /** |
| 278 | * Encodes the "to-be-signed" CRL to the OutputStream. |
| 279 | * |
| 280 | * @param out the OutputStream to write to. |
| 281 | * @exception CRLException on encoding errors. |
| 282 | */ |
| 283 | public void encodeInfo(OutputStream out) throws CRLException { |
| 284 | try { |
| 285 | DerOutputStream tmp = new DerOutputStream(); |
| 286 | DerOutputStream rCerts = new DerOutputStream(); |
| 287 | DerOutputStream seq = new DerOutputStream(); |
| 288 | |
| 289 | if (version != 0) // v2 crl encode version |
| 290 | tmp.putInteger(version); |
| 291 | infoSigAlgId.encode(tmp); |
| 292 | if ((version == 0) && (issuer.toString() == null)) |
| 293 | throw new CRLException("Null Issuer DN not allowed in v1 CRL"); |
| 294 | issuer.encode(tmp); |
| 295 | |
| 296 | if (thisUpdate.getTime() < YR_2050) |
| 297 | tmp.putUTCTime(thisUpdate); |
| 298 | else |
| 299 | tmp.putGeneralizedTime(thisUpdate); |
| 300 | |
| 301 | if (nextUpdate != null) { |
| 302 | if (nextUpdate.getTime() < YR_2050) |
| 303 | tmp.putUTCTime(nextUpdate); |
| 304 | else |
| 305 | tmp.putGeneralizedTime(nextUpdate); |
| 306 | } |
| 307 | |
| 308 | if (!revokedCerts.isEmpty()) { |
| 309 | for (X509CRLEntry entry : revokedCerts.values()) { |
| 310 | ((X509CRLEntryImpl)entry).encode(rCerts); |
| 311 | } |
| 312 | tmp.write(DerValue.tag_Sequence, rCerts); |
| 313 | } |
| 314 | |
| 315 | if (extensions != null) |
| 316 | extensions.encode(tmp, isExplicit); |
| 317 | |
| 318 | seq.write(DerValue.tag_Sequence, tmp); |
| 319 | |
| 320 | tbsCertList = seq.toByteArray(); |
| 321 | out.write(tbsCertList); |
| 322 | } catch (IOException e) { |
| 323 | throw new CRLException("Encoding error: " + e.getMessage()); |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | /** |
| 328 | * Verifies that this CRL was signed using the |
| 329 | * private key that corresponds to the given public key. |
| 330 | * |
| 331 | * @param key the PublicKey used to carry out the verification. |
| 332 | * |
| 333 | * @exception NoSuchAlgorithmException on unsupported signature |
| 334 | * algorithms. |
| 335 | * @exception InvalidKeyException on incorrect key. |
| 336 | * @exception NoSuchProviderException if there's no default provider. |
| 337 | * @exception SignatureException on signature errors. |
| 338 | * @exception CRLException on encoding errors. |
| 339 | */ |
| 340 | public void verify(PublicKey key) |
| 341 | throws CRLException, NoSuchAlgorithmException, InvalidKeyException, |
| 342 | NoSuchProviderException, SignatureException { |
| 343 | verify(key, ""); |
| 344 | } |
| 345 | |
| 346 | /** |
| 347 | * Verifies that this CRL was signed using the |
| 348 | * private key that corresponds to the given public key, |
| 349 | * and that the signature verification was computed by |
| 350 | * the given provider. |
| 351 | * |
| 352 | * @param key the PublicKey used to carry out the verification. |
| 353 | * @param sigProvider the name of the signature provider. |
| 354 | * |
| 355 | * @exception NoSuchAlgorithmException on unsupported signature |
| 356 | * algorithms. |
| 357 | * @exception InvalidKeyException on incorrect key. |
| 358 | * @exception NoSuchProviderException on incorrect provider. |
| 359 | * @exception SignatureException on signature errors. |
| 360 | * @exception CRLException on encoding errors. |
| 361 | */ |
| 362 | public synchronized void verify(PublicKey key, String sigProvider) |
| 363 | throws CRLException, NoSuchAlgorithmException, InvalidKeyException, |
| 364 | NoSuchProviderException, SignatureException { |
| 365 | |
| 366 | if (sigProvider == null) { |
| 367 | sigProvider = ""; |
| 368 | } |
| 369 | if ((verifiedPublicKey != null) && verifiedPublicKey.equals(key)) { |
| 370 | // this CRL has already been successfully verified using |
| 371 | // this public key. Make sure providers match, too. |
| 372 | if (sigProvider.equals(verifiedProvider)) { |
| 373 | return; |
| 374 | } |
| 375 | } |
| 376 | if (signedCRL == null) { |
| 377 | throw new CRLException("Uninitialized CRL"); |
| 378 | } |
| 379 | Signature sigVerf = null; |
| 380 | if (sigProvider.length() == 0) { |
| 381 | sigVerf = Signature.getInstance(sigAlgId.getName()); |
| 382 | } else { |
| 383 | sigVerf = Signature.getInstance(sigAlgId.getName(), sigProvider); |
| 384 | } |
| 385 | sigVerf.initVerify(key); |
| 386 | |
| 387 | if (tbsCertList == null) { |
| 388 | throw new CRLException("Uninitialized CRL"); |
| 389 | } |
| 390 | |
| 391 | sigVerf.update(tbsCertList, 0, tbsCertList.length); |
| 392 | |
| 393 | if (!sigVerf.verify(signature)) { |
| 394 | throw new SignatureException("Signature does not match."); |
| 395 | } |
| 396 | verifiedPublicKey = key; |
| 397 | verifiedProvider = sigProvider; |
| 398 | } |
| 399 | |
| 400 | /** |
| 401 | * Encodes an X.509 CRL, and signs it using the given key. |
| 402 | * |
| 403 | * @param key the private key used for signing. |
| 404 | * @param algorithm the name of the signature algorithm used. |
| 405 | * |
| 406 | * @exception NoSuchAlgorithmException on unsupported signature |
| 407 | * algorithms. |
| 408 | * @exception InvalidKeyException on incorrect key. |
| 409 | * @exception NoSuchProviderException on incorrect provider. |
| 410 | * @exception SignatureException on signature errors. |
| 411 | * @exception CRLException if any mandatory data was omitted. |
| 412 | */ |
| 413 | public void sign(PrivateKey key, String algorithm) |
| 414 | throws CRLException, NoSuchAlgorithmException, InvalidKeyException, |
| 415 | NoSuchProviderException, SignatureException { |
| 416 | sign(key, algorithm, null); |
| 417 | } |
| 418 | |
| 419 | /** |
| 420 | * Encodes an X.509 CRL, and signs it using the given key. |
| 421 | * |
| 422 | * @param key the private key used for signing. |
| 423 | * @param algorithm the name of the signature algorithm used. |
| 424 | * @param provider the name of the provider. |
| 425 | * |
| 426 | * @exception NoSuchAlgorithmException on unsupported signature |
| 427 | * algorithms. |
| 428 | * @exception InvalidKeyException on incorrect key. |
| 429 | * @exception NoSuchProviderException on incorrect provider. |
| 430 | * @exception SignatureException on signature errors. |
| 431 | * @exception CRLException if any mandatory data was omitted. |
| 432 | */ |
| 433 | public void sign(PrivateKey key, String algorithm, String provider) |
| 434 | throws CRLException, NoSuchAlgorithmException, InvalidKeyException, |
| 435 | NoSuchProviderException, SignatureException { |
| 436 | try { |
| 437 | if (readOnly) |
| 438 | throw new CRLException("cannot over-write existing CRL"); |
| 439 | Signature sigEngine = null; |
| 440 | if ((provider == null) || (provider.length() == 0)) |
| 441 | sigEngine = Signature.getInstance(algorithm); |
| 442 | else |
| 443 | sigEngine = Signature.getInstance(algorithm, provider); |
| 444 | |
| 445 | sigEngine.initSign(key); |
| 446 | |
| 447 | // in case the name is reset |
| 448 | sigAlgId = AlgorithmId.get(sigEngine.getAlgorithm()); |
| 449 | infoSigAlgId = sigAlgId; |
| 450 | |
| 451 | DerOutputStream out = new DerOutputStream(); |
| 452 | DerOutputStream tmp = new DerOutputStream(); |
| 453 | |
| 454 | // encode crl info |
| 455 | encodeInfo(tmp); |
| 456 | |
| 457 | // encode algorithm identifier |
| 458 | sigAlgId.encode(tmp); |
| 459 | |
| 460 | // Create and encode the signature itself. |
| 461 | sigEngine.update(tbsCertList, 0, tbsCertList.length); |
| 462 | signature = sigEngine.sign(); |
| 463 | tmp.putBitString(signature); |
| 464 | |
| 465 | // Wrap the signed data in a SEQUENCE { data, algorithm, sig } |
| 466 | out.write(DerValue.tag_Sequence, tmp); |
| 467 | signedCRL = out.toByteArray(); |
| 468 | readOnly = true; |
| 469 | |
| 470 | } catch (IOException e) { |
| 471 | throw new CRLException("Error while encoding data: " + |
| 472 | e.getMessage()); |
| 473 | } |
| 474 | } |
| 475 | |
| 476 | /** |
| 477 | * Returns a printable string of this CRL. |
| 478 | * |
| 479 | * @return value of this CRL in a printable form. |
| 480 | */ |
| 481 | public String toString() { |
| 482 | StringBuffer sb = new StringBuffer(); |
| 483 | sb.append("X.509 CRL v" + (version+1) + "\n"); |
| 484 | if (sigAlgId != null) |
| 485 | sb.append("Signature Algorithm: " + sigAlgId.toString() + |
| 486 | ", OID=" + (sigAlgId.getOID()).toString() + "\n"); |
| 487 | if (issuer != null) |
| 488 | sb.append("Issuer: " + issuer.toString() + "\n"); |
| 489 | if (thisUpdate != null) |
| 490 | sb.append("\nThis Update: " + thisUpdate.toString() + "\n"); |
| 491 | if (nextUpdate != null) |
| 492 | sb.append("Next Update: " + nextUpdate.toString() + "\n"); |
| 493 | if (revokedCerts.isEmpty()) |
| 494 | sb.append("\nNO certificates have been revoked\n"); |
| 495 | else { |
| 496 | sb.append("\nRevoked Certificates: " + revokedCerts.size()); |
| 497 | int i = 1; |
| 498 | for (Iterator<X509CRLEntry> iter = revokedCerts.values().iterator(); |
| 499 | iter.hasNext(); i++) |
| 500 | sb.append("\n[" + i + "] " + iter.next().toString()); |
| 501 | } |
| 502 | if (extensions != null) { |
| 503 | Collection<Extension> allExts = extensions.getAllExtensions(); |
| 504 | Object[] objs = allExts.toArray(); |
| 505 | sb.append("\nCRL Extensions: " + objs.length); |
| 506 | for (int i = 0; i < objs.length; i++) { |
| 507 | sb.append("\n[" + (i+1) + "]: "); |
| 508 | Extension ext = (Extension)objs[i]; |
| 509 | try { |
| 510 | if (OIDMap.getClass(ext.getExtensionId()) == null) { |
| 511 | sb.append(ext.toString()); |
| 512 | byte[] extValue = ext.getExtensionValue(); |
| 513 | if (extValue != null) { |
| 514 | DerOutputStream out = new DerOutputStream(); |
| 515 | out.putOctetString(extValue); |
| 516 | extValue = out.toByteArray(); |
| 517 | HexDumpEncoder enc = new HexDumpEncoder(); |
| 518 | sb.append("Extension unknown: " |
| 519 | + "DER encoded OCTET string =\n" |
| 520 | + enc.encodeBuffer(extValue) + "\n"); |
| 521 | } |
| 522 | } else |
| 523 | sb.append(ext.toString()); // sub-class exists |
| 524 | } catch (Exception e) { |
| 525 | sb.append(", Error parsing this extension"); |
| 526 | } |
| 527 | } |
| 528 | } |
| 529 | if (signature != null) { |
| 530 | HexDumpEncoder encoder = new HexDumpEncoder(); |
| 531 | sb.append("\nSignature:\n" + encoder.encodeBuffer(signature) |
| 532 | + "\n"); |
| 533 | } else |
| 534 | sb.append("NOT signed yet\n"); |
| 535 | return sb.toString(); |
| 536 | } |
| 537 | |
| 538 | /** |
| 539 | * Checks whether the given certificate is on this CRL. |
| 540 | * |
| 541 | * @param cert the certificate to check for. |
| 542 | * @return true if the given certificate is on this CRL, |
| 543 | * false otherwise. |
| 544 | */ |
| 545 | public boolean isRevoked(Certificate cert) { |
| 546 | if (revokedCerts.isEmpty() || (!(cert instanceof X509Certificate))) { |
| 547 | return false; |
| 548 | } |
| 549 | X509Certificate xcert = (X509Certificate) cert; |
| 550 | X509IssuerSerial issuerSerial = new X509IssuerSerial(xcert); |
| 551 | return revokedCerts.containsKey(issuerSerial); |
| 552 | } |
| 553 | |
| 554 | /** |
| 555 | * Gets the version number from this CRL. |
| 556 | * The ASN.1 definition for this is: |
| 557 | * <pre> |
| 558 | * Version ::= INTEGER { v1(0), v2(1), v3(2) } |
| 559 | * -- v3 does not apply to CRLs but appears for consistency |
| 560 | * -- with definition of Version for certs |
| 561 | * </pre> |
| 562 | * @return the version number, i.e. 1 or 2. |
| 563 | */ |
| 564 | public int getVersion() { |
| 565 | return version+1; |
| 566 | } |
| 567 | |
| 568 | /** |
| 569 | * Gets the issuer distinguished name from this CRL. |
| 570 | * The issuer name identifies the entity who has signed (and |
| 571 | * issued the CRL). The issuer name field contains an |
| 572 | * X.500 distinguished name (DN). |
| 573 | * The ASN.1 definition for this is: |
| 574 | * <pre> |
| 575 | * issuer Name |
| 576 | * |
| 577 | * Name ::= CHOICE { RDNSequence } |
| 578 | * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName |
| 579 | * RelativeDistinguishedName ::= |
| 580 | * SET OF AttributeValueAssertion |
| 581 | * |
| 582 | * AttributeValueAssertion ::= SEQUENCE { |
| 583 | * AttributeType, |
| 584 | * AttributeValue } |
| 585 | * AttributeType ::= OBJECT IDENTIFIER |
| 586 | * AttributeValue ::= ANY |
| 587 | * </pre> |
| 588 | * The Name describes a hierarchical name composed of attributes, |
| 589 | * such as country name, and corresponding values, such as US. |
| 590 | * The type of the component AttributeValue is determined by the |
| 591 | * AttributeType; in general it will be a directoryString. |
| 592 | * A directoryString is usually one of PrintableString, |
| 593 | * TeletexString or UniversalString. |
| 594 | * @return the issuer name. |
| 595 | */ |
| 596 | public Principal getIssuerDN() { |
| 597 | return (Principal)issuer; |
| 598 | } |
| 599 | |
| 600 | /** |
| 601 | * Return the issuer as X500Principal. Overrides method in X509CRL |
| 602 | * to provide a slightly more efficient version. |
| 603 | */ |
| 604 | public X500Principal getIssuerX500Principal() { |
| 605 | if (issuerPrincipal == null) { |
| 606 | issuerPrincipal = issuer.asX500Principal(); |
| 607 | } |
| 608 | return issuerPrincipal; |
| 609 | } |
| 610 | |
| 611 | /** |
| 612 | * Gets the thisUpdate date from the CRL. |
| 613 | * The ASN.1 definition for this is: |
| 614 | * |
| 615 | * @return the thisUpdate date from the CRL. |
| 616 | */ |
| 617 | public Date getThisUpdate() { |
| 618 | return (new Date(thisUpdate.getTime())); |
| 619 | } |
| 620 | |
| 621 | /** |
| 622 | * Gets the nextUpdate date from the CRL. |
| 623 | * |
| 624 | * @return the nextUpdate date from the CRL, or null if |
| 625 | * not present. |
| 626 | */ |
| 627 | public Date getNextUpdate() { |
| 628 | if (nextUpdate == null) |
| 629 | return null; |
| 630 | return (new Date(nextUpdate.getTime())); |
| 631 | } |
| 632 | |
| 633 | /** |
| 634 | * Gets the CRL entry with the given serial number from this CRL. |
| 635 | * |
| 636 | * @return the entry with the given serial number, or <code>null</code> if |
| 637 | * no such entry exists in the CRL. |
| 638 | * @see X509CRLEntry |
| 639 | */ |
| 640 | public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) { |
| 641 | if (revokedCerts.isEmpty()) { |
| 642 | return null; |
| 643 | } |
| 644 | // assume this is a direct CRL entry (cert and CRL issuer are the same) |
| 645 | X509IssuerSerial issuerSerial = new X509IssuerSerial |
| 646 | (getIssuerX500Principal(), serialNumber); |
| 647 | return revokedCerts.get(issuerSerial); |
| 648 | } |
| 649 | |
| 650 | /** |
| 651 | * Gets the CRL entry for the given certificate. |
| 652 | */ |
| 653 | public X509CRLEntry getRevokedCertificate(X509Certificate cert) { |
| 654 | if (revokedCerts.isEmpty()) { |
| 655 | return null; |
| 656 | } |
| 657 | X509IssuerSerial issuerSerial = new X509IssuerSerial(cert); |
| 658 | return revokedCerts.get(issuerSerial); |
| 659 | } |
| 660 | |
| 661 | /** |
| 662 | * Gets all the revoked certificates from the CRL. |
| 663 | * A Set of X509CRLEntry. |
| 664 | * |
| 665 | * @return all the revoked certificates or <code>null</code> if there are |
| 666 | * none. |
| 667 | * @see X509CRLEntry |
| 668 | */ |
| 669 | public Set<X509CRLEntry> getRevokedCertificates() { |
| 670 | if (revokedCerts.isEmpty()) { |
| 671 | return null; |
| 672 | } else { |
| 673 | return new HashSet<X509CRLEntry>(revokedCerts.values()); |
| 674 | } |
| 675 | } |
| 676 | |
| 677 | /** |
| 678 | * Gets the DER encoded CRL information, the |
| 679 | * <code>tbsCertList</code> from this CRL. |
| 680 | * This can be used to verify the signature independently. |
| 681 | * |
| 682 | * @return the DER encoded CRL information. |
| 683 | * @exception CRLException on encoding errors. |
| 684 | */ |
| 685 | public byte[] getTBSCertList() throws CRLException { |
| 686 | if (tbsCertList == null) |
| 687 | throw new CRLException("Uninitialized CRL"); |
| 688 | byte[] dup = new byte[tbsCertList.length]; |
| 689 | System.arraycopy(tbsCertList, 0, dup, 0, dup.length); |
| 690 | return dup; |
| 691 | } |
| 692 | |
| 693 | /** |
| 694 | * Gets the raw Signature bits from the CRL. |
| 695 | * |
| 696 | * @return the signature. |
| 697 | */ |
| 698 | public byte[] getSignature() { |
| 699 | if (signature == null) |
| 700 | return null; |
| 701 | byte[] dup = new byte[signature.length]; |
| 702 | System.arraycopy(signature, 0, dup, 0, dup.length); |
| 703 | return dup; |
| 704 | } |
| 705 | |
| 706 | /** |
| 707 | * Gets the signature algorithm name for the CRL |
| 708 | * signature algorithm. For example, the string "SHA1withDSA". |
| 709 | * The ASN.1 definition for this is: |
| 710 | * <pre> |
| 711 | * AlgorithmIdentifier ::= SEQUENCE { |
| 712 | * algorithm OBJECT IDENTIFIER, |
| 713 | * parameters ANY DEFINED BY algorithm OPTIONAL } |
| 714 | * -- contains a value of the type |
| 715 | * -- registered for use with the |
| 716 | * -- algorithm object identifier value |
| 717 | * </pre> |
| 718 | * |
| 719 | * @return the signature algorithm name. |
| 720 | */ |
| 721 | public String getSigAlgName() { |
| 722 | if (sigAlgId == null) |
| 723 | return null; |
| 724 | return sigAlgId.getName(); |
| 725 | } |
| 726 | |
| 727 | /** |
| 728 | * Gets the signature algorithm OID string from the CRL. |
| 729 | * An OID is represented by a set of positive whole number separated |
| 730 | * by ".", that means,<br> |
| 731 | * <positive whole number>.<positive whole number>.<...> |
| 732 | * For example, the string "1.2.840.10040.4.3" identifies the SHA-1 |
| 733 | * with DSA signature algorithm defined in |
| 734 | * <a href="http://www.ietf.org/rfc/rfc3279.txt">RFC 3279: Algorithms and |
| 735 | * Identifiers for the Internet X.509 Public Key Infrastructure Certificate |
| 736 | * and CRL Profile</a>. |
| 737 | * |
| 738 | * @return the signature algorithm oid string. |
| 739 | */ |
| 740 | public String getSigAlgOID() { |
| 741 | if (sigAlgId == null) |
| 742 | return null; |
| 743 | ObjectIdentifier oid = sigAlgId.getOID(); |
| 744 | return oid.toString(); |
| 745 | } |
| 746 | |
| 747 | /** |
| 748 | * Gets the DER encoded signature algorithm parameters from this |
| 749 | * CRL's signature algorithm. In most cases, the signature |
| 750 | * algorithm parameters are null, the parameters are usually |
| 751 | * supplied with the Public Key. |
| 752 | * |
| 753 | * @return the DER encoded signature algorithm parameters, or |
| 754 | * null if no parameters are present. |
| 755 | */ |
| 756 | public byte[] getSigAlgParams() { |
| 757 | if (sigAlgId == null) |
| 758 | return null; |
| 759 | try { |
| 760 | return sigAlgId.getEncodedParams(); |
| 761 | } catch (IOException e) { |
| 762 | return null; |
| 763 | } |
| 764 | } |
| 765 | |
| 766 | /** |
| 767 | * return the AuthorityKeyIdentifier, if any. |
| 768 | * |
| 769 | * @returns AuthorityKeyIdentifier or null |
| 770 | * (if no AuthorityKeyIdentifierExtension) |
| 771 | * @throws IOException on error |
| 772 | */ |
| 773 | public KeyIdentifier getAuthKeyId() throws IOException { |
| 774 | AuthorityKeyIdentifierExtension aki = getAuthKeyIdExtension(); |
| 775 | if (aki != null) { |
| 776 | KeyIdentifier keyId = (KeyIdentifier)aki.get(aki.KEY_ID); |
| 777 | return keyId; |
| 778 | } else { |
| 779 | return null; |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | /** |
| 784 | * return the AuthorityKeyIdentifierExtension, if any. |
| 785 | * |
| 786 | * @returns AuthorityKeyIdentifierExtension or null (if no such extension) |
| 787 | * @throws IOException on error |
| 788 | */ |
| 789 | public AuthorityKeyIdentifierExtension getAuthKeyIdExtension() |
| 790 | throws IOException { |
| 791 | Object obj = getExtension(PKIXExtensions.AuthorityKey_Id); |
| 792 | return (AuthorityKeyIdentifierExtension)obj; |
| 793 | } |
| 794 | |
| 795 | /** |
| 796 | * return the CRLNumberExtension, if any. |
| 797 | * |
| 798 | * @returns CRLNumberExtension or null (if no such extension) |
| 799 | * @throws IOException on error |
| 800 | */ |
| 801 | public CRLNumberExtension getCRLNumberExtension() throws IOException { |
| 802 | Object obj = getExtension(PKIXExtensions.CRLNumber_Id); |
| 803 | return (CRLNumberExtension)obj; |
| 804 | } |
| 805 | |
| 806 | /** |
| 807 | * return the CRL number from the CRLNumberExtension, if any. |
| 808 | * |
| 809 | * @returns number or null (if no such extension) |
| 810 | * @throws IOException on error |
| 811 | */ |
| 812 | public BigInteger getCRLNumber() throws IOException { |
| 813 | CRLNumberExtension numExt = getCRLNumberExtension(); |
| 814 | if (numExt != null) { |
| 815 | BigInteger num = (BigInteger)numExt.get(numExt.NUMBER); |
| 816 | return num; |
| 817 | } else { |
| 818 | return null; |
| 819 | } |
| 820 | } |
| 821 | |
| 822 | /** |
| 823 | * return the DeltaCRLIndicatorExtension, if any. |
| 824 | * |
| 825 | * @returns DeltaCRLIndicatorExtension or null (if no such extension) |
| 826 | * @throws IOException on error |
| 827 | */ |
| 828 | public DeltaCRLIndicatorExtension getDeltaCRLIndicatorExtension() |
| 829 | throws IOException { |
| 830 | |
| 831 | Object obj = getExtension(PKIXExtensions.DeltaCRLIndicator_Id); |
| 832 | return (DeltaCRLIndicatorExtension)obj; |
| 833 | } |
| 834 | |
| 835 | /** |
| 836 | * return the base CRL number from the DeltaCRLIndicatorExtension, if any. |
| 837 | * |
| 838 | * @returns number or null (if no such extension) |
| 839 | * @throws IOException on error |
| 840 | */ |
| 841 | public BigInteger getBaseCRLNumber() throws IOException { |
| 842 | DeltaCRLIndicatorExtension dciExt = getDeltaCRLIndicatorExtension(); |
| 843 | if (dciExt != null) { |
| 844 | BigInteger num = (BigInteger)dciExt.get(dciExt.NUMBER); |
| 845 | return num; |
| 846 | } else { |
| 847 | return null; |
| 848 | } |
| 849 | } |
| 850 | |
| 851 | /** |
| 852 | * return the IssuerAlternativeNameExtension, if any. |
| 853 | * |
| 854 | * @returns IssuerAlternativeNameExtension or null (if no such extension) |
| 855 | * @throws IOException on error |
| 856 | */ |
| 857 | public IssuerAlternativeNameExtension getIssuerAltNameExtension() |
| 858 | throws IOException { |
| 859 | Object obj = getExtension(PKIXExtensions.IssuerAlternativeName_Id); |
| 860 | return (IssuerAlternativeNameExtension)obj; |
| 861 | } |
| 862 | |
| 863 | /** |
| 864 | * return the IssuingDistributionPointExtension, if any. |
| 865 | * |
| 866 | * @returns IssuingDistributionPointExtension or null |
| 867 | * (if no such extension) |
| 868 | * @throws IOException on error |
| 869 | */ |
| 870 | public IssuingDistributionPointExtension |
| 871 | getIssuingDistributionPointExtension() throws IOException { |
| 872 | |
| 873 | Object obj = getExtension(PKIXExtensions.IssuingDistributionPoint_Id); |
| 874 | return (IssuingDistributionPointExtension) obj; |
| 875 | } |
| 876 | |
| 877 | /** |
| 878 | * Return true if a critical extension is found that is |
| 879 | * not supported, otherwise return false. |
| 880 | */ |
| 881 | public boolean hasUnsupportedCriticalExtension() { |
| 882 | if (extensions == null) |
| 883 | return false; |
| 884 | return extensions.hasUnsupportedCriticalExtension(); |
| 885 | } |
| 886 | |
| 887 | /** |
| 888 | * Gets a Set of the extension(s) marked CRITICAL in the |
| 889 | * CRL. In the returned set, each extension is represented by |
| 890 | * its OID string. |
| 891 | * |
| 892 | * @return a set of the extension oid strings in the |
| 893 | * CRL that are marked critical. |
| 894 | */ |
| 895 | public Set<String> getCriticalExtensionOIDs() { |
| 896 | if (extensions == null) { |
| 897 | return null; |
| 898 | } |
| 899 | Set<String> extSet = new HashSet<String>(); |
| 900 | for (Extension ex : extensions.getAllExtensions()) { |
| 901 | if (ex.isCritical()) { |
| 902 | extSet.add(ex.getExtensionId().toString()); |
| 903 | } |
| 904 | } |
| 905 | return extSet; |
| 906 | } |
| 907 | |
| 908 | /** |
| 909 | * Gets a Set of the extension(s) marked NON-CRITICAL in the |
| 910 | * CRL. In the returned set, each extension is represented by |
| 911 | * its OID string. |
| 912 | * |
| 913 | * @return a set of the extension oid strings in the |
| 914 | * CRL that are NOT marked critical. |
| 915 | */ |
| 916 | public Set<String> getNonCriticalExtensionOIDs() { |
| 917 | if (extensions == null) { |
| 918 | return null; |
| 919 | } |
| 920 | Set<String> extSet = new HashSet<String>(); |
| 921 | for (Extension ex : extensions.getAllExtensions()) { |
| 922 | if (!ex.isCritical()) { |
| 923 | extSet.add(ex.getExtensionId().toString()); |
| 924 | } |
| 925 | } |
| 926 | return extSet; |
| 927 | } |
| 928 | |
| 929 | /** |
| 930 | * Gets the DER encoded OCTET string for the extension value |
| 931 | * (<code>extnValue</code>) identified by the passed in oid String. |
| 932 | * The <code>oid</code> string is |
| 933 | * represented by a set of positive whole number separated |
| 934 | * by ".", that means,<br> |
| 935 | * <positive whole number>.<positive whole number>.<...> |
| 936 | * |
| 937 | * @param oid the Object Identifier value for the extension. |
| 938 | * @return the der encoded octet string of the extension value. |
| 939 | */ |
| 940 | public byte[] getExtensionValue(String oid) { |
| 941 | if (extensions == null) |
| 942 | return null; |
| 943 | try { |
| 944 | String extAlias = OIDMap.getName(new ObjectIdentifier(oid)); |
| 945 | Extension crlExt = null; |
| 946 | |
| 947 | if (extAlias == null) { // may be unknown |
| 948 | ObjectIdentifier findOID = new ObjectIdentifier(oid); |
| 949 | Extension ex = null; |
| 950 | ObjectIdentifier inCertOID; |
| 951 | for (Enumeration<Extension> e = extensions.getElements(); |
| 952 | e.hasMoreElements();) { |
| 953 | ex = e.nextElement(); |
| 954 | inCertOID = ex.getExtensionId(); |
| 955 | if (inCertOID.equals(findOID)) { |
| 956 | crlExt = ex; |
| 957 | break; |
| 958 | } |
| 959 | } |
| 960 | } else |
| 961 | crlExt = extensions.get(extAlias); |
| 962 | if (crlExt == null) |
| 963 | return null; |
| 964 | byte[] extData = crlExt.getExtensionValue(); |
| 965 | if (extData == null) |
| 966 | return null; |
| 967 | DerOutputStream out = new DerOutputStream(); |
| 968 | out.putOctetString(extData); |
| 969 | return out.toByteArray(); |
| 970 | } catch (Exception e) { |
| 971 | return null; |
| 972 | } |
| 973 | } |
| 974 | |
| 975 | /** |
| 976 | * get an extension |
| 977 | * |
| 978 | * @param oid ObjectIdentifier of extension desired |
| 979 | * @returns Object of type <extension> or null, if not found |
| 980 | * @throws IOException on error |
| 981 | */ |
| 982 | public Object getExtension(ObjectIdentifier oid) { |
| 983 | if (extensions == null) |
| 984 | return null; |
| 985 | |
| 986 | // XXX Consider cloning this |
| 987 | return extensions.get(OIDMap.getName(oid)); |
| 988 | } |
| 989 | |
| 990 | /* |
| 991 | * Parses an X.509 CRL, should be used only by constructors. |
| 992 | */ |
| 993 | private void parse(DerValue val) throws CRLException, IOException { |
| 994 | // check if can over write the certificate |
| 995 | if (readOnly) |
| 996 | throw new CRLException("cannot over-write existing CRL"); |
| 997 | |
| 998 | if ( val.getData() == null || val.tag != DerValue.tag_Sequence) |
| 999 | throw new CRLException("Invalid DER-encoded CRL data"); |
| 1000 | |
| 1001 | signedCRL = val.toByteArray(); |
| 1002 | DerValue seq[] = new DerValue[3]; |
| 1003 | |
| 1004 | seq[0] = val.data.getDerValue(); |
| 1005 | seq[1] = val.data.getDerValue(); |
| 1006 | seq[2] = val.data.getDerValue(); |
| 1007 | |
| 1008 | if (val.data.available() != 0) |
| 1009 | throw new CRLException("signed overrun, bytes = " |
| 1010 | + val.data.available()); |
| 1011 | |
| 1012 | if (seq[0].tag != DerValue.tag_Sequence) |
| 1013 | throw new CRLException("signed CRL fields invalid"); |
| 1014 | |
| 1015 | sigAlgId = AlgorithmId.parse(seq[1]); |
| 1016 | signature = seq[2].getBitString(); |
| 1017 | |
| 1018 | if (seq[1].data.available() != 0) |
| 1019 | throw new CRLException("AlgorithmId field overrun"); |
| 1020 | |
| 1021 | if (seq[2].data.available() != 0) |
| 1022 | throw new CRLException("Signature field overrun"); |
| 1023 | |
| 1024 | // the tbsCertsList |
| 1025 | tbsCertList = seq[0].toByteArray(); |
| 1026 | |
| 1027 | // parse the information |
| 1028 | DerInputStream derStrm = seq[0].data; |
| 1029 | DerValue tmp; |
| 1030 | byte nextByte; |
| 1031 | |
| 1032 | // version (optional if v1) |
| 1033 | version = 0; // by default, version = v1 == 0 |
| 1034 | nextByte = (byte)derStrm.peekByte(); |
| 1035 | if (nextByte == DerValue.tag_Integer) { |
| 1036 | version = derStrm.getInteger(); |
| 1037 | if (version != 1) // i.e. v2 |
| 1038 | throw new CRLException("Invalid version"); |
| 1039 | } |
| 1040 | tmp = derStrm.getDerValue(); |
| 1041 | |
| 1042 | // signature |
| 1043 | AlgorithmId tmpId = AlgorithmId.parse(tmp); |
| 1044 | |
| 1045 | // the "inner" and "outer" signature algorithms must match |
| 1046 | if (! tmpId.equals(sigAlgId)) |
| 1047 | throw new CRLException("Signature algorithm mismatch"); |
| 1048 | infoSigAlgId = tmpId; |
| 1049 | |
| 1050 | // issuer |
| 1051 | issuer = new X500Name(derStrm); |
| 1052 | if (issuer.isEmpty()) { |
| 1053 | throw new CRLException("Empty issuer DN not allowed in X509CRLs"); |
| 1054 | } |
| 1055 | |
| 1056 | // thisUpdate |
| 1057 | // check if UTCTime encoded or GeneralizedTime |
| 1058 | |
| 1059 | nextByte = (byte)derStrm.peekByte(); |
| 1060 | if (nextByte == DerValue.tag_UtcTime) { |
| 1061 | thisUpdate = derStrm.getUTCTime(); |
| 1062 | } else if (nextByte == DerValue.tag_GeneralizedTime) { |
| 1063 | thisUpdate = derStrm.getGeneralizedTime(); |
| 1064 | } else { |
| 1065 | throw new CRLException("Invalid encoding for thisUpdate" |
| 1066 | + " (tag=" + nextByte + ")"); |
| 1067 | } |
| 1068 | |
| 1069 | if (derStrm.available() == 0) |
| 1070 | return; // done parsing no more optional fields present |
| 1071 | |
| 1072 | // nextUpdate (optional) |
| 1073 | nextByte = (byte)derStrm.peekByte(); |
| 1074 | if (nextByte == DerValue.tag_UtcTime) { |
| 1075 | nextUpdate = derStrm.getUTCTime(); |
| 1076 | } else if (nextByte == DerValue.tag_GeneralizedTime) { |
| 1077 | nextUpdate = derStrm.getGeneralizedTime(); |
| 1078 | } // else it is not present |
| 1079 | |
| 1080 | if (derStrm.available() == 0) |
| 1081 | return; // done parsing no more optional fields present |
| 1082 | |
| 1083 | // revokedCertificates (optional) |
| 1084 | nextByte = (byte)derStrm.peekByte(); |
| 1085 | if ((nextByte == DerValue.tag_SequenceOf) |
| 1086 | && (! ((nextByte & 0x0c0) == 0x080))) { |
| 1087 | DerValue[] badCerts = derStrm.getSequence(4); |
| 1088 | |
| 1089 | X500Principal crlIssuer = getIssuerX500Principal(); |
| 1090 | X500Principal badCertIssuer = crlIssuer; |
| 1091 | for (int i = 0; i < badCerts.length; i++) { |
| 1092 | X509CRLEntryImpl entry = new X509CRLEntryImpl(badCerts[i]); |
| 1093 | badCertIssuer = getCertIssuer(entry, badCertIssuer); |
| 1094 | entry.setCertificateIssuer(crlIssuer, badCertIssuer); |
| 1095 | X509IssuerSerial issuerSerial = new X509IssuerSerial |
| 1096 | (badCertIssuer, entry.getSerialNumber()); |
| 1097 | revokedCerts.put(issuerSerial, entry); |
| 1098 | } |
| 1099 | } |
| 1100 | |
| 1101 | if (derStrm.available() == 0) |
| 1102 | return; // done parsing no extensions |
| 1103 | |
| 1104 | // crlExtensions (optional) |
| 1105 | tmp = derStrm.getDerValue(); |
| 1106 | if (tmp.isConstructed() && tmp.isContextSpecific((byte)0)) { |
| 1107 | extensions = new CRLExtensions(tmp.data); |
| 1108 | } |
| 1109 | readOnly = true; |
| 1110 | } |
| 1111 | |
| 1112 | /** |
| 1113 | * Extract the issuer X500Principal from an X509CRL. Parses the encoded |
| 1114 | * form of the CRL to preserve the principal's ASN.1 encoding. |
| 1115 | * |
| 1116 | * Called by java.security.cert.X509CRL.getIssuerX500Principal(). |
| 1117 | */ |
| 1118 | public static X500Principal getIssuerX500Principal(X509CRL crl) { |
| 1119 | try { |
| 1120 | byte[] encoded = crl.getEncoded(); |
| 1121 | DerInputStream derIn = new DerInputStream(encoded); |
| 1122 | DerValue tbsCert = derIn.getSequence(3)[0]; |
| 1123 | DerInputStream tbsIn = tbsCert.data; |
| 1124 | |
| 1125 | DerValue tmp; |
| 1126 | // skip version number if present |
| 1127 | byte nextByte = (byte)tbsIn.peekByte(); |
| 1128 | if (nextByte == DerValue.tag_Integer) { |
| 1129 | tmp = tbsIn.getDerValue(); |
| 1130 | } |
| 1131 | |
| 1132 | tmp = tbsIn.getDerValue(); // skip signature |
| 1133 | tmp = tbsIn.getDerValue(); // issuer |
| 1134 | byte[] principalBytes = tmp.toByteArray(); |
| 1135 | return new X500Principal(principalBytes); |
| 1136 | } catch (Exception e) { |
| 1137 | throw new RuntimeException("Could not parse issuer", e); |
| 1138 | } |
| 1139 | } |
| 1140 | |
| 1141 | /** |
| 1142 | * Returned the encoding of the given certificate for internal use. |
| 1143 | * Callers must guarantee that they neither modify it nor expose it |
| 1144 | * to untrusted code. Uses getEncodedInternal() if the certificate |
| 1145 | * is instance of X509CertImpl, getEncoded() otherwise. |
| 1146 | */ |
| 1147 | public static byte[] getEncodedInternal(X509CRL crl) throws CRLException { |
| 1148 | if (crl instanceof X509CRLImpl) { |
| 1149 | return ((X509CRLImpl)crl).getEncodedInternal(); |
| 1150 | } else { |
| 1151 | return crl.getEncoded(); |
| 1152 | } |
| 1153 | } |
| 1154 | |
| 1155 | /** |
| 1156 | * Utility method to convert an arbitrary instance of X509CRL |
| 1157 | * to a X509CRLImpl. Does a cast if possible, otherwise reparses |
| 1158 | * the encoding. |
| 1159 | */ |
| 1160 | public static X509CRLImpl toImpl(X509CRL crl) |
| 1161 | throws CRLException { |
| 1162 | if (crl instanceof X509CRLImpl) { |
| 1163 | return (X509CRLImpl)crl; |
| 1164 | } else { |
| 1165 | return X509Factory.intern(crl); |
| 1166 | } |
| 1167 | } |
| 1168 | |
| 1169 | /** |
| 1170 | * Returns the X500 certificate issuer DN of a CRL entry. |
| 1171 | * |
| 1172 | * @param entry the entry to check |
| 1173 | * @param prevCertIssuer the previous entry's certificate issuer |
| 1174 | * @return the X500Principal in a CertificateIssuerExtension, or |
| 1175 | * prevCertIssuer if it does not exist |
| 1176 | */ |
| 1177 | private X500Principal getCertIssuer(X509CRLEntryImpl entry, |
| 1178 | X500Principal prevCertIssuer) throws IOException { |
| 1179 | |
| 1180 | CertificateIssuerExtension ciExt = |
| 1181 | entry.getCertificateIssuerExtension(); |
| 1182 | if (ciExt != null) { |
| 1183 | GeneralNames names = (GeneralNames) |
| 1184 | ciExt.get(CertificateIssuerExtension.ISSUER); |
| 1185 | X500Name issuerDN = (X500Name) names.get(0).getName(); |
| 1186 | return issuerDN.asX500Principal(); |
| 1187 | } else { |
| 1188 | return prevCertIssuer; |
| 1189 | } |
| 1190 | } |
| 1191 | |
| 1192 | /** |
| 1193 | * Immutable X.509 Certificate Issuer DN and serial number pair |
| 1194 | */ |
| 1195 | private final static class X509IssuerSerial { |
| 1196 | final X500Principal issuer; |
| 1197 | final BigInteger serial; |
| 1198 | volatile int hashcode = 0; |
| 1199 | |
| 1200 | /** |
| 1201 | * Create an X509IssuerSerial. |
| 1202 | * |
| 1203 | * @param issuer the issuer DN |
| 1204 | * @param serial the serial number |
| 1205 | */ |
| 1206 | X509IssuerSerial(X500Principal issuer, BigInteger serial) { |
| 1207 | this.issuer = issuer; |
| 1208 | this.serial = serial; |
| 1209 | } |
| 1210 | |
| 1211 | /** |
| 1212 | * Construct an X509IssuerSerial from an X509Certificate. |
| 1213 | */ |
| 1214 | X509IssuerSerial(X509Certificate cert) { |
| 1215 | this(cert.getIssuerX500Principal(), cert.getSerialNumber()); |
| 1216 | } |
| 1217 | |
| 1218 | /** |
| 1219 | * Returns the issuer. |
| 1220 | * |
| 1221 | * @return the issuer |
| 1222 | */ |
| 1223 | X500Principal getIssuer() { |
| 1224 | return issuer; |
| 1225 | } |
| 1226 | |
| 1227 | /** |
| 1228 | * Returns the serial number. |
| 1229 | * |
| 1230 | * @return the serial number |
| 1231 | */ |
| 1232 | BigInteger getSerial() { |
| 1233 | return serial; |
| 1234 | } |
| 1235 | |
| 1236 | /** |
| 1237 | * Compares this X509Serial with another and returns true if they |
| 1238 | * are equivalent. |
| 1239 | * |
| 1240 | * @param o the other object to compare with |
| 1241 | * @return true if equal, false otherwise |
| 1242 | */ |
| 1243 | public boolean equals(Object o) { |
| 1244 | if (o == this) { |
| 1245 | return true; |
| 1246 | } |
| 1247 | |
| 1248 | if (!(o instanceof X509IssuerSerial)) { |
| 1249 | return false; |
| 1250 | } |
| 1251 | |
| 1252 | X509IssuerSerial other = (X509IssuerSerial) o; |
| 1253 | if (serial.equals(other.getSerial()) && |
| 1254 | issuer.equals(other.getIssuer())) { |
| 1255 | return true; |
| 1256 | } |
| 1257 | return false; |
| 1258 | } |
| 1259 | |
| 1260 | /** |
| 1261 | * Returns a hash code value for this X509IssuerSerial. |
| 1262 | * |
| 1263 | * @return the hash code value |
| 1264 | */ |
| 1265 | public int hashCode() { |
| 1266 | if (hashcode == 0) { |
| 1267 | int result = 17; |
| 1268 | result = 37*result + issuer.hashCode(); |
| 1269 | result = 37*result + serial.hashCode(); |
| 1270 | hashcode = result; |
| 1271 | } |
| 1272 | return hashcode; |
| 1273 | } |
| 1274 | } |
| 1275 | } |