J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 1996-2006 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.BufferedReader; |
| 29 | import java.io.BufferedInputStream; |
| 30 | import java.io.ByteArrayOutputStream; |
| 31 | import java.io.IOException; |
| 32 | import java.io.InputStream; |
| 33 | import java.io.InputStreamReader; |
| 34 | import java.io.OutputStream; |
| 35 | import java.math.BigInteger; |
| 36 | import java.security.*; |
| 37 | import java.security.cert.*; |
| 38 | import java.security.cert.Certificate; |
| 39 | import java.util.*; |
| 40 | |
| 41 | import javax.security.auth.x500.X500Principal; |
| 42 | |
| 43 | import sun.misc.HexDumpEncoder; |
| 44 | import sun.misc.BASE64Decoder; |
| 45 | import sun.security.util.*; |
| 46 | import sun.security.provider.X509Factory; |
| 47 | |
| 48 | /** |
| 49 | * The X509CertImpl class represents an X.509 certificate. These certificates |
| 50 | * are widely used to support authentication and other functionality in |
| 51 | * Internet security systems. Common applications include Privacy Enhanced |
| 52 | * Mail (PEM), Transport Layer Security (SSL), code signing for trusted |
| 53 | * software distribution, and Secure Electronic Transactions (SET). There |
| 54 | * is a commercial infrastructure ready to manage large scale deployments |
| 55 | * of X.509 identity certificates. |
| 56 | * |
| 57 | * <P>These certificates are managed and vouched for by <em>Certificate |
| 58 | * Authorities</em> (CAs). CAs are services which create certificates by |
| 59 | * placing data in the X.509 standard format and then digitally signing |
| 60 | * that data. Such signatures are quite difficult to forge. CAs act as |
| 61 | * trusted third parties, making introductions between agents who have no |
| 62 | * direct knowledge of each other. CA certificates are either signed by |
| 63 | * themselves, or by some other CA such as a "root" CA. |
| 64 | * |
| 65 | * <P>RFC 1422 is very informative, though it does not describe much |
| 66 | * of the recent work being done with X.509 certificates. That includes |
| 67 | * a 1996 version (X.509v3) and a variety of enhancements being made to |
| 68 | * facilitate an explosion of personal certificates used as "Internet |
| 69 | * Drivers' Licences", or with SET for credit card transactions. |
| 70 | * |
| 71 | * <P>More recent work includes the IETF PKIX Working Group efforts, |
| 72 | * especially RFC2459. |
| 73 | * |
| 74 | * @author Dave Brownell |
| 75 | * @author Amit Kapoor |
| 76 | * @author Hemma Prafullchandra |
| 77 | * @see X509CertInfo |
| 78 | */ |
| 79 | public class X509CertImpl extends X509Certificate implements DerEncoder { |
| 80 | |
| 81 | private static final long serialVersionUID = -3457612960190864406L; |
| 82 | |
| 83 | private static final String DOT = "."; |
| 84 | /** |
| 85 | * Public attribute names. |
| 86 | */ |
| 87 | public static final String NAME = "x509"; |
| 88 | public static final String INFO = X509CertInfo.NAME; |
| 89 | public static final String ALG_ID = "algorithm"; |
| 90 | public static final String SIGNATURE = "signature"; |
| 91 | public static final String SIGNED_CERT = "signed_cert"; |
| 92 | |
| 93 | /** |
| 94 | * The following are defined for ease-of-use. These |
| 95 | * are the most frequently retrieved attributes. |
| 96 | */ |
| 97 | // x509.info.subject.dname |
| 98 | public static final String SUBJECT_DN = NAME + DOT + INFO + DOT + |
| 99 | X509CertInfo.SUBJECT + DOT + |
| 100 | CertificateSubjectName.DN_NAME; |
| 101 | // x509.info.issuer.dname |
| 102 | public static final String ISSUER_DN = NAME + DOT + INFO + DOT + |
| 103 | X509CertInfo.ISSUER + DOT + |
| 104 | CertificateIssuerName.DN_NAME; |
| 105 | // x509.info.serialNumber.number |
| 106 | public static final String SERIAL_ID = NAME + DOT + INFO + DOT + |
| 107 | X509CertInfo.SERIAL_NUMBER + DOT + |
| 108 | CertificateSerialNumber.NUMBER; |
| 109 | // x509.info.key.value |
| 110 | public static final String PUBLIC_KEY = NAME + DOT + INFO + DOT + |
| 111 | X509CertInfo.KEY + DOT + |
| 112 | CertificateX509Key.KEY; |
| 113 | |
| 114 | // x509.info.version.value |
| 115 | public static final String VERSION = NAME + DOT + INFO + DOT + |
| 116 | X509CertInfo.VERSION + DOT + |
| 117 | CertificateVersion.VERSION; |
| 118 | |
| 119 | // x509.algorithm |
| 120 | public static final String SIG_ALG = NAME + DOT + ALG_ID; |
| 121 | |
| 122 | // x509.signature |
| 123 | public static final String SIG = NAME + DOT + SIGNATURE; |
| 124 | |
| 125 | // when we sign and decode we set this to true |
| 126 | // this is our means to make certificates immutable |
| 127 | private boolean readOnly = false; |
| 128 | |
| 129 | // Certificate data, and its envelope |
| 130 | private byte[] signedCert = null; |
| 131 | protected X509CertInfo info = null; |
| 132 | protected AlgorithmId algId = null; |
| 133 | protected byte[] signature = null; |
| 134 | |
| 135 | // recognized extension OIDS |
| 136 | private static final String KEY_USAGE_OID = "2.5.29.15"; |
| 137 | private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37"; |
| 138 | private static final String BASIC_CONSTRAINT_OID = "2.5.29.19"; |
| 139 | private static final String SUBJECT_ALT_NAME_OID = "2.5.29.17"; |
| 140 | private static final String ISSUER_ALT_NAME_OID = "2.5.29.18"; |
| 141 | private static final String AUTH_INFO_ACCESS_OID = "1.3.6.1.5.5.7.1.1"; |
| 142 | |
| 143 | // number of standard key usage bits. |
| 144 | private static final int NUM_STANDARD_KEY_USAGE = 9; |
| 145 | |
| 146 | // SubjectAlterntativeNames cache |
| 147 | private Collection<List<?>> subjectAlternativeNames; |
| 148 | |
| 149 | // IssuerAlternativeNames cache |
| 150 | private Collection<List<?>> issuerAlternativeNames; |
| 151 | |
| 152 | // ExtendedKeyUsage cache |
| 153 | private List<String> extKeyUsage; |
| 154 | |
| 155 | // AuthorityInformationAccess cache |
| 156 | private Set<AccessDescription> authInfoAccess; |
| 157 | |
| 158 | /** |
| 159 | * PublicKey that has previously been used to verify |
| 160 | * the signature of this certificate. Null if the certificate has not |
| 161 | * yet been verified. |
| 162 | */ |
| 163 | private PublicKey verifiedPublicKey; |
| 164 | /** |
| 165 | * If verifiedPublicKey is not null, name of the provider used to |
| 166 | * successfully verify the signature of this certificate, or the |
| 167 | * empty String if no provider was explicitly specified. |
| 168 | */ |
| 169 | private String verifiedProvider; |
| 170 | /** |
| 171 | * If verifiedPublicKey is not null, result of the verification using |
| 172 | * verifiedPublicKey and verifiedProvider. If true, verification was |
| 173 | * successful, if false, it failed. |
| 174 | */ |
| 175 | private boolean verificationResult; |
| 176 | |
| 177 | /** |
| 178 | * Default constructor. |
| 179 | */ |
| 180 | public X509CertImpl() { } |
| 181 | |
| 182 | /** |
| 183 | * Unmarshals a certificate from its encoded form, parsing the |
| 184 | * encoded bytes. This form of constructor is used by agents which |
| 185 | * need to examine and use certificate contents. That is, this is |
| 186 | * one of the more commonly used constructors. Note that the buffer |
| 187 | * must include only a certificate, and no "garbage" may be left at |
| 188 | * the end. If you need to ignore data at the end of a certificate, |
| 189 | * use another constructor. |
| 190 | * |
| 191 | * @param certData the encoded bytes, with no trailing padding. |
| 192 | * @exception CertificateException on parsing and initialization errors. |
| 193 | */ |
| 194 | public X509CertImpl(byte[] certData) throws CertificateException { |
| 195 | try { |
| 196 | parse(new DerValue(certData)); |
| 197 | } catch (IOException e) { |
| 198 | signedCert = null; |
| 199 | CertificateException ce = new |
| 200 | CertificateException("Unable to initialize, " + e); |
| 201 | ce.initCause(e); |
| 202 | throw ce; |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | /** |
| 207 | * unmarshals an X.509 certificate from an input stream. If the |
| 208 | * certificate is RFC1421 hex-encoded, then it must begin with |
| 209 | * the line X509Factory.BEGIN_CERT and end with the line |
| 210 | * X509Factory.END_CERT. |
| 211 | * |
| 212 | * @param in an input stream holding at least one certificate that may |
| 213 | * be either DER-encoded or RFC1421 hex-encoded version of the |
| 214 | * DER-encoded certificate. |
| 215 | * @exception CertificateException on parsing and initialization errors. |
| 216 | */ |
| 217 | public X509CertImpl(InputStream in) throws CertificateException { |
| 218 | |
| 219 | DerValue der = null; |
| 220 | |
| 221 | BufferedInputStream inBuffered = new BufferedInputStream(in); |
| 222 | |
| 223 | // First try reading stream as HEX-encoded DER-encoded bytes, |
| 224 | // since not mistakable for raw DER |
| 225 | try { |
| 226 | inBuffered.mark(Integer.MAX_VALUE); |
| 227 | der = readRFC1421Cert(inBuffered); |
| 228 | } catch (IOException ioe) { |
| 229 | try { |
| 230 | // Next, try reading stream as raw DER-encoded bytes |
| 231 | inBuffered.reset(); |
| 232 | der = new DerValue(inBuffered); |
| 233 | } catch (IOException ioe1) { |
| 234 | CertificateException ce = new |
| 235 | CertificateException("Input stream must be " + |
| 236 | "either DER-encoded bytes " + |
| 237 | "or RFC1421 hex-encoded " + |
| 238 | "DER-encoded bytes: " + |
| 239 | ioe1.getMessage()); |
| 240 | ce.initCause(ioe1); |
| 241 | throw ce; |
| 242 | } |
| 243 | } |
| 244 | try { |
| 245 | parse(der); |
| 246 | } catch (IOException ioe) { |
| 247 | signedCert = null; |
| 248 | CertificateException ce = new |
| 249 | CertificateException("Unable to parse DER value of " + |
| 250 | "certificate, " + ioe); |
| 251 | ce.initCause(ioe); |
| 252 | throw ce; |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | /** |
| 257 | * read input stream as HEX-encoded DER-encoded bytes |
| 258 | * |
| 259 | * @param in InputStream to read |
| 260 | * @returns DerValue corresponding to decoded HEX-encoded bytes |
| 261 | * @throws IOException if stream can not be interpreted as RFC1421 |
| 262 | * encoded bytes |
| 263 | */ |
| 264 | private DerValue readRFC1421Cert(InputStream in) throws IOException { |
| 265 | DerValue der = null; |
| 266 | String line = null; |
| 267 | BufferedReader certBufferedReader = |
| 268 | new BufferedReader(new InputStreamReader(in, "ASCII")); |
| 269 | try { |
| 270 | line = certBufferedReader.readLine(); |
| 271 | } catch (IOException ioe1) { |
| 272 | throw new IOException("Unable to read InputStream: " + |
| 273 | ioe1.getMessage()); |
| 274 | } |
| 275 | if (line.equals(X509Factory.BEGIN_CERT)) { |
| 276 | /* stream appears to be hex-encoded bytes */ |
| 277 | BASE64Decoder decoder = new BASE64Decoder(); |
| 278 | ByteArrayOutputStream decstream = new ByteArrayOutputStream(); |
| 279 | try { |
| 280 | while ((line = certBufferedReader.readLine()) != null) { |
| 281 | if (line.equals(X509Factory.END_CERT)) { |
| 282 | der = new DerValue(decstream.toByteArray()); |
| 283 | break; |
| 284 | } else { |
| 285 | decstream.write(decoder.decodeBuffer(line)); |
| 286 | } |
| 287 | } |
| 288 | } catch (IOException ioe2) { |
| 289 | throw new IOException("Unable to read InputStream: " |
| 290 | + ioe2.getMessage()); |
| 291 | } |
| 292 | } else { |
| 293 | throw new IOException("InputStream is not RFC1421 hex-encoded " + |
| 294 | "DER bytes"); |
| 295 | } |
| 296 | return der; |
| 297 | } |
| 298 | |
| 299 | /** |
| 300 | * Construct an initialized X509 Certificate. The certificate is stored |
| 301 | * in raw form and has to be signed to be useful. |
| 302 | * |
| 303 | * @params info the X509CertificateInfo which the Certificate is to be |
| 304 | * created from. |
| 305 | */ |
| 306 | public X509CertImpl(X509CertInfo certInfo) { |
| 307 | this.info = certInfo; |
| 308 | } |
| 309 | |
| 310 | /** |
| 311 | * Unmarshal a certificate from its encoded form, parsing a DER value. |
| 312 | * This form of constructor is used by agents which need to examine |
| 313 | * and use certificate contents. |
| 314 | * |
| 315 | * @param derVal the der value containing the encoded cert. |
| 316 | * @exception CertificateException on parsing and initialization errors. |
| 317 | */ |
| 318 | public X509CertImpl(DerValue derVal) throws CertificateException { |
| 319 | try { |
| 320 | parse(derVal); |
| 321 | } catch (IOException e) { |
| 322 | signedCert = null; |
| 323 | CertificateException ce = new |
| 324 | CertificateException("Unable to initialize, " + e); |
| 325 | ce.initCause(e); |
| 326 | throw ce; |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | /** |
| 331 | * Appends the certificate to an output stream. |
| 332 | * |
| 333 | * @param out an input stream to which the certificate is appended. |
| 334 | * @exception CertificateEncodingException on encoding errors. |
| 335 | */ |
| 336 | public void encode(OutputStream out) |
| 337 | throws CertificateEncodingException { |
| 338 | if (signedCert == null) |
| 339 | throw new CertificateEncodingException( |
| 340 | "Null certificate to encode"); |
| 341 | try { |
| 342 | out.write(signedCert.clone()); |
| 343 | } catch (IOException e) { |
| 344 | throw new CertificateEncodingException(e.toString()); |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | /** |
| 349 | * DER encode this object onto an output stream. |
| 350 | * Implements the <code>DerEncoder</code> interface. |
| 351 | * |
| 352 | * @param out the output stream on which to write the DER encoding. |
| 353 | * |
| 354 | * @exception IOException on encoding error. |
| 355 | */ |
| 356 | public void derEncode(OutputStream out) throws IOException { |
| 357 | if (signedCert == null) |
| 358 | throw new IOException("Null certificate to encode"); |
| 359 | out.write(signedCert.clone()); |
| 360 | } |
| 361 | |
| 362 | /** |
| 363 | * Returns the encoded form of this certificate. It is |
| 364 | * assumed that each certificate type would have only a single |
| 365 | * form of encoding; for example, X.509 certificates would |
| 366 | * be encoded as ASN.1 DER. |
| 367 | * |
| 368 | * @exception CertificateEncodingException if an encoding error occurs. |
| 369 | */ |
| 370 | public byte[] getEncoded() throws CertificateEncodingException { |
| 371 | return getEncodedInternal().clone(); |
| 372 | } |
| 373 | |
| 374 | /** |
| 375 | * Returned the encoding as an uncloned byte array. Callers must |
| 376 | * guarantee that they neither modify it nor expose it to untrusted |
| 377 | * code. |
| 378 | */ |
| 379 | public byte[] getEncodedInternal() throws CertificateEncodingException { |
| 380 | if (signedCert == null) { |
| 381 | throw new CertificateEncodingException( |
| 382 | "Null certificate to encode"); |
| 383 | } |
| 384 | return signedCert; |
| 385 | } |
| 386 | |
| 387 | /** |
| 388 | * Throws an exception if the certificate was not signed using the |
| 389 | * verification key provided. Successfully verifying a certificate |
| 390 | * does <em>not</em> indicate that one should trust the entity which |
| 391 | * it represents. |
| 392 | * |
| 393 | * @param key the public key used for verification. |
| 394 | * |
| 395 | * @exception InvalidKeyException on incorrect key. |
| 396 | * @exception NoSuchAlgorithmException on unsupported signature |
| 397 | * algorithms. |
| 398 | * @exception NoSuchProviderException if there's no default provider. |
| 399 | * @exception SignatureException on signature errors. |
| 400 | * @exception CertificateException on encoding errors. |
| 401 | */ |
| 402 | public void verify(PublicKey key) |
| 403 | throws CertificateException, NoSuchAlgorithmException, |
| 404 | InvalidKeyException, NoSuchProviderException, SignatureException { |
| 405 | |
| 406 | verify(key, ""); |
| 407 | } |
| 408 | |
| 409 | /** |
| 410 | * Throws an exception if the certificate was not signed using the |
| 411 | * verification key provided. Successfully verifying a certificate |
| 412 | * does <em>not</em> indicate that one should trust the entity which |
| 413 | * it represents. |
| 414 | * |
| 415 | * @param key the public key used for verification. |
| 416 | * @param sigProvider the name of the provider. |
| 417 | * |
| 418 | * @exception NoSuchAlgorithmException on unsupported signature |
| 419 | * algorithms. |
| 420 | * @exception InvalidKeyException on incorrect key. |
| 421 | * @exception NoSuchProviderException on incorrect provider. |
| 422 | * @exception SignatureException on signature errors. |
| 423 | * @exception CertificateException on encoding errors. |
| 424 | */ |
| 425 | public synchronized void verify(PublicKey key, String sigProvider) |
| 426 | throws CertificateException, NoSuchAlgorithmException, |
| 427 | InvalidKeyException, NoSuchProviderException, SignatureException { |
| 428 | if (sigProvider == null) { |
| 429 | sigProvider = ""; |
| 430 | } |
| 431 | if ((verifiedPublicKey != null) && verifiedPublicKey.equals(key)) { |
| 432 | // this certificate has already been verified using |
| 433 | // this public key. Make sure providers match, too. |
| 434 | if (sigProvider.equals(verifiedProvider)) { |
| 435 | if (verificationResult) { |
| 436 | return; |
| 437 | } else { |
| 438 | throw new SignatureException("Signature does not match."); |
| 439 | } |
| 440 | } |
| 441 | } |
| 442 | if (signedCert == null) { |
| 443 | throw new CertificateEncodingException("Uninitialized certificate"); |
| 444 | } |
| 445 | // Verify the signature ... |
| 446 | Signature sigVerf = null; |
| 447 | if (sigProvider.length() == 0) { |
| 448 | sigVerf = Signature.getInstance(algId.getName()); |
| 449 | } else { |
| 450 | sigVerf = Signature.getInstance(algId.getName(), sigProvider); |
| 451 | } |
| 452 | sigVerf.initVerify(key); |
| 453 | |
| 454 | byte[] rawCert = info.getEncodedInfo(); |
| 455 | sigVerf.update(rawCert, 0, rawCert.length); |
| 456 | |
| 457 | // verify may throw SignatureException for invalid encodings, etc. |
| 458 | verificationResult = sigVerf.verify(signature); |
| 459 | verifiedPublicKey = key; |
| 460 | verifiedProvider = sigProvider; |
| 461 | |
| 462 | if (verificationResult == false) { |
| 463 | throw new SignatureException("Signature does not match."); |
| 464 | } |
| 465 | } |
| 466 | |
| 467 | /** |
| 468 | * Creates an X.509 certificate, and signs it using the given key |
| 469 | * (associating a signature algorithm and an X.500 name). |
| 470 | * This operation is used to implement the certificate generation |
| 471 | * functionality of a certificate authority. |
| 472 | * |
| 473 | * @param key the private key used for signing. |
| 474 | * @param algorithm the name of the signature algorithm used. |
| 475 | * |
| 476 | * @exception InvalidKeyException on incorrect key. |
| 477 | * @exception NoSuchAlgorithmException on unsupported signature |
| 478 | * algorithms. |
| 479 | * @exception NoSuchProviderException if there's no default provider. |
| 480 | * @exception SignatureException on signature errors. |
| 481 | * @exception CertificateException on encoding errors. |
| 482 | */ |
| 483 | public void sign(PrivateKey key, String algorithm) |
| 484 | throws CertificateException, NoSuchAlgorithmException, |
| 485 | InvalidKeyException, NoSuchProviderException, SignatureException { |
| 486 | sign(key, algorithm, null); |
| 487 | } |
| 488 | |
| 489 | /** |
| 490 | * Creates an X.509 certificate, and signs it using the given key |
| 491 | * (associating a signature algorithm and an X.500 name). |
| 492 | * This operation is used to implement the certificate generation |
| 493 | * functionality of a certificate authority. |
| 494 | * |
| 495 | * @param key the private key used for signing. |
| 496 | * @param algorithm the name of the signature algorithm used. |
| 497 | * @param provider the name of the provider. |
| 498 | * |
| 499 | * @exception NoSuchAlgorithmException on unsupported signature |
| 500 | * algorithms. |
| 501 | * @exception InvalidKeyException on incorrect key. |
| 502 | * @exception NoSuchProviderException on incorrect provider. |
| 503 | * @exception SignatureException on signature errors. |
| 504 | * @exception CertificateException on encoding errors. |
| 505 | */ |
| 506 | public void sign(PrivateKey key, String algorithm, String provider) |
| 507 | throws CertificateException, NoSuchAlgorithmException, |
| 508 | InvalidKeyException, NoSuchProviderException, SignatureException { |
| 509 | try { |
| 510 | if (readOnly) |
| 511 | throw new CertificateEncodingException( |
| 512 | "cannot over-write existing certificate"); |
| 513 | Signature sigEngine = null; |
| 514 | if ((provider == null) || (provider.length() == 0)) |
| 515 | sigEngine = Signature.getInstance(algorithm); |
| 516 | else |
| 517 | sigEngine = Signature.getInstance(algorithm, provider); |
| 518 | |
| 519 | sigEngine.initSign(key); |
| 520 | |
| 521 | // in case the name is reset |
| 522 | algId = AlgorithmId.get(sigEngine.getAlgorithm()); |
| 523 | |
| 524 | DerOutputStream out = new DerOutputStream(); |
| 525 | DerOutputStream tmp = new DerOutputStream(); |
| 526 | |
| 527 | // encode certificate info |
| 528 | info.encode(tmp); |
| 529 | byte[] rawCert = tmp.toByteArray(); |
| 530 | |
| 531 | // encode algorithm identifier |
| 532 | algId.encode(tmp); |
| 533 | |
| 534 | // Create and encode the signature itself. |
| 535 | sigEngine.update(rawCert, 0, rawCert.length); |
| 536 | signature = sigEngine.sign(); |
| 537 | tmp.putBitString(signature); |
| 538 | |
| 539 | // Wrap the signed data in a SEQUENCE { data, algorithm, sig } |
| 540 | out.write(DerValue.tag_Sequence, tmp); |
| 541 | signedCert = out.toByteArray(); |
| 542 | readOnly = true; |
| 543 | |
| 544 | } catch (IOException e) { |
| 545 | throw new CertificateEncodingException(e.toString()); |
| 546 | } |
| 547 | } |
| 548 | |
| 549 | /** |
| 550 | * Checks that the certificate is currently valid, i.e. the current |
| 551 | * time is within the specified validity period. |
| 552 | * |
| 553 | * @exception CertificateExpiredException if the certificate has expired. |
| 554 | * @exception CertificateNotYetValidException if the certificate is not |
| 555 | * yet valid. |
| 556 | */ |
| 557 | public void checkValidity() |
| 558 | throws CertificateExpiredException, CertificateNotYetValidException { |
| 559 | Date date = new Date(); |
| 560 | checkValidity(date); |
| 561 | } |
| 562 | |
| 563 | /** |
| 564 | * Checks that the specified date is within the certificate's |
| 565 | * validity period, or basically if the certificate would be |
| 566 | * valid at the specified date/time. |
| 567 | * |
| 568 | * @param date the Date to check against to see if this certificate |
| 569 | * is valid at that date/time. |
| 570 | * |
| 571 | * @exception CertificateExpiredException if the certificate has expired |
| 572 | * with respect to the <code>date</code> supplied. |
| 573 | * @exception CertificateNotYetValidException if the certificate is not |
| 574 | * yet valid with respect to the <code>date</code> supplied. |
| 575 | */ |
| 576 | public void checkValidity(Date date) |
| 577 | throws CertificateExpiredException, CertificateNotYetValidException { |
| 578 | |
| 579 | CertificateValidity interval = null; |
| 580 | try { |
| 581 | interval = (CertificateValidity)info.get(CertificateValidity.NAME); |
| 582 | } catch (Exception e) { |
| 583 | throw new CertificateNotYetValidException("Incorrect validity period"); |
| 584 | } |
| 585 | if (interval == null) |
| 586 | throw new CertificateNotYetValidException("Null validity period"); |
| 587 | interval.valid(date); |
| 588 | } |
| 589 | |
| 590 | /** |
| 591 | * Return the requested attribute from the certificate. |
| 592 | * |
| 593 | * Note that the X509CertInfo is not cloned for performance reasons. |
| 594 | * Callers must ensure that they do not modify it. All other |
| 595 | * attributes are cloned. |
| 596 | * |
| 597 | * @param name the name of the attribute. |
| 598 | * @exception CertificateParsingException on invalid attribute identifier. |
| 599 | */ |
| 600 | public Object get(String name) |
| 601 | throws CertificateParsingException { |
| 602 | X509AttributeName attr = new X509AttributeName(name); |
| 603 | String id = attr.getPrefix(); |
| 604 | if (!(id.equalsIgnoreCase(NAME))) { |
| 605 | throw new CertificateParsingException("Invalid root of " |
| 606 | + "attribute name, expected [" + NAME + |
| 607 | "], received " + "[" + id + "]"); |
| 608 | } |
| 609 | attr = new X509AttributeName(attr.getSuffix()); |
| 610 | id = attr.getPrefix(); |
| 611 | |
| 612 | if (id.equalsIgnoreCase(INFO)) { |
| 613 | if (info == null) { |
| 614 | return null; |
| 615 | } |
| 616 | if (attr.getSuffix() != null) { |
| 617 | try { |
| 618 | return info.get(attr.getSuffix()); |
| 619 | } catch (IOException e) { |
| 620 | throw new CertificateParsingException(e.toString()); |
| 621 | } catch (CertificateException e) { |
| 622 | throw new CertificateParsingException(e.toString()); |
| 623 | } |
| 624 | } else { |
| 625 | return info; |
| 626 | } |
| 627 | } else if (id.equalsIgnoreCase(ALG_ID)) { |
| 628 | return(algId); |
| 629 | } else if (id.equalsIgnoreCase(SIGNATURE)) { |
| 630 | if (signature != null) |
| 631 | return signature.clone(); |
| 632 | else |
| 633 | return null; |
| 634 | } else if (id.equalsIgnoreCase(SIGNED_CERT)) { |
| 635 | if (signedCert != null) |
| 636 | return signedCert.clone(); |
| 637 | else |
| 638 | return null; |
| 639 | } else { |
| 640 | throw new CertificateParsingException("Attribute name not " |
| 641 | + "recognized or get() not allowed for the same: " + id); |
| 642 | } |
| 643 | } |
| 644 | |
| 645 | /** |
| 646 | * Set the requested attribute in the certificate. |
| 647 | * |
| 648 | * @param name the name of the attribute. |
| 649 | * @param obj the value of the attribute. |
| 650 | * @exception CertificateException on invalid attribute identifier. |
| 651 | * @exception IOException on encoding error of attribute. |
| 652 | */ |
| 653 | public void set(String name, Object obj) |
| 654 | throws CertificateException, IOException { |
| 655 | // check if immutable |
| 656 | if (readOnly) |
| 657 | throw new CertificateException("cannot over-write existing" |
| 658 | + " certificate"); |
| 659 | |
| 660 | X509AttributeName attr = new X509AttributeName(name); |
| 661 | String id = attr.getPrefix(); |
| 662 | if (!(id.equalsIgnoreCase(NAME))) { |
| 663 | throw new CertificateException("Invalid root of attribute name," |
| 664 | + " expected [" + NAME + "], received " + id); |
| 665 | } |
| 666 | attr = new X509AttributeName(attr.getSuffix()); |
| 667 | id = attr.getPrefix(); |
| 668 | |
| 669 | if (id.equalsIgnoreCase(INFO)) { |
| 670 | if (attr.getSuffix() == null) { |
| 671 | if (!(obj instanceof X509CertInfo)) { |
| 672 | throw new CertificateException("Attribute value should" |
| 673 | + " be of type X509CertInfo."); |
| 674 | } |
| 675 | info = (X509CertInfo)obj; |
| 676 | signedCert = null; //reset this as certificate data has changed |
| 677 | } else { |
| 678 | info.set(attr.getSuffix(), obj); |
| 679 | signedCert = null; //reset this as certificate data has changed |
| 680 | } |
| 681 | } else { |
| 682 | throw new CertificateException("Attribute name not recognized or " + |
| 683 | "set() not allowed for the same: " + id); |
| 684 | } |
| 685 | } |
| 686 | |
| 687 | /** |
| 688 | * Delete the requested attribute from the certificate. |
| 689 | * |
| 690 | * @param name the name of the attribute. |
| 691 | * @exception CertificateException on invalid attribute identifier. |
| 692 | * @exception IOException on other errors. |
| 693 | */ |
| 694 | public void delete(String name) |
| 695 | throws CertificateException, IOException { |
| 696 | // check if immutable |
| 697 | if (readOnly) |
| 698 | throw new CertificateException("cannot over-write existing" |
| 699 | + " certificate"); |
| 700 | |
| 701 | X509AttributeName attr = new X509AttributeName(name); |
| 702 | String id = attr.getPrefix(); |
| 703 | if (!(id.equalsIgnoreCase(NAME))) { |
| 704 | throw new CertificateException("Invalid root of attribute name," |
| 705 | + " expected [" |
| 706 | + NAME + "], received " + id); |
| 707 | } |
| 708 | attr = new X509AttributeName(attr.getSuffix()); |
| 709 | id = attr.getPrefix(); |
| 710 | |
| 711 | if (id.equalsIgnoreCase(INFO)) { |
| 712 | if (attr.getSuffix() != null) { |
| 713 | info = null; |
| 714 | } else { |
| 715 | info.delete(attr.getSuffix()); |
| 716 | } |
| 717 | } else if (id.equalsIgnoreCase(ALG_ID)) { |
| 718 | algId = null; |
| 719 | } else if (id.equalsIgnoreCase(SIGNATURE)) { |
| 720 | signature = null; |
| 721 | } else if (id.equalsIgnoreCase(SIGNED_CERT)) { |
| 722 | signedCert = null; |
| 723 | } else { |
| 724 | throw new CertificateException("Attribute name not recognized or " + |
| 725 | "delete() not allowed for the same: " + id); |
| 726 | } |
| 727 | } |
| 728 | |
| 729 | /** |
| 730 | * Return an enumeration of names of attributes existing within this |
| 731 | * attribute. |
| 732 | */ |
| 733 | public Enumeration<String> getElements() { |
| 734 | AttributeNameEnumeration elements = new AttributeNameEnumeration(); |
| 735 | elements.addElement(NAME + DOT + INFO); |
| 736 | elements.addElement(NAME + DOT + ALG_ID); |
| 737 | elements.addElement(NAME + DOT + SIGNATURE); |
| 738 | elements.addElement(NAME + DOT + SIGNED_CERT); |
| 739 | |
| 740 | return elements.elements(); |
| 741 | } |
| 742 | |
| 743 | /** |
| 744 | * Return the name of this attribute. |
| 745 | */ |
| 746 | public String getName() { |
| 747 | return(NAME); |
| 748 | } |
| 749 | |
| 750 | /** |
| 751 | * Returns a printable representation of the certificate. This does not |
| 752 | * contain all the information available to distinguish this from any |
| 753 | * other certificate. The certificate must be fully constructed |
| 754 | * before this function may be called. |
| 755 | */ |
| 756 | public String toString() { |
| 757 | if (info == null || algId == null || signature == null) |
| 758 | return ""; |
| 759 | |
| 760 | StringBuilder sb = new StringBuilder(); |
| 761 | |
| 762 | sb.append("[\n"); |
| 763 | sb.append(info.toString() + "\n"); |
| 764 | sb.append(" Algorithm: [" + algId.toString() + "]\n"); |
| 765 | |
| 766 | HexDumpEncoder encoder = new HexDumpEncoder(); |
| 767 | sb.append(" Signature:\n" + encoder.encodeBuffer(signature)); |
| 768 | sb.append("\n]"); |
| 769 | |
| 770 | return sb.toString(); |
| 771 | } |
| 772 | |
| 773 | // the strongly typed gets, as per java.security.cert.X509Certificate |
| 774 | |
| 775 | /** |
| 776 | * Gets the publickey from this certificate. |
| 777 | * |
| 778 | * @return the publickey. |
| 779 | */ |
| 780 | public PublicKey getPublicKey() { |
| 781 | if (info == null) |
| 782 | return null; |
| 783 | try { |
| 784 | PublicKey key = (PublicKey)info.get(CertificateX509Key.NAME |
| 785 | + DOT + CertificateX509Key.KEY); |
| 786 | return key; |
| 787 | } catch (Exception e) { |
| 788 | return null; |
| 789 | } |
| 790 | } |
| 791 | |
| 792 | /** |
| 793 | * Gets the version number from the certificate. |
| 794 | * |
| 795 | * @return the version number, i.e. 1, 2 or 3. |
| 796 | */ |
| 797 | public int getVersion() { |
| 798 | if (info == null) |
| 799 | return -1; |
| 800 | try { |
| 801 | int vers = ((Integer)info.get(CertificateVersion.NAME |
| 802 | + DOT + CertificateVersion.VERSION)).intValue(); |
| 803 | return vers+1; |
| 804 | } catch (Exception e) { |
| 805 | return -1; |
| 806 | } |
| 807 | } |
| 808 | |
| 809 | /** |
| 810 | * Gets the serial number from the certificate. |
| 811 | * |
| 812 | * @return the serial number. |
| 813 | */ |
| 814 | public BigInteger getSerialNumber() { |
| 815 | SerialNumber ser = getSerialNumberObject(); |
| 816 | |
| 817 | return ser != null ? ser.getNumber() : null; |
| 818 | } |
| 819 | |
| 820 | /** |
| 821 | * Gets the serial number from the certificate as |
| 822 | * a SerialNumber object. |
| 823 | * |
| 824 | * @return the serial number. |
| 825 | */ |
| 826 | public SerialNumber getSerialNumberObject() { |
| 827 | if (info == null) |
| 828 | return null; |
| 829 | try { |
| 830 | SerialNumber ser = (SerialNumber)info.get( |
| 831 | CertificateSerialNumber.NAME + DOT + |
| 832 | CertificateSerialNumber.NUMBER); |
| 833 | return ser; |
| 834 | } catch (Exception e) { |
| 835 | return null; |
| 836 | } |
| 837 | } |
| 838 | |
| 839 | |
| 840 | /** |
| 841 | * Gets the subject distinguished name from the certificate. |
| 842 | * |
| 843 | * @return the subject name. |
| 844 | */ |
| 845 | public Principal getSubjectDN() { |
| 846 | if (info == null) |
| 847 | return null; |
| 848 | try { |
| 849 | Principal subject = (Principal)info.get( |
| 850 | CertificateSubjectName.NAME + DOT + |
| 851 | CertificateSubjectName.DN_NAME); |
| 852 | return subject; |
| 853 | } catch (Exception e) { |
| 854 | return null; |
| 855 | } |
| 856 | } |
| 857 | |
| 858 | /** |
| 859 | * Get subject name as X500Principal. Overrides implementation in |
| 860 | * X509Certificate with a slightly more efficient version that is |
| 861 | * also aware of X509CertImpl mutability. |
| 862 | */ |
| 863 | public X500Principal getSubjectX500Principal() { |
| 864 | if (info == null) { |
| 865 | return null; |
| 866 | } |
| 867 | try { |
| 868 | X500Principal subject = (X500Principal)info.get( |
| 869 | CertificateSubjectName.NAME + DOT + |
| 870 | CertificateSubjectName.DN_PRINCIPAL); |
| 871 | return subject; |
| 872 | } catch (Exception e) { |
| 873 | return null; |
| 874 | } |
| 875 | } |
| 876 | |
| 877 | /** |
| 878 | * Gets the issuer distinguished name from the certificate. |
| 879 | * |
| 880 | * @return the issuer name. |
| 881 | */ |
| 882 | public Principal getIssuerDN() { |
| 883 | if (info == null) |
| 884 | return null; |
| 885 | try { |
| 886 | Principal issuer = (Principal)info.get( |
| 887 | CertificateIssuerName.NAME + DOT + |
| 888 | CertificateIssuerName.DN_NAME); |
| 889 | return issuer; |
| 890 | } catch (Exception e) { |
| 891 | return null; |
| 892 | } |
| 893 | } |
| 894 | |
| 895 | /** |
| 896 | * Get issuer name as X500Principal. Overrides implementation in |
| 897 | * X509Certificate with a slightly more efficient version that is |
| 898 | * also aware of X509CertImpl mutability. |
| 899 | */ |
| 900 | public X500Principal getIssuerX500Principal() { |
| 901 | if (info == null) { |
| 902 | return null; |
| 903 | } |
| 904 | try { |
| 905 | X500Principal issuer = (X500Principal)info.get( |
| 906 | CertificateIssuerName.NAME + DOT + |
| 907 | CertificateIssuerName.DN_PRINCIPAL); |
| 908 | return issuer; |
| 909 | } catch (Exception e) { |
| 910 | return null; |
| 911 | } |
| 912 | } |
| 913 | |
| 914 | /** |
| 915 | * Gets the notBefore date from the validity period of the certificate. |
| 916 | * |
| 917 | * @return the start date of the validity period. |
| 918 | */ |
| 919 | public Date getNotBefore() { |
| 920 | if (info == null) |
| 921 | return null; |
| 922 | try { |
| 923 | Date d = (Date) info.get(CertificateValidity.NAME + DOT + |
| 924 | CertificateValidity.NOT_BEFORE); |
| 925 | return d; |
| 926 | } catch (Exception e) { |
| 927 | return null; |
| 928 | } |
| 929 | } |
| 930 | |
| 931 | /** |
| 932 | * Gets the notAfter date from the validity period of the certificate. |
| 933 | * |
| 934 | * @return the end date of the validity period. |
| 935 | */ |
| 936 | public Date getNotAfter() { |
| 937 | if (info == null) |
| 938 | return null; |
| 939 | try { |
| 940 | Date d = (Date) info.get(CertificateValidity.NAME + DOT + |
| 941 | CertificateValidity.NOT_AFTER); |
| 942 | return d; |
| 943 | } catch (Exception e) { |
| 944 | return null; |
| 945 | } |
| 946 | } |
| 947 | |
| 948 | /** |
| 949 | * Gets the DER encoded certificate informations, the |
| 950 | * <code>tbsCertificate</code> from this certificate. |
| 951 | * This can be used to verify the signature independently. |
| 952 | * |
| 953 | * @return the DER encoded certificate information. |
| 954 | * @exception CertificateEncodingException if an encoding error occurs. |
| 955 | */ |
| 956 | public byte[] getTBSCertificate() throws CertificateEncodingException { |
| 957 | if (info != null) { |
| 958 | return info.getEncodedInfo(); |
| 959 | } else |
| 960 | throw new CertificateEncodingException("Uninitialized certificate"); |
| 961 | } |
| 962 | |
| 963 | /** |
| 964 | * Gets the raw Signature bits from the certificate. |
| 965 | * |
| 966 | * @return the signature. |
| 967 | */ |
| 968 | public byte[] getSignature() { |
| 969 | if (signature == null) |
| 970 | return null; |
| 971 | byte[] dup = new byte[signature.length]; |
| 972 | System.arraycopy(signature, 0, dup, 0, dup.length); |
| 973 | return dup; |
| 974 | } |
| 975 | |
| 976 | /** |
| 977 | * Gets the signature algorithm name for the certificate |
| 978 | * signature algorithm. |
| 979 | * For example, the string "SHA-1/DSA" or "DSS". |
| 980 | * |
| 981 | * @return the signature algorithm name. |
| 982 | */ |
| 983 | public String getSigAlgName() { |
| 984 | if (algId == null) |
| 985 | return null; |
| 986 | return (algId.getName()); |
| 987 | } |
| 988 | |
| 989 | /** |
| 990 | * Gets the signature algorithm OID string from the certificate. |
| 991 | * For example, the string "1.2.840.10040.4.3" |
| 992 | * |
| 993 | * @return the signature algorithm oid string. |
| 994 | */ |
| 995 | public String getSigAlgOID() { |
| 996 | if (algId == null) |
| 997 | return null; |
| 998 | ObjectIdentifier oid = algId.getOID(); |
| 999 | return (oid.toString()); |
| 1000 | } |
| 1001 | |
| 1002 | /** |
| 1003 | * Gets the DER encoded signature algorithm parameters from this |
| 1004 | * certificate's signature algorithm. |
| 1005 | * |
| 1006 | * @return the DER encoded signature algorithm parameters, or |
| 1007 | * null if no parameters are present. |
| 1008 | */ |
| 1009 | public byte[] getSigAlgParams() { |
| 1010 | if (algId == null) |
| 1011 | return null; |
| 1012 | try { |
| 1013 | return algId.getEncodedParams(); |
| 1014 | } catch (IOException e) { |
| 1015 | return null; |
| 1016 | } |
| 1017 | } |
| 1018 | |
| 1019 | /** |
| 1020 | * Gets the Issuer Unique Identity from the certificate. |
| 1021 | * |
| 1022 | * @return the Issuer Unique Identity. |
| 1023 | */ |
| 1024 | public boolean[] getIssuerUniqueID() { |
| 1025 | if (info == null) |
| 1026 | return null; |
| 1027 | try { |
| 1028 | UniqueIdentity id = (UniqueIdentity)info.get( |
| 1029 | CertificateIssuerUniqueIdentity.NAME |
| 1030 | + DOT + CertificateIssuerUniqueIdentity.ID); |
| 1031 | if (id == null) |
| 1032 | return null; |
| 1033 | else |
| 1034 | return (id.getId()); |
| 1035 | } catch (Exception e) { |
| 1036 | return null; |
| 1037 | } |
| 1038 | } |
| 1039 | |
| 1040 | /** |
| 1041 | * Gets the Subject Unique Identity from the certificate. |
| 1042 | * |
| 1043 | * @return the Subject Unique Identity. |
| 1044 | */ |
| 1045 | public boolean[] getSubjectUniqueID() { |
| 1046 | if (info == null) |
| 1047 | return null; |
| 1048 | try { |
| 1049 | UniqueIdentity id = (UniqueIdentity)info.get( |
| 1050 | CertificateSubjectUniqueIdentity.NAME |
| 1051 | + DOT + CertificateSubjectUniqueIdentity.ID); |
| 1052 | if (id == null) |
| 1053 | return null; |
| 1054 | else |
| 1055 | return (id.getId()); |
| 1056 | } catch (Exception e) { |
| 1057 | return null; |
| 1058 | } |
| 1059 | } |
| 1060 | |
| 1061 | /** |
| 1062 | * Get AuthorityKeyIdentifier extension |
| 1063 | * @return AuthorityKeyIdentifier object or null (if no such object |
| 1064 | * in certificate) |
| 1065 | */ |
| 1066 | public AuthorityKeyIdentifierExtension getAuthorityKeyIdentifierExtension() |
| 1067 | { |
| 1068 | return (AuthorityKeyIdentifierExtension) |
| 1069 | getExtension(PKIXExtensions.AuthorityKey_Id); |
| 1070 | } |
| 1071 | |
| 1072 | /** |
| 1073 | * Get BasicConstraints extension |
| 1074 | * @return BasicConstraints object or null (if no such object in |
| 1075 | * certificate) |
| 1076 | */ |
| 1077 | public BasicConstraintsExtension getBasicConstraintsExtension() { |
| 1078 | return (BasicConstraintsExtension) |
| 1079 | getExtension(PKIXExtensions.BasicConstraints_Id); |
| 1080 | } |
| 1081 | |
| 1082 | /** |
| 1083 | * Get CertificatePoliciesExtension |
| 1084 | * @return CertificatePoliciesExtension or null (if no such object in |
| 1085 | * certificate) |
| 1086 | */ |
| 1087 | public CertificatePoliciesExtension getCertificatePoliciesExtension() { |
| 1088 | return (CertificatePoliciesExtension) |
| 1089 | getExtension(PKIXExtensions.CertificatePolicies_Id); |
| 1090 | } |
| 1091 | |
| 1092 | /** |
| 1093 | * Get ExtendedKeyUsage extension |
| 1094 | * @return ExtendedKeyUsage extension object or null (if no such object |
| 1095 | * in certificate) |
| 1096 | */ |
| 1097 | public ExtendedKeyUsageExtension getExtendedKeyUsageExtension() { |
| 1098 | return (ExtendedKeyUsageExtension) |
| 1099 | getExtension(PKIXExtensions.ExtendedKeyUsage_Id); |
| 1100 | } |
| 1101 | |
| 1102 | /** |
| 1103 | * Get IssuerAlternativeName extension |
| 1104 | * @return IssuerAlternativeName object or null (if no such object in |
| 1105 | * certificate) |
| 1106 | */ |
| 1107 | public IssuerAlternativeNameExtension getIssuerAlternativeNameExtension() { |
| 1108 | return (IssuerAlternativeNameExtension) |
| 1109 | getExtension(PKIXExtensions.IssuerAlternativeName_Id); |
| 1110 | } |
| 1111 | |
| 1112 | /** |
| 1113 | * Get NameConstraints extension |
| 1114 | * @return NameConstraints object or null (if no such object in certificate) |
| 1115 | */ |
| 1116 | public NameConstraintsExtension getNameConstraintsExtension() { |
| 1117 | return (NameConstraintsExtension) |
| 1118 | getExtension(PKIXExtensions.NameConstraints_Id); |
| 1119 | } |
| 1120 | |
| 1121 | /** |
| 1122 | * Get PolicyConstraints extension |
| 1123 | * @return PolicyConstraints object or null (if no such object in |
| 1124 | * certificate) |
| 1125 | */ |
| 1126 | public PolicyConstraintsExtension getPolicyConstraintsExtension() { |
| 1127 | return (PolicyConstraintsExtension) |
| 1128 | getExtension(PKIXExtensions.PolicyConstraints_Id); |
| 1129 | } |
| 1130 | |
| 1131 | /** |
| 1132 | * Get PolicyMappingsExtension extension |
| 1133 | * @return PolicyMappingsExtension object or null (if no such object |
| 1134 | * in certificate) |
| 1135 | */ |
| 1136 | public PolicyMappingsExtension getPolicyMappingsExtension() { |
| 1137 | return (PolicyMappingsExtension) |
| 1138 | getExtension(PKIXExtensions.PolicyMappings_Id); |
| 1139 | } |
| 1140 | |
| 1141 | /** |
| 1142 | * Get PrivateKeyUsage extension |
| 1143 | * @return PrivateKeyUsage object or null (if no such object in certificate) |
| 1144 | */ |
| 1145 | public PrivateKeyUsageExtension getPrivateKeyUsageExtension() { |
| 1146 | return (PrivateKeyUsageExtension) |
| 1147 | getExtension(PKIXExtensions.PrivateKeyUsage_Id); |
| 1148 | } |
| 1149 | |
| 1150 | /** |
| 1151 | * Get SubjectAlternativeName extension |
| 1152 | * @return SubjectAlternativeName object or null (if no such object in |
| 1153 | * certificate) |
| 1154 | */ |
| 1155 | public SubjectAlternativeNameExtension getSubjectAlternativeNameExtension() |
| 1156 | { |
| 1157 | return (SubjectAlternativeNameExtension) |
| 1158 | getExtension(PKIXExtensions.SubjectAlternativeName_Id); |
| 1159 | } |
| 1160 | |
| 1161 | /** |
| 1162 | * Get SubjectKeyIdentifier extension |
| 1163 | * @return SubjectKeyIdentifier object or null (if no such object in |
| 1164 | * certificate) |
| 1165 | */ |
| 1166 | public SubjectKeyIdentifierExtension getSubjectKeyIdentifierExtension() { |
| 1167 | return (SubjectKeyIdentifierExtension) |
| 1168 | getExtension(PKIXExtensions.SubjectKey_Id); |
| 1169 | } |
| 1170 | |
| 1171 | /** |
| 1172 | * Get CRLDistributionPoints extension |
| 1173 | * @return CRLDistributionPoints object or null (if no such object in |
| 1174 | * certificate) |
| 1175 | */ |
| 1176 | public CRLDistributionPointsExtension getCRLDistributionPointsExtension() { |
| 1177 | return (CRLDistributionPointsExtension) |
| 1178 | getExtension(PKIXExtensions.CRLDistributionPoints_Id); |
| 1179 | } |
| 1180 | |
| 1181 | /** |
| 1182 | * Return true if a critical extension is found that is |
| 1183 | * not supported, otherwise return false. |
| 1184 | */ |
| 1185 | public boolean hasUnsupportedCriticalExtension() { |
| 1186 | if (info == null) |
| 1187 | return false; |
| 1188 | try { |
| 1189 | CertificateExtensions exts = (CertificateExtensions)info.get( |
| 1190 | CertificateExtensions.NAME); |
| 1191 | if (exts == null) |
| 1192 | return false; |
| 1193 | return exts.hasUnsupportedCriticalExtension(); |
| 1194 | } catch (Exception e) { |
| 1195 | return false; |
| 1196 | } |
| 1197 | } |
| 1198 | |
| 1199 | /** |
| 1200 | * Gets a Set of the extension(s) marked CRITICAL in the |
| 1201 | * certificate. In the returned set, each extension is |
| 1202 | * represented by its OID string. |
| 1203 | * |
| 1204 | * @return a set of the extension oid strings in the |
| 1205 | * certificate that are marked critical. |
| 1206 | */ |
| 1207 | public Set<String> getCriticalExtensionOIDs() { |
| 1208 | if (info == null) { |
| 1209 | return null; |
| 1210 | } |
| 1211 | try { |
| 1212 | CertificateExtensions exts = (CertificateExtensions)info.get( |
| 1213 | CertificateExtensions.NAME); |
| 1214 | if (exts == null) { |
| 1215 | return null; |
| 1216 | } |
| 1217 | Set<String> extSet = new HashSet<String>(); |
| 1218 | for (Extension ex : exts.getAllExtensions()) { |
| 1219 | if (ex.isCritical()) { |
| 1220 | extSet.add(ex.getExtensionId().toString()); |
| 1221 | } |
| 1222 | } |
| 1223 | return extSet; |
| 1224 | } catch (Exception e) { |
| 1225 | return null; |
| 1226 | } |
| 1227 | } |
| 1228 | |
| 1229 | /** |
| 1230 | * Gets a Set of the extension(s) marked NON-CRITICAL in the |
| 1231 | * certificate. In the returned set, each extension is |
| 1232 | * represented by its OID string. |
| 1233 | * |
| 1234 | * @return a set of the extension oid strings in the |
| 1235 | * certificate that are NOT marked critical. |
| 1236 | */ |
| 1237 | public Set<String> getNonCriticalExtensionOIDs() { |
| 1238 | if (info == null) { |
| 1239 | return null; |
| 1240 | } |
| 1241 | try { |
| 1242 | CertificateExtensions exts = (CertificateExtensions)info.get( |
| 1243 | CertificateExtensions.NAME); |
| 1244 | if (exts == null) { |
| 1245 | return null; |
| 1246 | } |
| 1247 | Set<String> extSet = new HashSet<String>(); |
| 1248 | for (Extension ex : exts.getAllExtensions()) { |
| 1249 | if (!ex.isCritical()) { |
| 1250 | extSet.add(ex.getExtensionId().toString()); |
| 1251 | } |
| 1252 | } |
| 1253 | extSet.addAll(exts.getUnparseableExtensions().keySet()); |
| 1254 | return extSet; |
| 1255 | } catch (Exception e) { |
| 1256 | return null; |
| 1257 | } |
| 1258 | } |
| 1259 | |
| 1260 | /** |
| 1261 | * Gets the extension identified by the given ObjectIdentifier |
| 1262 | * |
| 1263 | * @param oid the Object Identifier value for the extension. |
| 1264 | * @return Extension or null if certificate does not contain this |
| 1265 | * extension |
| 1266 | */ |
| 1267 | public Extension getExtension(ObjectIdentifier oid) { |
| 1268 | if (info == null) { |
| 1269 | return null; |
| 1270 | } |
| 1271 | try { |
| 1272 | CertificateExtensions extensions; |
| 1273 | try { |
| 1274 | extensions = (CertificateExtensions)info.get(CertificateExtensions.NAME); |
| 1275 | } catch (CertificateException ce) { |
| 1276 | return null; |
| 1277 | } |
| 1278 | if (extensions == null) { |
| 1279 | return null; |
| 1280 | } else { |
| 1281 | for (Extension ex : extensions.getAllExtensions()) { |
| 1282 | if (ex.getExtensionId().equals(oid)) { |
| 1283 | //XXXX May want to consider cloning this |
| 1284 | return ex; |
| 1285 | } |
| 1286 | } |
| 1287 | /* no such extension in this certificate */ |
| 1288 | return null; |
| 1289 | } |
| 1290 | } catch (IOException ioe) { |
| 1291 | return null; |
| 1292 | } |
| 1293 | } |
| 1294 | |
| 1295 | public Extension getUnparseableExtension(ObjectIdentifier oid) { |
| 1296 | if (info == null) { |
| 1297 | return null; |
| 1298 | } |
| 1299 | try { |
| 1300 | CertificateExtensions extensions; |
| 1301 | try { |
| 1302 | extensions = (CertificateExtensions)info.get(CertificateExtensions.NAME); |
| 1303 | } catch (CertificateException ce) { |
| 1304 | return null; |
| 1305 | } |
| 1306 | if (extensions == null) { |
| 1307 | return null; |
| 1308 | } else { |
| 1309 | return extensions.getUnparseableExtensions().get(oid.toString()); |
| 1310 | } |
| 1311 | } catch (IOException ioe) { |
| 1312 | return null; |
| 1313 | } |
| 1314 | } |
| 1315 | |
| 1316 | /** |
| 1317 | * Gets the DER encoded extension identified by the given |
| 1318 | * oid String. |
| 1319 | * |
| 1320 | * @param oid the Object Identifier value for the extension. |
| 1321 | */ |
| 1322 | public byte[] getExtensionValue(String oid) { |
| 1323 | try { |
| 1324 | ObjectIdentifier findOID = new ObjectIdentifier(oid); |
| 1325 | String extAlias = OIDMap.getName(findOID); |
| 1326 | Extension certExt = null; |
| 1327 | CertificateExtensions exts = (CertificateExtensions)info.get( |
| 1328 | CertificateExtensions.NAME); |
| 1329 | |
| 1330 | if (extAlias == null) { // may be unknown |
| 1331 | // get the extensions, search thru' for this oid |
| 1332 | if (exts == null) { |
| 1333 | return null; |
| 1334 | } |
| 1335 | |
| 1336 | for (Extension ex : exts.getAllExtensions()) { |
| 1337 | ObjectIdentifier inCertOID = ex.getExtensionId(); |
| 1338 | if (inCertOID.equals(findOID)) { |
| 1339 | certExt = ex; |
| 1340 | break; |
| 1341 | } |
| 1342 | } |
| 1343 | } else { // there's sub-class that can handle this extension |
| 1344 | try { |
| 1345 | certExt = (Extension)this.get(extAlias); |
| 1346 | } catch (CertificateException e) { |
| 1347 | // get() throws an Exception instead of returning null, ignore |
| 1348 | } |
| 1349 | } |
| 1350 | if (certExt == null) { |
| 1351 | if (exts != null) { |
| 1352 | certExt = exts.getUnparseableExtensions().get(oid); |
| 1353 | } |
| 1354 | if (certExt == null) { |
| 1355 | return null; |
| 1356 | } |
| 1357 | } |
| 1358 | byte[] extData = certExt.getExtensionValue(); |
| 1359 | if (extData == null) { |
| 1360 | return null; |
| 1361 | } |
| 1362 | DerOutputStream out = new DerOutputStream(); |
| 1363 | out.putOctetString(extData); |
| 1364 | return out.toByteArray(); |
| 1365 | } catch (Exception e) { |
| 1366 | return null; |
| 1367 | } |
| 1368 | } |
| 1369 | |
| 1370 | /** |
| 1371 | * Get a boolean array representing the bits of the KeyUsage extension, |
| 1372 | * (oid = 2.5.29.15). |
| 1373 | * @return the bit values of this extension as an array of booleans. |
| 1374 | */ |
| 1375 | public boolean[] getKeyUsage() { |
| 1376 | try { |
| 1377 | String extAlias = OIDMap.getName(PKIXExtensions.KeyUsage_Id); |
| 1378 | if (extAlias == null) |
| 1379 | return null; |
| 1380 | |
| 1381 | KeyUsageExtension certExt = (KeyUsageExtension)this.get(extAlias); |
| 1382 | if (certExt == null) |
| 1383 | return null; |
| 1384 | |
| 1385 | boolean[] ret = certExt.getBits(); |
| 1386 | if (ret.length < NUM_STANDARD_KEY_USAGE) { |
| 1387 | boolean[] usageBits = new boolean[NUM_STANDARD_KEY_USAGE]; |
| 1388 | System.arraycopy(ret, 0, usageBits, 0, ret.length); |
| 1389 | ret = usageBits; |
| 1390 | } |
| 1391 | return ret; |
| 1392 | } catch (Exception e) { |
| 1393 | return null; |
| 1394 | } |
| 1395 | } |
| 1396 | |
| 1397 | /** |
| 1398 | * This method are the overridden implementation of |
| 1399 | * getExtendedKeyUsage method in X509Certificate in the Sun |
| 1400 | * provider. It is better performance-wise since it returns cached |
| 1401 | * values. |
| 1402 | */ |
| 1403 | public synchronized List<String> getExtendedKeyUsage() |
| 1404 | throws CertificateParsingException { |
| 1405 | if (readOnly && extKeyUsage != null) { |
| 1406 | return extKeyUsage; |
| 1407 | } else { |
| 1408 | ExtendedKeyUsageExtension ext = getExtendedKeyUsageExtension(); |
| 1409 | if (ext == null) { |
| 1410 | return null; |
| 1411 | } |
| 1412 | extKeyUsage = |
| 1413 | Collections.unmodifiableList(ext.getExtendedKeyUsage()); |
| 1414 | return extKeyUsage; |
| 1415 | } |
| 1416 | } |
| 1417 | |
| 1418 | /** |
| 1419 | * This static method is the default implementation of the |
| 1420 | * getExtendedKeyUsage method in X509Certificate. A |
| 1421 | * X509Certificate provider generally should overwrite this to |
| 1422 | * provide among other things caching for better performance. |
| 1423 | */ |
| 1424 | public static List<String> getExtendedKeyUsage(X509Certificate cert) |
| 1425 | throws CertificateParsingException { |
| 1426 | try { |
| 1427 | byte[] ext = cert.getExtensionValue(EXTENDED_KEY_USAGE_OID); |
| 1428 | if (ext == null) |
| 1429 | return null; |
| 1430 | DerValue val = new DerValue(ext); |
| 1431 | byte[] data = val.getOctetString(); |
| 1432 | |
| 1433 | ExtendedKeyUsageExtension ekuExt = |
| 1434 | new ExtendedKeyUsageExtension(Boolean.FALSE, data); |
| 1435 | return Collections.unmodifiableList(ekuExt.getExtendedKeyUsage()); |
| 1436 | } catch (IOException ioe) { |
| 1437 | CertificateParsingException cpe = |
| 1438 | new CertificateParsingException(); |
| 1439 | cpe.initCause(ioe); |
| 1440 | throw cpe; |
| 1441 | } |
| 1442 | } |
| 1443 | |
| 1444 | /** |
| 1445 | * Get the certificate constraints path length from the |
| 1446 | * the critical BasicConstraints extension, (oid = 2.5.29.19). |
| 1447 | * @return the length of the constraint. |
| 1448 | */ |
| 1449 | public int getBasicConstraints() { |
| 1450 | try { |
| 1451 | String extAlias = OIDMap.getName(PKIXExtensions.BasicConstraints_Id); |
| 1452 | if (extAlias == null) |
| 1453 | return -1; |
| 1454 | BasicConstraintsExtension certExt = |
| 1455 | (BasicConstraintsExtension)this.get(extAlias); |
| 1456 | if (certExt == null) |
| 1457 | return -1; |
| 1458 | |
| 1459 | if (((Boolean)certExt.get(BasicConstraintsExtension.IS_CA) |
| 1460 | ).booleanValue() == true) |
| 1461 | return ((Integer)certExt.get( |
| 1462 | BasicConstraintsExtension.PATH_LEN)).intValue(); |
| 1463 | else |
| 1464 | return -1; |
| 1465 | } catch (Exception e) { |
| 1466 | return -1; |
| 1467 | } |
| 1468 | } |
| 1469 | |
| 1470 | /** |
| 1471 | * Converts a GeneralNames structure into an immutable Collection of |
| 1472 | * alternative names (subject or issuer) in the form required by |
| 1473 | * {@link #getSubjectAlternativeNames} or |
| 1474 | * {@link #getIssuerAlternativeNames}. |
| 1475 | * |
| 1476 | * @param names the GeneralNames to be converted |
| 1477 | * @return an immutable Collection of alternative names |
| 1478 | */ |
| 1479 | private static Collection<List<?>> makeAltNames(GeneralNames names) { |
| 1480 | if (names.isEmpty()) { |
| 1481 | return Collections.<List<?>>emptySet(); |
| 1482 | } |
| 1483 | Set<List<?>> newNames = new HashSet<List<?>>(); |
| 1484 | for (GeneralName gname : names.names()) { |
| 1485 | GeneralNameInterface name = gname.getName(); |
| 1486 | List<Object> nameEntry = new ArrayList<Object>(2); |
| 1487 | nameEntry.add(Integer.valueOf(name.getType())); |
| 1488 | switch (name.getType()) { |
| 1489 | case GeneralNameInterface.NAME_RFC822: |
| 1490 | nameEntry.add(((RFC822Name) name).getName()); |
| 1491 | break; |
| 1492 | case GeneralNameInterface.NAME_DNS: |
| 1493 | nameEntry.add(((DNSName) name).getName()); |
| 1494 | break; |
| 1495 | case GeneralNameInterface.NAME_DIRECTORY: |
| 1496 | nameEntry.add(((X500Name) name).getRFC2253Name()); |
| 1497 | break; |
| 1498 | case GeneralNameInterface.NAME_URI: |
| 1499 | nameEntry.add(((URIName) name).getName()); |
| 1500 | break; |
| 1501 | case GeneralNameInterface.NAME_IP: |
| 1502 | try { |
| 1503 | nameEntry.add(((IPAddressName) name).getName()); |
| 1504 | } catch (IOException ioe) { |
| 1505 | // IPAddressName in cert is bogus |
| 1506 | throw new RuntimeException("IPAddress cannot be parsed", |
| 1507 | ioe); |
| 1508 | } |
| 1509 | break; |
| 1510 | case GeneralNameInterface.NAME_OID: |
| 1511 | nameEntry.add(((OIDName) name).getOID().toString()); |
| 1512 | break; |
| 1513 | default: |
| 1514 | // add DER encoded form |
| 1515 | DerOutputStream derOut = new DerOutputStream(); |
| 1516 | try { |
| 1517 | name.encode(derOut); |
| 1518 | } catch (IOException ioe) { |
| 1519 | // should not occur since name has already been decoded |
| 1520 | // from cert (this would indicate a bug in our code) |
| 1521 | throw new RuntimeException("name cannot be encoded", ioe); |
| 1522 | } |
| 1523 | nameEntry.add(derOut.toByteArray()); |
| 1524 | break; |
| 1525 | } |
| 1526 | newNames.add(Collections.unmodifiableList(nameEntry)); |
| 1527 | } |
| 1528 | return Collections.unmodifiableCollection(newNames); |
| 1529 | } |
| 1530 | |
| 1531 | /** |
| 1532 | * Checks a Collection of altNames and clones any name entries of type |
| 1533 | * byte []. |
| 1534 | */ // only partially generified due to javac bug |
| 1535 | private static Collection<List<?>> cloneAltNames(Collection<List<?>> altNames) { |
| 1536 | boolean mustClone = false; |
| 1537 | for (List<?> nameEntry : altNames) { |
| 1538 | if (nameEntry.get(1) instanceof byte[]) { |
| 1539 | // must clone names |
| 1540 | mustClone = true; |
| 1541 | } |
| 1542 | } |
| 1543 | if (mustClone) { |
| 1544 | Set<List<?>> namesCopy = new HashSet<List<?>>(); |
| 1545 | for (List<?> nameEntry : altNames) { |
| 1546 | Object nameObject = nameEntry.get(1); |
| 1547 | if (nameObject instanceof byte[]) { |
| 1548 | List<Object> nameEntryCopy = |
| 1549 | new ArrayList<Object>(nameEntry); |
| 1550 | nameEntryCopy.set(1, ((byte[])nameObject).clone()); |
| 1551 | namesCopy.add(Collections.unmodifiableList(nameEntryCopy)); |
| 1552 | } else { |
| 1553 | namesCopy.add(nameEntry); |
| 1554 | } |
| 1555 | } |
| 1556 | return Collections.unmodifiableCollection(namesCopy); |
| 1557 | } else { |
| 1558 | return altNames; |
| 1559 | } |
| 1560 | } |
| 1561 | |
| 1562 | /** |
| 1563 | * This method are the overridden implementation of |
| 1564 | * getSubjectAlternativeNames method in X509Certificate in the Sun |
| 1565 | * provider. It is better performance-wise since it returns cached |
| 1566 | * values. |
| 1567 | */ |
| 1568 | public synchronized Collection<List<?>> getSubjectAlternativeNames() |
| 1569 | throws CertificateParsingException { |
| 1570 | // return cached value if we can |
| 1571 | if (readOnly && subjectAlternativeNames != null) { |
| 1572 | return cloneAltNames(subjectAlternativeNames); |
| 1573 | } |
| 1574 | SubjectAlternativeNameExtension subjectAltNameExt = |
| 1575 | getSubjectAlternativeNameExtension(); |
| 1576 | if (subjectAltNameExt == null) { |
| 1577 | return null; |
| 1578 | } |
| 1579 | GeneralNames names; |
| 1580 | try { |
| 1581 | names = (GeneralNames) subjectAltNameExt.get |
| 1582 | (SubjectAlternativeNameExtension.SUBJECT_NAME); |
| 1583 | } catch (IOException ioe) { |
| 1584 | // should not occur |
| 1585 | return Collections.<List<?>>emptySet(); |
| 1586 | } |
| 1587 | subjectAlternativeNames = makeAltNames(names); |
| 1588 | return subjectAlternativeNames; |
| 1589 | } |
| 1590 | |
| 1591 | /** |
| 1592 | * This static method is the default implementation of the |
| 1593 | * getSubjectAlternaitveNames method in X509Certificate. A |
| 1594 | * X509Certificate provider generally should overwrite this to |
| 1595 | * provide among other things caching for better performance. |
| 1596 | */ |
| 1597 | public static Collection<List<?>> getSubjectAlternativeNames(X509Certificate cert) |
| 1598 | throws CertificateParsingException { |
| 1599 | try { |
| 1600 | byte[] ext = cert.getExtensionValue(SUBJECT_ALT_NAME_OID); |
| 1601 | if (ext == null) { |
| 1602 | return null; |
| 1603 | } |
| 1604 | DerValue val = new DerValue(ext); |
| 1605 | byte[] data = val.getOctetString(); |
| 1606 | |
| 1607 | SubjectAlternativeNameExtension subjectAltNameExt = |
| 1608 | new SubjectAlternativeNameExtension(Boolean.FALSE, |
| 1609 | data); |
| 1610 | |
| 1611 | GeneralNames names; |
| 1612 | try { |
| 1613 | names = (GeneralNames) subjectAltNameExt.get |
| 1614 | (SubjectAlternativeNameExtension.SUBJECT_NAME); |
| 1615 | } catch (IOException ioe) { |
| 1616 | // should not occur |
| 1617 | return Collections.<List<?>>emptySet(); |
| 1618 | } |
| 1619 | return makeAltNames(names); |
| 1620 | } catch (IOException ioe) { |
| 1621 | CertificateParsingException cpe = |
| 1622 | new CertificateParsingException(); |
| 1623 | cpe.initCause(ioe); |
| 1624 | throw cpe; |
| 1625 | } |
| 1626 | } |
| 1627 | |
| 1628 | /** |
| 1629 | * This method are the overridden implementation of |
| 1630 | * getIssuerAlternativeNames method in X509Certificate in the Sun |
| 1631 | * provider. It is better performance-wise since it returns cached |
| 1632 | * values. |
| 1633 | */ |
| 1634 | public synchronized Collection<List<?>> getIssuerAlternativeNames() |
| 1635 | throws CertificateParsingException { |
| 1636 | // return cached value if we can |
| 1637 | if (readOnly && issuerAlternativeNames != null) { |
| 1638 | return cloneAltNames(issuerAlternativeNames); |
| 1639 | } |
| 1640 | IssuerAlternativeNameExtension issuerAltNameExt = |
| 1641 | getIssuerAlternativeNameExtension(); |
| 1642 | if (issuerAltNameExt == null) { |
| 1643 | return null; |
| 1644 | } |
| 1645 | GeneralNames names; |
| 1646 | try { |
| 1647 | names = (GeneralNames) issuerAltNameExt.get |
| 1648 | (IssuerAlternativeNameExtension.ISSUER_NAME); |
| 1649 | } catch (IOException ioe) { |
| 1650 | // should not occur |
| 1651 | return Collections.<List<?>>emptySet(); |
| 1652 | } |
| 1653 | issuerAlternativeNames = makeAltNames(names); |
| 1654 | return issuerAlternativeNames; |
| 1655 | } |
| 1656 | |
| 1657 | /** |
| 1658 | * This static method is the default implementation of the |
| 1659 | * getIssuerAlternaitveNames method in X509Certificate. A |
| 1660 | * X509Certificate provider generally should overwrite this to |
| 1661 | * provide among other things caching for better performance. |
| 1662 | */ |
| 1663 | public static Collection<List<?>> getIssuerAlternativeNames(X509Certificate cert) |
| 1664 | throws CertificateParsingException { |
| 1665 | try { |
| 1666 | byte[] ext = cert.getExtensionValue(ISSUER_ALT_NAME_OID); |
| 1667 | if (ext == null) { |
| 1668 | return null; |
| 1669 | } |
| 1670 | |
| 1671 | DerValue val = new DerValue(ext); |
| 1672 | byte[] data = val.getOctetString(); |
| 1673 | |
| 1674 | IssuerAlternativeNameExtension issuerAltNameExt = |
| 1675 | new IssuerAlternativeNameExtension(Boolean.FALSE, |
| 1676 | data); |
| 1677 | GeneralNames names; |
| 1678 | try { |
| 1679 | names = (GeneralNames) issuerAltNameExt.get |
| 1680 | (IssuerAlternativeNameExtension.ISSUER_NAME); |
| 1681 | } catch (IOException ioe) { |
| 1682 | // should not occur |
| 1683 | return Collections.<List<?>>emptySet(); |
| 1684 | } |
| 1685 | return makeAltNames(names); |
| 1686 | } catch (IOException ioe) { |
| 1687 | CertificateParsingException cpe = |
| 1688 | new CertificateParsingException(); |
| 1689 | cpe.initCause(ioe); |
| 1690 | throw cpe; |
| 1691 | } |
| 1692 | } |
| 1693 | |
| 1694 | public AuthorityInfoAccessExtension getAuthorityInfoAccessExtension() { |
| 1695 | return (AuthorityInfoAccessExtension) |
| 1696 | getExtension(PKIXExtensions.AuthInfoAccess_Id); |
| 1697 | } |
| 1698 | |
| 1699 | /************************************************************/ |
| 1700 | |
| 1701 | /* |
| 1702 | * Cert is a SIGNED ASN.1 macro, a three elment sequence: |
| 1703 | * |
| 1704 | * - Data to be signed (ToBeSigned) -- the "raw" cert |
| 1705 | * - Signature algorithm (SigAlgId) |
| 1706 | * - The signature bits |
| 1707 | * |
| 1708 | * This routine unmarshals the certificate, saving the signature |
| 1709 | * parts away for later verification. |
| 1710 | */ |
| 1711 | private void parse(DerValue val) |
| 1712 | throws CertificateException, IOException { |
| 1713 | // check if can over write the certificate |
| 1714 | if (readOnly) |
| 1715 | throw new CertificateParsingException( |
| 1716 | "cannot over-write existing certificate"); |
| 1717 | |
| 1718 | if (val.data == null || val.tag != DerValue.tag_Sequence) |
| 1719 | throw new CertificateParsingException( |
| 1720 | "invalid DER-encoded certificate data"); |
| 1721 | |
| 1722 | signedCert = val.toByteArray(); |
| 1723 | DerValue[] seq = new DerValue[3]; |
| 1724 | |
| 1725 | seq[0] = val.data.getDerValue(); |
| 1726 | seq[1] = val.data.getDerValue(); |
| 1727 | seq[2] = val.data.getDerValue(); |
| 1728 | |
| 1729 | if (val.data.available() != 0) { |
| 1730 | throw new CertificateParsingException("signed overrun, bytes = " |
| 1731 | + val.data.available()); |
| 1732 | } |
| 1733 | if (seq[0].tag != DerValue.tag_Sequence) { |
| 1734 | throw new CertificateParsingException("signed fields invalid"); |
| 1735 | } |
| 1736 | |
| 1737 | algId = AlgorithmId.parse(seq[1]); |
| 1738 | signature = seq[2].getBitString(); |
| 1739 | |
| 1740 | if (seq[1].data.available() != 0) { |
| 1741 | throw new CertificateParsingException("algid field overrun"); |
| 1742 | } |
| 1743 | if (seq[2].data.available() != 0) |
| 1744 | throw new CertificateParsingException("signed fields overrun"); |
| 1745 | |
| 1746 | // The CertificateInfo |
| 1747 | info = new X509CertInfo(seq[0]); |
| 1748 | |
| 1749 | // the "inner" and "outer" signature algorithms must match |
| 1750 | AlgorithmId infoSigAlg = (AlgorithmId)info.get( |
| 1751 | CertificateAlgorithmId.NAME |
| 1752 | + DOT + |
| 1753 | CertificateAlgorithmId.ALGORITHM); |
| 1754 | if (! algId.equals(infoSigAlg)) |
| 1755 | throw new CertificateException("Signature algorithm mismatch"); |
| 1756 | readOnly = true; |
| 1757 | } |
| 1758 | |
| 1759 | /** |
| 1760 | * Extract the subject or issuer X500Principal from an X509Certificate. |
| 1761 | * Parses the encoded form of the cert to preserve the principal's |
| 1762 | * ASN.1 encoding. |
| 1763 | */ |
| 1764 | private static X500Principal getX500Principal(X509Certificate cert, |
| 1765 | boolean getIssuer) throws Exception { |
| 1766 | byte[] encoded = cert.getEncoded(); |
| 1767 | DerInputStream derIn = new DerInputStream(encoded); |
| 1768 | DerValue tbsCert = derIn.getSequence(3)[0]; |
| 1769 | DerInputStream tbsIn = tbsCert.data; |
| 1770 | DerValue tmp; |
| 1771 | tmp = tbsIn.getDerValue(); |
| 1772 | // skip version number if present |
| 1773 | if (tmp.isContextSpecific((byte)0)) { |
| 1774 | tmp = tbsIn.getDerValue(); |
| 1775 | } |
| 1776 | // tmp always contains serial number now |
| 1777 | tmp = tbsIn.getDerValue(); // skip signature |
| 1778 | tmp = tbsIn.getDerValue(); // issuer |
| 1779 | if (getIssuer == false) { |
| 1780 | tmp = tbsIn.getDerValue(); // skip validity |
| 1781 | tmp = tbsIn.getDerValue(); // subject |
| 1782 | } |
| 1783 | byte[] principalBytes = tmp.toByteArray(); |
| 1784 | return new X500Principal(principalBytes); |
| 1785 | } |
| 1786 | |
| 1787 | /** |
| 1788 | * Extract the subject X500Principal from an X509Certificate. |
| 1789 | * Called from java.security.cert.X509Certificate.getSubjectX500Principal(). |
| 1790 | */ |
| 1791 | public static X500Principal getSubjectX500Principal(X509Certificate cert) { |
| 1792 | try { |
| 1793 | return getX500Principal(cert, false); |
| 1794 | } catch (Exception e) { |
| 1795 | throw new RuntimeException("Could not parse subject", e); |
| 1796 | } |
| 1797 | } |
| 1798 | |
| 1799 | /** |
| 1800 | * Extract the issuer X500Principal from an X509Certificate. |
| 1801 | * Called from java.security.cert.X509Certificate.getIssuerX500Principal(). |
| 1802 | */ |
| 1803 | public static X500Principal getIssuerX500Principal(X509Certificate cert) { |
| 1804 | try { |
| 1805 | return getX500Principal(cert, true); |
| 1806 | } catch (Exception e) { |
| 1807 | throw new RuntimeException("Could not parse issuer", e); |
| 1808 | } |
| 1809 | } |
| 1810 | |
| 1811 | /** |
| 1812 | * Returned the encoding of the given certificate for internal use. |
| 1813 | * Callers must guarantee that they neither modify it nor expose it |
| 1814 | * to untrusted code. Uses getEncodedInternal() if the certificate |
| 1815 | * is instance of X509CertImpl, getEncoded() otherwise. |
| 1816 | */ |
| 1817 | public static byte[] getEncodedInternal(Certificate cert) |
| 1818 | throws CertificateEncodingException { |
| 1819 | if (cert instanceof X509CertImpl) { |
| 1820 | return ((X509CertImpl)cert).getEncodedInternal(); |
| 1821 | } else { |
| 1822 | return cert.getEncoded(); |
| 1823 | } |
| 1824 | } |
| 1825 | |
| 1826 | /** |
| 1827 | * Utility method to convert an arbitrary instance of X509Certificate |
| 1828 | * to a X509CertImpl. Does a cast if possible, otherwise reparses |
| 1829 | * the encoding. |
| 1830 | */ |
| 1831 | public static X509CertImpl toImpl(X509Certificate cert) |
| 1832 | throws CertificateException { |
| 1833 | if (cert instanceof X509CertImpl) { |
| 1834 | return (X509CertImpl)cert; |
| 1835 | } else { |
| 1836 | return X509Factory.intern(cert); |
| 1837 | } |
| 1838 | } |
| 1839 | |
| 1840 | /** |
| 1841 | * Utility method to test if a certificate is self-issued. This is |
| 1842 | * the case iff the subject and issuer X500Principals are equal. |
| 1843 | */ |
| 1844 | public static boolean isSelfIssued(X509Certificate cert) { |
| 1845 | X500Principal subject = cert.getSubjectX500Principal(); |
| 1846 | X500Principal issuer = cert.getIssuerX500Principal(); |
| 1847 | return subject.equals(issuer); |
| 1848 | } |
| 1849 | |
| 1850 | /** |
| 1851 | * Utility method to test if a certificate is self-signed. This is |
| 1852 | * the case iff the subject and issuer X500Principals are equal |
| 1853 | * AND the certificate's subject public key can be used to verify |
| 1854 | * the certificate. In case of exception, returns false. |
| 1855 | */ |
| 1856 | public static boolean isSelfSigned(X509Certificate cert, |
| 1857 | String sigProvider) { |
| 1858 | if (isSelfIssued(cert)) { |
| 1859 | try { |
| 1860 | if (sigProvider == null) { |
| 1861 | cert.verify(cert.getPublicKey()); |
| 1862 | } else { |
| 1863 | cert.verify(cert.getPublicKey(), sigProvider); |
| 1864 | } |
| 1865 | return true; |
| 1866 | } catch (Exception e) { |
| 1867 | // In case of exception, return false |
| 1868 | } |
| 1869 | } |
| 1870 | return false; |
| 1871 | } |
| 1872 | } |