1 /*
   2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.x509;
  27 
  28 import java.io.IOException;
  29 import java.io.OutputStream;
  30 
  31 import java.security.cert.*;
  32 import java.util.*;
  33 
  34 import sun.security.util.*;
  35 import sun.misc.HexDumpEncoder;
  36 
  37 
  38 /**
  39  * The X509CertInfo class represents X.509 certificate information.
  40  *
  41  * <P>X.509 certificates have several base data elements, including:<UL>
  42  *
  43  * <LI>The <em>Subject Name</em>, an X.500 Distinguished Name for
  44  *      the entity (subject) for which the certificate was issued.
  45  *
  46  * <LI>The <em>Subject Public Key</em>, the public key of the subject.
  47  *      This is one of the most important parts of the certificate.
  48  *
  49  * <LI>The <em>Validity Period</em>, a time period (e.g. six months)
  50  *      within which the certificate is valid (unless revoked).
  51  *
  52  * <LI>The <em>Issuer Name</em>, an X.500 Distinguished Name for the
  53  *      Certificate Authority (CA) which issued the certificate.
  54  *
  55  * <LI>A <em>Serial Number</em> assigned by the CA, for use in
  56  *      certificate revocation and other applications.
  57  *
  58  * @author Amit Kapoor
  59  * @author Hemma Prafullchandra
  60  * @see CertAttrSet
  61  * @see X509CertImpl
  62  */
  63 public class X509CertInfo implements CertAttrSet<String> {
  64     /**
  65      * Identifier for this attribute, to be used with the
  66      * get, set, delete methods of Certificate, x509 type.
  67      */
  68     public static final String IDENT = "x509.info";
  69     // Certificate attribute names
  70     public static final String NAME = "info";
  71     public static final String DN_NAME = "dname";
  72     public static final String VERSION = CertificateVersion.NAME;
  73     public static final String SERIAL_NUMBER = CertificateSerialNumber.NAME;
  74     public static final String ALGORITHM_ID = CertificateAlgorithmId.NAME;
  75     public static final String ISSUER = "issuer";
  76     public static final String SUBJECT = "subject";
  77     public static final String VALIDITY = CertificateValidity.NAME;
  78     public static final String KEY = CertificateX509Key.NAME;
  79     public static final String ISSUER_ID = "issuerID";
  80     public static final String SUBJECT_ID = "subjectID";
  81     public static final String EXTENSIONS = CertificateExtensions.NAME;
  82 
  83     // X509.v1 data
  84     protected CertificateVersion version = new CertificateVersion();
  85     protected CertificateSerialNumber   serialNum = null;
  86     protected CertificateAlgorithmId    algId = null;
  87     protected X500Name                  issuer = null;
  88     protected X500Name                  subject = null;
  89     protected CertificateValidity       interval = null;
  90     protected CertificateX509Key        pubKey = null;
  91 
  92     // X509.v2 & v3 extensions
  93     protected UniqueIdentity   issuerUniqueId = null;
  94     protected UniqueIdentity  subjectUniqueId = null;
  95 
  96     // X509.v3 extensions
  97     protected CertificateExtensions     extensions = null;
  98 
  99     // Attribute numbers for internal manipulation
 100     private static final int ATTR_VERSION = 1;
 101     private static final int ATTR_SERIAL = 2;
 102     private static final int ATTR_ALGORITHM = 3;
 103     private static final int ATTR_ISSUER = 4;
 104     private static final int ATTR_VALIDITY = 5;
 105     private static final int ATTR_SUBJECT = 6;
 106     private static final int ATTR_KEY = 7;
 107     private static final int ATTR_ISSUER_ID = 8;
 108     private static final int ATTR_SUBJECT_ID = 9;
 109     private static final int ATTR_EXTENSIONS = 10;
 110 
 111     // DER encoded CertificateInfo data
 112     private byte[]      rawCertInfo = null;
 113 
 114     // The certificate attribute name to integer mapping stored here
 115     private static final Map<String,Integer> map = new HashMap<String,Integer>();
 116     static {
 117         map.put(VERSION, Integer.valueOf(ATTR_VERSION));
 118         map.put(SERIAL_NUMBER, Integer.valueOf(ATTR_SERIAL));
 119         map.put(ALGORITHM_ID, Integer.valueOf(ATTR_ALGORITHM));
 120         map.put(ISSUER, Integer.valueOf(ATTR_ISSUER));
 121         map.put(VALIDITY, Integer.valueOf(ATTR_VALIDITY));
 122         map.put(SUBJECT, Integer.valueOf(ATTR_SUBJECT));
 123         map.put(KEY, Integer.valueOf(ATTR_KEY));
 124         map.put(ISSUER_ID, Integer.valueOf(ATTR_ISSUER_ID));
 125         map.put(SUBJECT_ID, Integer.valueOf(ATTR_SUBJECT_ID));
 126         map.put(EXTENSIONS, Integer.valueOf(ATTR_EXTENSIONS));
 127     }
 128 
 129     /**
 130      * Construct an uninitialized X509CertInfo on which <a href="#decode">
 131      * decode</a> must later be called (or which may be deserialized).
 132      */
 133     public X509CertInfo() { }
 134 
 135     /**
 136      * Unmarshals a certificate from its encoded form, parsing the
 137      * encoded bytes.  This form of constructor is used by agents which
 138      * need to examine and use certificate contents.  That is, this is
 139      * one of the more commonly used constructors.  Note that the buffer
 140      * must include only a certificate, and no "garbage" may be left at
 141      * the end.  If you need to ignore data at the end of a certificate,
 142      * use another constructor.
 143      *
 144      * @param cert the encoded bytes, with no trailing data.
 145      * @exception CertificateParsingException on parsing errors.
 146      */
 147     public X509CertInfo(byte[] cert) throws CertificateParsingException {
 148         try {
 149             DerValue    in = new DerValue(cert);
 150 
 151             parse(in);
 152         } catch (IOException e) {
 153             throw new CertificateParsingException(e);
 154         }
 155     }
 156 
 157     /**
 158      * Unmarshal a certificate from its encoded form, parsing a DER value.
 159      * This form of constructor is used by agents which need to examine
 160      * and use certificate contents.
 161      *
 162      * @param derVal the der value containing the encoded cert.
 163      * @exception CertificateParsingException on parsing errors.
 164      */
 165     public X509CertInfo(DerValue derVal) throws CertificateParsingException {
 166         try {
 167             parse(derVal);
 168         } catch (IOException e) {
 169             throw new CertificateParsingException(e);
 170         }
 171     }
 172 
 173     /**
 174      * Appends the certificate to an output stream.
 175      *
 176      * @param out an output stream to which the certificate is appended.
 177      * @exception CertificateException on encoding errors.
 178      * @exception IOException on other errors.
 179      */
 180     public void encode(OutputStream out)
 181     throws CertificateException, IOException {
 182         if (rawCertInfo == null) {
 183             DerOutputStream tmp = new DerOutputStream();
 184             emit(tmp);
 185             rawCertInfo = tmp.toByteArray();
 186         }
 187         out.write(rawCertInfo.clone());
 188     }
 189 
 190     /**
 191      * Return an enumeration of names of attributes existing within this
 192      * attribute.
 193      */
 194     public Enumeration<String> getElements() {
 195         AttributeNameEnumeration elements = new AttributeNameEnumeration();
 196         elements.addElement(VERSION);
 197         elements.addElement(SERIAL_NUMBER);
 198         elements.addElement(ALGORITHM_ID);
 199         elements.addElement(ISSUER);
 200         elements.addElement(VALIDITY);
 201         elements.addElement(SUBJECT);
 202         elements.addElement(KEY);
 203         elements.addElement(ISSUER_ID);
 204         elements.addElement(SUBJECT_ID);
 205         elements.addElement(EXTENSIONS);
 206 
 207         return elements.elements();
 208     }
 209 
 210     /**
 211      * Return the name of this attribute.
 212      */
 213     public String getName() {
 214         return(NAME);
 215     }
 216 
 217     /**
 218      * Returns the encoded certificate info.
 219      *
 220      * @exception CertificateEncodingException on encoding information errors.
 221      */
 222     public byte[] getEncodedInfo() throws CertificateEncodingException {
 223         try {
 224             if (rawCertInfo == null) {
 225                 DerOutputStream tmp = new DerOutputStream();
 226                 emit(tmp);
 227                 rawCertInfo = tmp.toByteArray();
 228             }
 229             return rawCertInfo.clone();
 230         } catch (IOException e) {
 231             throw new CertificateEncodingException(e.toString());
 232         } catch (CertificateException e) {
 233             throw new CertificateEncodingException(e.toString());
 234         }
 235     }
 236 
 237     /**
 238      * Compares two X509CertInfo objects.  This is false if the
 239      * certificates are not both X.509 certs, otherwise it
 240      * compares them as binary data.
 241      *
 242      * @param other the object being compared with this one
 243      * @return true iff the certificates are equivalent
 244      */
 245     public boolean equals(Object other) {
 246         if (other instanceof X509CertInfo) {
 247             return equals((X509CertInfo) other);
 248         } else {
 249             return false;
 250         }
 251     }
 252 
 253     /**
 254      * Compares two certificates, returning false if any data
 255      * differs between the two.
 256      *
 257      * @param other the object being compared with this one
 258      * @return true iff the certificates are equivalent
 259      */
 260     public boolean equals(X509CertInfo other) {
 261         if (this == other) {
 262             return(true);
 263         } else if (rawCertInfo == null || other.rawCertInfo == null) {
 264             return(false);
 265         } else if (rawCertInfo.length != other.rawCertInfo.length) {
 266             return(false);
 267         }
 268         for (int i = 0; i < rawCertInfo.length; i++) {
 269             if (rawCertInfo[i] != other.rawCertInfo[i]) {
 270                 return(false);
 271             }
 272         }
 273         return(true);
 274     }
 275 
 276     /**
 277      * Calculates a hash code value for the object.  Objects
 278      * which are equal will also have the same hashcode.
 279      */
 280     public int hashCode() {
 281         int     retval = 0;
 282 
 283         for (int i = 1; i < rawCertInfo.length; i++) {
 284             retval += rawCertInfo[i] * i;
 285         }
 286         return(retval);
 287     }
 288 
 289     /**
 290      * Returns a printable representation of the certificate.
 291      */
 292     public String toString() {
 293 
 294         if (subject == null || pubKey == null || interval == null
 295             || issuer == null || algId == null || serialNum == null) {
 296                 throw new NullPointerException("X.509 cert is incomplete");
 297         }
 298         StringBuilder sb = new StringBuilder();
 299 
 300         sb.append("[\n");
 301         sb.append("  ").append(version).append('\n');
 302         sb.append("  Subject: ").append(subject).append('\n');
 303         sb.append("  Signature Algorithm: ").append(algId).append('\n');
 304         sb.append("  Key:  ").append(pubKey).append('\n');
 305         sb.append("  ").append(interval).append('\n');
 306         sb.append("  Issuer: ").append(issuer).append('\n');
 307         sb.append("  ").append(serialNum).append('\n');
 308 
 309         // optional v2, v3 extras
 310         if (issuerUniqueId != null) {
 311             sb.append("  Issuer Id:\n").append(issuerUniqueId).append('\n');
 312         }
 313         if (subjectUniqueId != null) {
 314             sb.append("  Subject Id:\n").append(subjectUniqueId).append('\n');
 315         }
 316         if (extensions != null) {
 317             Collection<Extension> allExts = extensions.getAllExtensions();
 318             Extension[] exts = allExts.toArray(new Extension[0]);
 319             sb.append("\nCertificate Extensions: ").append(exts.length);
 320             for (int i = 0; i < exts.length; i++) {
 321                 sb.append("\n[").append(i + 1).append("]: ");
 322                 Extension ext = exts[i];
 323                 try {
 324                     if (OIDMap.getClass(ext.getExtensionId()) == null) {
 325                         sb.append(ext.toString());
 326                         byte[] extValue = ext.getExtensionValue();
 327                         if (extValue != null) {
 328                             DerOutputStream out = new DerOutputStream();
 329                             out.putOctetString(extValue);
 330                             extValue = out.toByteArray();
 331                             HexDumpEncoder enc = new HexDumpEncoder();
 332                             sb.append("Extension unknown: " + "DER encoded OCTET string =\n")
 333                                     .append(enc.encodeBuffer(extValue)).append('\n');
 334                         }
 335                     } else
 336                         sb.append(ext.toString()); //sub-class exists
 337                 } catch (Exception e) {
 338                     sb.append(", Error parsing this extension");
 339                 }
 340             }
 341             Map<String,Extension> invalid = extensions.getUnparseableExtensions();
 342             if (invalid.isEmpty() == false) {
 343                 sb.append("\nUnparseable certificate extensions: ").append(invalid.size());
 344                 int i = 1;
 345                 for (Extension ext : invalid.values()) {
 346                     sb.append("\n[").append(i++).append("]: ");
 347                     sb.append(ext);
 348                 }
 349             }
 350         }
 351         sb.append("\n]");
 352         return sb.toString();
 353     }
 354 
 355     /**
 356      * Set the certificate attribute.
 357      *
 358      * @params name the name of the Certificate attribute.
 359      * @params val the value of the Certificate attribute.
 360      * @exception CertificateException on invalid attributes.
 361      * @exception IOException on other errors.
 362      */
 363     public void set(String name, Object val)
 364     throws CertificateException, IOException {
 365         X509AttributeName attrName = new X509AttributeName(name);
 366 
 367         int attr = attributeMap(attrName.getPrefix());
 368         if (attr == 0) {
 369             throw new CertificateException("Attribute name not recognized: "
 370                                            + name);
 371         }
 372         // set rawCertInfo to null, so that we are forced to re-encode
 373         rawCertInfo = null;
 374         String suffix = attrName.getSuffix();
 375 
 376         switch (attr) {
 377         case ATTR_VERSION:
 378             if (suffix == null) {
 379                 setVersion(val);
 380             } else {
 381                 version.set(suffix, val);
 382             }
 383             break;
 384 
 385         case ATTR_SERIAL:
 386             if (suffix == null) {
 387                 setSerialNumber(val);
 388             } else {
 389                 serialNum.set(suffix, val);
 390             }
 391             break;
 392 
 393         case ATTR_ALGORITHM:
 394             if (suffix == null) {
 395                 setAlgorithmId(val);
 396             } else {
 397                 algId.set(suffix, val);
 398             }
 399             break;
 400 
 401         case ATTR_ISSUER:
 402             setIssuer(val);
 403             break;
 404 
 405         case ATTR_VALIDITY:
 406             if (suffix == null) {
 407                 setValidity(val);
 408             } else {
 409                 interval.set(suffix, val);
 410             }
 411             break;
 412 
 413         case ATTR_SUBJECT:
 414             setSubject(val);
 415             break;
 416 
 417         case ATTR_KEY:
 418             if (suffix == null) {
 419                 setKey(val);
 420             } else {
 421                 pubKey.set(suffix, val);
 422             }
 423             break;
 424 
 425         case ATTR_ISSUER_ID:
 426             setIssuerUniqueId(val);
 427             break;
 428 
 429         case ATTR_SUBJECT_ID:
 430             setSubjectUniqueId(val);
 431             break;
 432 
 433         case ATTR_EXTENSIONS:
 434             if (suffix == null) {
 435                 setExtensions(val);
 436             } else {
 437                 if (extensions == null)
 438                     extensions = new CertificateExtensions();
 439                 extensions.set(suffix, val);
 440             }
 441             break;
 442         }
 443     }
 444 
 445     /**
 446      * Delete the certificate attribute.
 447      *
 448      * @params name the name of the Certificate attribute.
 449      * @exception CertificateException on invalid attributes.
 450      * @exception IOException on other errors.
 451      */
 452     public void delete(String name)
 453     throws CertificateException, IOException {
 454         X509AttributeName attrName = new X509AttributeName(name);
 455 
 456         int attr = attributeMap(attrName.getPrefix());
 457         if (attr == 0) {
 458             throw new CertificateException("Attribute name not recognized: "
 459                                            + name);
 460         }
 461         // set rawCertInfo to null, so that we are forced to re-encode
 462         rawCertInfo = null;
 463         String suffix = attrName.getSuffix();
 464 
 465         switch (attr) {
 466         case ATTR_VERSION:
 467             if (suffix == null) {
 468                 version = null;
 469             } else {
 470                 version.delete(suffix);
 471             }
 472             break;
 473         case (ATTR_SERIAL):
 474             if (suffix == null) {
 475                 serialNum = null;
 476             } else {
 477                 serialNum.delete(suffix);
 478             }
 479             break;
 480         case (ATTR_ALGORITHM):
 481             if (suffix == null) {
 482                 algId = null;
 483             } else {
 484                 algId.delete(suffix);
 485             }
 486             break;
 487         case (ATTR_ISSUER):
 488             issuer = null;
 489             break;
 490         case (ATTR_VALIDITY):
 491             if (suffix == null) {
 492                 interval = null;
 493             } else {
 494                 interval.delete(suffix);
 495             }
 496             break;
 497         case (ATTR_SUBJECT):
 498             subject = null;
 499             break;
 500         case (ATTR_KEY):
 501             if (suffix == null) {
 502                 pubKey = null;
 503             } else {
 504                 pubKey.delete(suffix);
 505             }
 506             break;
 507         case (ATTR_ISSUER_ID):
 508             issuerUniqueId = null;
 509             break;
 510         case (ATTR_SUBJECT_ID):
 511             subjectUniqueId = null;
 512             break;
 513         case (ATTR_EXTENSIONS):
 514             if (suffix == null) {
 515                 extensions = null;
 516             } else {
 517                 if (extensions != null)
 518                    extensions.delete(suffix);
 519             }
 520             break;
 521         }
 522     }
 523 
 524     /**
 525      * Get the certificate attribute.
 526      *
 527      * @params name the name of the Certificate attribute.
 528      *
 529      * @exception CertificateException on invalid attributes.
 530      * @exception IOException on other errors.
 531      */
 532     public Object get(String name)
 533     throws CertificateException, IOException {
 534         X509AttributeName attrName = new X509AttributeName(name);
 535 
 536         int attr = attributeMap(attrName.getPrefix());
 537         if (attr == 0) {
 538             throw new CertificateParsingException(
 539                           "Attribute name not recognized: " + name);
 540         }
 541         String suffix = attrName.getSuffix();
 542 
 543         switch (attr) { // frequently used attributes first
 544         case (ATTR_EXTENSIONS):
 545             if (suffix == null) {
 546                 return(extensions);
 547             } else {
 548                 if (extensions == null) {
 549                     return null;
 550                 } else {
 551                     return(extensions.get(suffix));
 552                 }
 553             }
 554         case (ATTR_SUBJECT):
 555             if (suffix == null) {
 556                 return(subject);
 557             } else {
 558                 return(getX500Name(suffix, false));
 559             }
 560         case (ATTR_ISSUER):
 561             if (suffix == null) {
 562                 return(issuer);
 563             } else {
 564                 return(getX500Name(suffix, true));
 565             }
 566         case (ATTR_KEY):
 567             if (suffix == null) {
 568                 return(pubKey);
 569             } else {
 570                 return(pubKey.get(suffix));
 571             }
 572         case (ATTR_ALGORITHM):
 573             if (suffix == null) {
 574                 return(algId);
 575             } else {
 576                 return(algId.get(suffix));
 577             }
 578         case (ATTR_VALIDITY):
 579             if (suffix == null) {
 580                 return(interval);
 581             } else {
 582                 return(interval.get(suffix));
 583             }
 584         case (ATTR_VERSION):
 585             if (suffix == null) {
 586                 return(version);
 587             } else {
 588                 return(version.get(suffix));
 589             }
 590         case (ATTR_SERIAL):
 591             if (suffix == null) {
 592                 return(serialNum);
 593             } else {
 594                 return(serialNum.get(suffix));
 595             }
 596         case (ATTR_ISSUER_ID):
 597             return(issuerUniqueId);
 598         case (ATTR_SUBJECT_ID):
 599             return(subjectUniqueId);
 600         }
 601         return null;
 602     }
 603 
 604     /*
 605      * Get the Issuer or Subject name
 606      */
 607     private Object getX500Name(String name, boolean getIssuer)
 608         throws IOException {
 609         if (name.equalsIgnoreCase(X509CertInfo.DN_NAME)) {
 610             return getIssuer ? issuer : subject;
 611         } else if (name.equalsIgnoreCase("x500principal")) {
 612             return getIssuer ? issuer.asX500Principal()
 613                              : subject.asX500Principal();
 614         } else {
 615             throw new IOException("Attribute name not recognized.");
 616         }
 617     }
 618 
 619     /*
 620      * This routine unmarshals the certificate information.
 621      */
 622     private void parse(DerValue val)
 623     throws CertificateParsingException, IOException {
 624         DerInputStream  in;
 625         DerValue        tmp;
 626 
 627         if (val.tag != DerValue.tag_Sequence) {
 628             throw new CertificateParsingException("signed fields invalid");
 629         }
 630         rawCertInfo = val.toByteArray();
 631 
 632         in = val.data;
 633 
 634         // Version
 635         tmp = in.getDerValue();
 636         if (tmp.isContextSpecific((byte)0)) {
 637             version = new CertificateVersion(tmp);
 638             tmp = in.getDerValue();
 639         }
 640 
 641         // Serial number ... an integer
 642         serialNum = new CertificateSerialNumber(tmp);
 643 
 644         // Algorithm Identifier
 645         algId = new CertificateAlgorithmId(in);
 646 
 647         // Issuer name
 648         issuer = new X500Name(in);
 649         if (issuer.isEmpty()) {
 650             throw new CertificateParsingException(
 651                 "Empty issuer DN not allowed in X509Certificates");
 652         }
 653 
 654         // validity:  SEQUENCE { start date, end date }
 655         interval = new CertificateValidity(in);
 656 
 657         // subject name
 658         subject = new X500Name(in);
 659         if ((version.compare(CertificateVersion.V1) == 0) &&
 660                 subject.isEmpty()) {
 661             throw new CertificateParsingException(
 662                       "Empty subject DN not allowed in v1 certificate");
 663         }
 664 
 665         // public key
 666         pubKey = new CertificateX509Key(in);
 667 
 668         // If more data available, make sure version is not v1.
 669         if (in.available() != 0) {
 670             if (version.compare(CertificateVersion.V1) == 0) {
 671                 throw new CertificateParsingException(
 672                           "no more data allowed for version 1 certificate");
 673             }
 674         } else {
 675             return;
 676         }
 677 
 678         // Get the issuerUniqueId if present
 679         tmp = in.getDerValue();
 680         if (tmp.isContextSpecific((byte)1)) {
 681             issuerUniqueId = new UniqueIdentity(tmp);
 682             if (in.available() == 0)
 683                 return;
 684             tmp = in.getDerValue();
 685         }
 686 
 687         // Get the subjectUniqueId if present.
 688         if (tmp.isContextSpecific((byte)2)) {
 689             subjectUniqueId = new UniqueIdentity(tmp);
 690             if (in.available() == 0)
 691                 return;
 692             tmp = in.getDerValue();
 693         }
 694 
 695         // Get the extensions.
 696         if (version.compare(CertificateVersion.V3) != 0) {
 697             throw new CertificateParsingException(
 698                       "Extensions not allowed in v2 certificate");
 699         }
 700         if (tmp.isConstructed() && tmp.isContextSpecific((byte)3)) {
 701             extensions = new CertificateExtensions(tmp.data);
 702         }
 703 
 704         // verify X.509 V3 Certificate
 705         verifyCert(subject, extensions);
 706 
 707     }
 708 
 709     /*
 710      * Verify if X.509 V3 Certificate is compliant with RFC 3280.
 711      */
 712     private void verifyCert(X500Name subject,
 713         CertificateExtensions extensions)
 714         throws CertificateParsingException, IOException {
 715 
 716         // if SubjectName is empty, check for SubjectAlternativeNameExtension
 717         if (subject.isEmpty()) {
 718             if (extensions == null) {
 719                 throw new CertificateParsingException("X.509 Certificate is " +
 720                         "incomplete: subject field is empty, and certificate " +
 721                         "has no extensions");
 722             }
 723             SubjectAlternativeNameExtension subjectAltNameExt = null;
 724             SubjectAlternativeNameExtension extValue = null;
 725             GeneralNames names = null;
 726             try {
 727                 subjectAltNameExt = (SubjectAlternativeNameExtension)
 728                         extensions.get(SubjectAlternativeNameExtension.NAME);
 729                 names = subjectAltNameExt.get(
 730                         SubjectAlternativeNameExtension.SUBJECT_NAME);
 731             } catch (IOException e) {
 732                 throw new CertificateParsingException("X.509 Certificate is " +
 733                         "incomplete: subject field is empty, and " +
 734                         "SubjectAlternativeName extension is absent");
 735             }
 736 
 737             // SubjectAlternativeName extension is empty or not marked critical
 738             if (names == null || names.isEmpty()) {
 739                 throw new CertificateParsingException("X.509 Certificate is " +
 740                         "incomplete: subject field is empty, and " +
 741                         "SubjectAlternativeName extension is empty");
 742             } else if (subjectAltNameExt.isCritical() == false) {
 743                 throw new CertificateParsingException("X.509 Certificate is " +
 744                         "incomplete: SubjectAlternativeName extension MUST " +
 745                         "be marked critical when subject field is empty");
 746             }
 747         }
 748     }
 749 
 750     /*
 751      * Marshal the contents of a "raw" certificate into a DER sequence.
 752      */
 753     private void emit(DerOutputStream out)
 754     throws CertificateException, IOException {
 755         DerOutputStream tmp = new DerOutputStream();
 756 
 757         // version number, iff not V1
 758         version.encode(tmp);
 759 
 760         // Encode serial number, issuer signing algorithm, issuer name
 761         // and validity
 762         serialNum.encode(tmp);
 763         algId.encode(tmp);
 764 
 765         if ((version.compare(CertificateVersion.V1) == 0) &&
 766             (issuer.toString() == null))
 767             throw new CertificateParsingException(
 768                       "Null issuer DN not allowed in v1 certificate");
 769 
 770         issuer.encode(tmp);
 771         interval.encode(tmp);
 772 
 773         // Encode subject (principal) and associated key
 774         if ((version.compare(CertificateVersion.V1) == 0) &&
 775             (subject.toString() == null))
 776             throw new CertificateParsingException(
 777                       "Null subject DN not allowed in v1 certificate");
 778         subject.encode(tmp);
 779         pubKey.encode(tmp);
 780 
 781         // Encode issuerUniqueId & subjectUniqueId.
 782         if (issuerUniqueId != null) {
 783             issuerUniqueId.encode(tmp, DerValue.createTag(DerValue.TAG_CONTEXT,
 784                                                           false,(byte)1));
 785         }
 786         if (subjectUniqueId != null) {
 787             subjectUniqueId.encode(tmp, DerValue.createTag(DerValue.TAG_CONTEXT,
 788                                                            false,(byte)2));
 789         }
 790 
 791         // Write all the extensions.
 792         if (extensions != null) {
 793             extensions.encode(tmp);
 794         }
 795 
 796         // Wrap the data; encoding of the "raw" cert is now complete.
 797         out.write(DerValue.tag_Sequence, tmp);
 798     }
 799 
 800     /**
 801      * Returns the integer attribute number for the passed attribute name.
 802      */
 803     private int attributeMap(String name) {
 804         Integer num = map.get(name);
 805         if (num == null) {
 806             return 0;
 807         }
 808         return num.intValue();
 809     }
 810 
 811     /**
 812      * Set the version number of the certificate.
 813      *
 814      * @params val the Object class value for the Extensions
 815      * @exception CertificateException on invalid data.
 816      */
 817     private void setVersion(Object val) throws CertificateException {
 818         if (!(val instanceof CertificateVersion)) {
 819             throw new CertificateException("Version class type invalid.");
 820         }
 821         version = (CertificateVersion)val;
 822     }
 823 
 824     /**
 825      * Set the serial number of the certificate.
 826      *
 827      * @params val the Object class value for the CertificateSerialNumber
 828      * @exception CertificateException on invalid data.
 829      */
 830     private void setSerialNumber(Object val) throws CertificateException {
 831         if (!(val instanceof CertificateSerialNumber)) {
 832             throw new CertificateException("SerialNumber class type invalid.");
 833         }
 834         serialNum = (CertificateSerialNumber)val;
 835     }
 836 
 837     /**
 838      * Set the algorithm id of the certificate.
 839      *
 840      * @params val the Object class value for the AlgorithmId
 841      * @exception CertificateException on invalid data.
 842      */
 843     private void setAlgorithmId(Object val) throws CertificateException {
 844         if (!(val instanceof CertificateAlgorithmId)) {
 845             throw new CertificateException(
 846                              "AlgorithmId class type invalid.");
 847         }
 848         algId = (CertificateAlgorithmId)val;
 849     }
 850 
 851     /**
 852      * Set the issuer name of the certificate.
 853      *
 854      * @params val the Object class value for the issuer
 855      * @exception CertificateException on invalid data.
 856      */
 857     private void setIssuer(Object val) throws CertificateException {
 858         if (!(val instanceof X500Name)) {
 859             throw new CertificateException(
 860                              "Issuer class type invalid.");
 861         }
 862         issuer = (X500Name)val;
 863     }
 864 
 865     /**
 866      * Set the validity interval of the certificate.
 867      *
 868      * @params val the Object class value for the CertificateValidity
 869      * @exception CertificateException on invalid data.
 870      */
 871     private void setValidity(Object val) throws CertificateException {
 872         if (!(val instanceof CertificateValidity)) {
 873             throw new CertificateException(
 874                              "CertificateValidity class type invalid.");
 875         }
 876         interval = (CertificateValidity)val;
 877     }
 878 
 879     /**
 880      * Set the subject name of the certificate.
 881      *
 882      * @params val the Object class value for the Subject
 883      * @exception CertificateException on invalid data.
 884      */
 885     private void setSubject(Object val) throws CertificateException {
 886         if (!(val instanceof X500Name)) {
 887             throw new CertificateException(
 888                              "Subject class type invalid.");
 889         }
 890         subject = (X500Name)val;
 891     }
 892 
 893     /**
 894      * Set the public key in the certificate.
 895      *
 896      * @params val the Object class value for the PublicKey
 897      * @exception CertificateException on invalid data.
 898      */
 899     private void setKey(Object val) throws CertificateException {
 900         if (!(val instanceof CertificateX509Key)) {
 901             throw new CertificateException(
 902                              "Key class type invalid.");
 903         }
 904         pubKey = (CertificateX509Key)val;
 905     }
 906 
 907     /**
 908      * Set the Issuer Unique Identity in the certificate.
 909      *
 910      * @params val the Object class value for the IssuerUniqueId
 911      * @exception CertificateException
 912      */
 913     private void setIssuerUniqueId(Object val) throws CertificateException {
 914         if (version.compare(CertificateVersion.V2) < 0) {
 915             throw new CertificateException("Invalid version");
 916         }
 917         if (!(val instanceof UniqueIdentity)) {
 918             throw new CertificateException(
 919                              "IssuerUniqueId class type invalid.");
 920         }
 921         issuerUniqueId = (UniqueIdentity)val;
 922     }
 923 
 924     /**
 925      * Set the Subject Unique Identity in the certificate.
 926      *
 927      * @params val the Object class value for the SubjectUniqueId
 928      * @exception CertificateException
 929      */
 930     private void setSubjectUniqueId(Object val) throws CertificateException {
 931         if (version.compare(CertificateVersion.V2) < 0) {
 932             throw new CertificateException("Invalid version");
 933         }
 934         if (!(val instanceof UniqueIdentity)) {
 935             throw new CertificateException(
 936                              "SubjectUniqueId class type invalid.");
 937         }
 938         subjectUniqueId = (UniqueIdentity)val;
 939     }
 940 
 941     /**
 942      * Set the extensions in the certificate.
 943      *
 944      * @params val the Object class value for the Extensions
 945      * @exception CertificateException
 946      */
 947     private void setExtensions(Object val) throws CertificateException {
 948         if (version.compare(CertificateVersion.V3) < 0) {
 949             throw new CertificateException("Invalid version");
 950         }
 951         if (!(val instanceof CertificateExtensions)) {
 952           throw new CertificateException(
 953                              "Extensions class type invalid.");
 954         }
 955         extensions = (CertificateExtensions)val;
 956     }
 957 }