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("  " + version.toString() + "\n");
 302         sb.append("  Subject: " + subject.toString() + "\n");
 303         sb.append("  Signature Algorithm: " + algId.toString() + "\n");
 304         sb.append("  Key:  " + pubKey.toString() + "\n");
 305         sb.append("  " + interval.toString() + "\n");
 306         sb.append("  Issuer: " + issuer.toString() + "\n");
 307         sb.append("  " + serialNum.toString() + "\n");
 308 
 309         // optional v2, v3 extras
 310         if (issuerUniqueId != null) {
 311             sb.append("  Issuer Id:\n" + issuerUniqueId.toString() + "\n");
 312         }
 313         if (subjectUniqueId != null) {
 314             sb.append("  Subject Id:\n" + subjectUniqueId.toString() + "\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: " + exts.length);
 320             for (int i = 0; i < exts.length; i++) {
 321                 sb.append("\n[" + (i+1) + "]: ");
 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: "
 333                                       + "DER encoded OCTET string =\n"
 334                                       + enc.encodeBuffer(extValue) + "\n");
 335                         }
 336                     } else
 337                         sb.append(ext.toString()); //sub-class exists
 338                 } catch (Exception e) {
 339                     sb.append(", Error parsing this extension");
 340                 }
 341             }
 342             Map<String,Extension> invalid = extensions.getUnparseableExtensions();
 343             if (invalid.isEmpty() == false) {
 344                 sb.append("\nUnparseable certificate extensions: " + invalid.size());
 345                 int i = 1;
 346                 for (Extension ext : invalid.values()) {
 347                     sb.append("\n[" + (i++) + "]: ");
 348                     sb.append(ext);
 349                 }
 350             }
 351         }
 352         sb.append("\n]");
 353         return sb.toString();
 354     }
 355 
 356     /**
 357      * Set the certificate attribute.
 358      *
 359      * @params name the name of the Certificate attribute.
 360      * @params val the value of the Certificate attribute.
 361      * @exception CertificateException on invalid attributes.
 362      * @exception IOException on other errors.
 363      */
 364     public void set(String name, Object val)
 365     throws CertificateException, IOException {
 366         X509AttributeName attrName = new X509AttributeName(name);
 367 
 368         int attr = attributeMap(attrName.getPrefix());
 369         if (attr == 0) {
 370             throw new CertificateException("Attribute name not recognized: "
 371                                            + name);
 372         }
 373         // set rawCertInfo to null, so that we are forced to re-encode
 374         rawCertInfo = null;
 375         String suffix = attrName.getSuffix();
 376 
 377         switch (attr) {
 378         case ATTR_VERSION:
 379             if (suffix == null) {
 380                 setVersion(val);
 381             } else {
 382                 version.set(suffix, val);
 383             }
 384             break;
 385 
 386         case ATTR_SERIAL:
 387             if (suffix == null) {
 388                 setSerialNumber(val);
 389             } else {
 390                 serialNum.set(suffix, val);
 391             }
 392             break;
 393 
 394         case ATTR_ALGORITHM:
 395             if (suffix == null) {
 396                 setAlgorithmId(val);
 397             } else {
 398                 algId.set(suffix, val);
 399             }
 400             break;
 401 
 402         case ATTR_ISSUER:
 403             setIssuer(val);
 404             break;
 405 
 406         case ATTR_VALIDITY:
 407             if (suffix == null) {
 408                 setValidity(val);
 409             } else {
 410                 interval.set(suffix, val);
 411             }
 412             break;
 413 
 414         case ATTR_SUBJECT:
 415             setSubject(val);
 416             break;
 417 
 418         case ATTR_KEY:
 419             if (suffix == null) {
 420                 setKey(val);
 421             } else {
 422                 pubKey.set(suffix, val);
 423             }
 424             break;
 425 
 426         case ATTR_ISSUER_ID:
 427             setIssuerUniqueId(val);
 428             break;
 429 
 430         case ATTR_SUBJECT_ID:
 431             setSubjectUniqueId(val);
 432             break;
 433 
 434         case ATTR_EXTENSIONS:
 435             if (suffix == null) {
 436                 setExtensions(val);
 437             } else {
 438                 if (extensions == null)
 439                     extensions = new CertificateExtensions();
 440                 extensions.set(suffix, val);
 441             }
 442             break;
 443         }
 444     }
 445 
 446     /**
 447      * Delete the certificate attribute.
 448      *
 449      * @params name the name of the Certificate attribute.
 450      * @exception CertificateException on invalid attributes.
 451      * @exception IOException on other errors.
 452      */
 453     public void delete(String name)
 454     throws CertificateException, IOException {
 455         X509AttributeName attrName = new X509AttributeName(name);
 456 
 457         int attr = attributeMap(attrName.getPrefix());
 458         if (attr == 0) {
 459             throw new CertificateException("Attribute name not recognized: "
 460                                            + name);
 461         }
 462         // set rawCertInfo to null, so that we are forced to re-encode
 463         rawCertInfo = null;
 464         String suffix = attrName.getSuffix();
 465 
 466         switch (attr) {
 467         case ATTR_VERSION:
 468             if (suffix == null) {
 469                 version = null;
 470             } else {
 471                 version.delete(suffix);
 472             }
 473             break;
 474         case (ATTR_SERIAL):
 475             if (suffix == null) {
 476                 serialNum = null;
 477             } else {
 478                 serialNum.delete(suffix);
 479             }
 480             break;
 481         case (ATTR_ALGORITHM):
 482             if (suffix == null) {
 483                 algId = null;
 484             } else {
 485                 algId.delete(suffix);
 486             }
 487             break;
 488         case (ATTR_ISSUER):
 489             issuer = null;
 490             break;
 491         case (ATTR_VALIDITY):
 492             if (suffix == null) {
 493                 interval = null;
 494             } else {
 495                 interval.delete(suffix);
 496             }
 497             break;
 498         case (ATTR_SUBJECT):
 499             subject = null;
 500             break;
 501         case (ATTR_KEY):
 502             if (suffix == null) {
 503                 pubKey = null;
 504             } else {
 505                 pubKey.delete(suffix);
 506             }
 507             break;
 508         case (ATTR_ISSUER_ID):
 509             issuerUniqueId = null;
 510             break;
 511         case (ATTR_SUBJECT_ID):
 512             subjectUniqueId = null;
 513             break;
 514         case (ATTR_EXTENSIONS):
 515             if (suffix == null) {
 516                 extensions = null;
 517             } else {
 518                 if (extensions != null)
 519                    extensions.delete(suffix);
 520             }
 521             break;
 522         }
 523     }
 524 
 525     /**
 526      * Get the certificate attribute.
 527      *
 528      * @params name the name of the Certificate attribute.
 529      *
 530      * @exception CertificateException on invalid attributes.
 531      * @exception IOException on other errors.
 532      */
 533     public Object get(String name)
 534     throws CertificateException, IOException {
 535         X509AttributeName attrName = new X509AttributeName(name);
 536 
 537         int attr = attributeMap(attrName.getPrefix());
 538         if (attr == 0) {
 539             throw new CertificateParsingException(
 540                           "Attribute name not recognized: " + name);
 541         }
 542         String suffix = attrName.getSuffix();
 543 
 544         switch (attr) { // frequently used attributes first
 545         case (ATTR_EXTENSIONS):
 546             if (suffix == null) {
 547                 return(extensions);
 548             } else {
 549                 if (extensions == null) {
 550                     return null;
 551                 } else {
 552                     return(extensions.get(suffix));
 553                 }
 554             }
 555         case (ATTR_SUBJECT):
 556             if (suffix == null) {
 557                 return(subject);
 558             } else {
 559                 return(getX500Name(suffix, false));
 560             }
 561         case (ATTR_ISSUER):
 562             if (suffix == null) {
 563                 return(issuer);
 564             } else {
 565                 return(getX500Name(suffix, true));
 566             }
 567         case (ATTR_KEY):
 568             if (suffix == null) {
 569                 return(pubKey);
 570             } else {
 571                 return(pubKey.get(suffix));
 572             }
 573         case (ATTR_ALGORITHM):
 574             if (suffix == null) {
 575                 return(algId);
 576             } else {
 577                 return(algId.get(suffix));
 578             }
 579         case (ATTR_VALIDITY):
 580             if (suffix == null) {
 581                 return(interval);
 582             } else {
 583                 return(interval.get(suffix));
 584             }
 585         case (ATTR_VERSION):
 586             if (suffix == null) {
 587                 return(version);
 588             } else {
 589                 return(version.get(suffix));
 590             }
 591         case (ATTR_SERIAL):
 592             if (suffix == null) {
 593                 return(serialNum);
 594             } else {
 595                 return(serialNum.get(suffix));
 596             }
 597         case (ATTR_ISSUER_ID):
 598             return(issuerUniqueId);
 599         case (ATTR_SUBJECT_ID):
 600             return(subjectUniqueId);
 601         }
 602         return null;
 603     }
 604 
 605     /*
 606      * Get the Issuer or Subject name
 607      */
 608     private Object getX500Name(String name, boolean getIssuer)
 609         throws IOException {
 610         if (name.equalsIgnoreCase(X509CertInfo.DN_NAME)) {
 611             return getIssuer ? issuer : subject;
 612         } else if (name.equalsIgnoreCase("x500principal")) {
 613             return getIssuer ? issuer.asX500Principal()
 614                              : subject.asX500Principal();
 615         } else {
 616             throw new IOException("Attribute name not recognized.");
 617         }
 618     }
 619 
 620     /*
 621      * This routine unmarshals the certificate information.
 622      */
 623     private void parse(DerValue val)
 624     throws CertificateParsingException, IOException {
 625         DerInputStream  in;
 626         DerValue        tmp;
 627 
 628         if (val.tag != DerValue.tag_Sequence) {
 629             throw new CertificateParsingException("signed fields invalid");
 630         }
 631         rawCertInfo = val.toByteArray();
 632 
 633         in = val.data;
 634 
 635         // Version
 636         tmp = in.getDerValue();
 637         if (tmp.isContextSpecific((byte)0)) {
 638             version = new CertificateVersion(tmp);
 639             tmp = in.getDerValue();
 640         }
 641 
 642         // Serial number ... an integer
 643         serialNum = new CertificateSerialNumber(tmp);
 644 
 645         // Algorithm Identifier
 646         algId = new CertificateAlgorithmId(in);
 647 
 648         // Issuer name
 649         issuer = new X500Name(in);
 650         if (issuer.isEmpty()) {
 651             throw new CertificateParsingException(
 652                 "Empty issuer DN not allowed in X509Certificates");
 653         }
 654 
 655         // validity:  SEQUENCE { start date, end date }
 656         interval = new CertificateValidity(in);
 657 
 658         // subject name
 659         subject = new X500Name(in);
 660         if ((version.compare(CertificateVersion.V1) == 0) &&
 661                 subject.isEmpty()) {
 662             throw new CertificateParsingException(
 663                       "Empty subject DN not allowed in v1 certificate");
 664         }
 665 
 666         // public key
 667         pubKey = new CertificateX509Key(in);
 668 
 669         // If more data available, make sure version is not v1.
 670         if (in.available() != 0) {
 671             if (version.compare(CertificateVersion.V1) == 0) {
 672                 throw new CertificateParsingException(
 673                           "no more data allowed for version 1 certificate");
 674             }
 675         } else {
 676             return;
 677         }
 678 
 679         // Get the issuerUniqueId if present
 680         tmp = in.getDerValue();
 681         if (tmp.isContextSpecific((byte)1)) {
 682             issuerUniqueId = new UniqueIdentity(tmp);
 683             if (in.available() == 0)
 684                 return;
 685             tmp = in.getDerValue();
 686         }
 687 
 688         // Get the subjectUniqueId if present.
 689         if (tmp.isContextSpecific((byte)2)) {
 690             subjectUniqueId = new UniqueIdentity(tmp);
 691             if (in.available() == 0)
 692                 return;
 693             tmp = in.getDerValue();
 694         }
 695 
 696         // Get the extensions.
 697         if (version.compare(CertificateVersion.V3) != 0) {
 698             throw new CertificateParsingException(
 699                       "Extensions not allowed in v2 certificate");
 700         }
 701         if (tmp.isConstructed() && tmp.isContextSpecific((byte)3)) {
 702             extensions = new CertificateExtensions(tmp.data);
 703         }
 704 
 705         // verify X.509 V3 Certificate
 706         verifyCert(subject, extensions);
 707 
 708     }
 709 
 710     /*
 711      * Verify if X.509 V3 Certificate is compliant with RFC 3280.
 712      */
 713     private void verifyCert(X500Name subject,
 714         CertificateExtensions extensions)
 715         throws CertificateParsingException, IOException {
 716 
 717         // if SubjectName is empty, check for SubjectAlternativeNameExtension
 718         if (subject.isEmpty()) {
 719             if (extensions == null) {
 720                 throw new CertificateParsingException("X.509 Certificate is " +
 721                         "incomplete: subject field is empty, and certificate " +
 722                         "has no extensions");
 723             }
 724             SubjectAlternativeNameExtension subjectAltNameExt = null;
 725             SubjectAlternativeNameExtension extValue = null;
 726             GeneralNames names = null;
 727             try {
 728                 subjectAltNameExt = (SubjectAlternativeNameExtension)
 729                         extensions.get(SubjectAlternativeNameExtension.NAME);
 730                 names = subjectAltNameExt.get(
 731                         SubjectAlternativeNameExtension.SUBJECT_NAME);
 732             } catch (IOException e) {
 733                 throw new CertificateParsingException("X.509 Certificate is " +
 734                         "incomplete: subject field is empty, and " +
 735                         "SubjectAlternativeName extension is absent");
 736             }
 737 
 738             // SubjectAlternativeName extension is empty or not marked critical
 739             if (names == null || names.isEmpty()) {
 740                 throw new CertificateParsingException("X.509 Certificate is " +
 741                         "incomplete: subject field is empty, and " +
 742                         "SubjectAlternativeName extension is empty");
 743             } else if (subjectAltNameExt.isCritical() == false) {
 744                 throw new CertificateParsingException("X.509 Certificate is " +
 745                         "incomplete: SubjectAlternativeName extension MUST " +
 746                         "be marked critical when subject field is empty");
 747             }
 748         }
 749     }
 750 
 751     /*
 752      * Marshal the contents of a "raw" certificate into a DER sequence.
 753      */
 754     private void emit(DerOutputStream out)
 755     throws CertificateException, IOException {
 756         DerOutputStream tmp = new DerOutputStream();
 757 
 758         // version number, iff not V1
 759         version.encode(tmp);
 760 
 761         // Encode serial number, issuer signing algorithm, issuer name
 762         // and validity
 763         serialNum.encode(tmp);
 764         algId.encode(tmp);
 765 
 766         if ((version.compare(CertificateVersion.V1) == 0) &&
 767             (issuer.toString() == null))
 768             throw new CertificateParsingException(
 769                       "Null issuer DN not allowed in v1 certificate");
 770 
 771         issuer.encode(tmp);
 772         interval.encode(tmp);
 773 
 774         // Encode subject (principal) and associated key
 775         if ((version.compare(CertificateVersion.V1) == 0) &&
 776             (subject.toString() == null))
 777             throw new CertificateParsingException(
 778                       "Null subject DN not allowed in v1 certificate");
 779         subject.encode(tmp);
 780         pubKey.encode(tmp);
 781 
 782         // Encode issuerUniqueId & subjectUniqueId.
 783         if (issuerUniqueId != null) {
 784             issuerUniqueId.encode(tmp, DerValue.createTag(DerValue.TAG_CONTEXT,
 785                                                           false,(byte)1));
 786         }
 787         if (subjectUniqueId != null) {
 788             subjectUniqueId.encode(tmp, DerValue.createTag(DerValue.TAG_CONTEXT,
 789                                                            false,(byte)2));
 790         }
 791 
 792         // Write all the extensions.
 793         if (extensions != null) {
 794             extensions.encode(tmp);
 795         }
 796 
 797         // Wrap the data; encoding of the "raw" cert is now complete.
 798         out.write(DerValue.tag_Sequence, tmp);
 799     }
 800 
 801     /**
 802      * Returns the integer attribute number for the passed attribute name.
 803      */
 804     private int attributeMap(String name) {
 805         Integer num = map.get(name);
 806         if (num == null) {
 807             return 0;
 808         }
 809         return num.intValue();
 810     }
 811 
 812     /**
 813      * Set the version number of the certificate.
 814      *
 815      * @params val the Object class value for the Extensions
 816      * @exception CertificateException on invalid data.
 817      */
 818     private void setVersion(Object val) throws CertificateException {
 819         if (!(val instanceof CertificateVersion)) {
 820             throw new CertificateException("Version class type invalid.");
 821         }
 822         version = (CertificateVersion)val;
 823     }
 824 
 825     /**
 826      * Set the serial number of the certificate.
 827      *
 828      * @params val the Object class value for the CertificateSerialNumber
 829      * @exception CertificateException on invalid data.
 830      */
 831     private void setSerialNumber(Object val) throws CertificateException {
 832         if (!(val instanceof CertificateSerialNumber)) {
 833             throw new CertificateException("SerialNumber class type invalid.");
 834         }
 835         serialNum = (CertificateSerialNumber)val;
 836     }
 837 
 838     /**
 839      * Set the algorithm id of the certificate.
 840      *
 841      * @params val the Object class value for the AlgorithmId
 842      * @exception CertificateException on invalid data.
 843      */
 844     private void setAlgorithmId(Object val) throws CertificateException {
 845         if (!(val instanceof CertificateAlgorithmId)) {
 846             throw new CertificateException(
 847                              "AlgorithmId class type invalid.");
 848         }
 849         algId = (CertificateAlgorithmId)val;
 850     }
 851 
 852     /**
 853      * Set the issuer name of the certificate.
 854      *
 855      * @params val the Object class value for the issuer
 856      * @exception CertificateException on invalid data.
 857      */
 858     private void setIssuer(Object val) throws CertificateException {
 859         if (!(val instanceof X500Name)) {
 860             throw new CertificateException(
 861                              "Issuer class type invalid.");
 862         }
 863         issuer = (X500Name)val;
 864     }
 865 
 866     /**
 867      * Set the validity interval of the certificate.
 868      *
 869      * @params val the Object class value for the CertificateValidity
 870      * @exception CertificateException on invalid data.
 871      */
 872     private void setValidity(Object val) throws CertificateException {
 873         if (!(val instanceof CertificateValidity)) {
 874             throw new CertificateException(
 875                              "CertificateValidity class type invalid.");
 876         }
 877         interval = (CertificateValidity)val;
 878     }
 879 
 880     /**
 881      * Set the subject name of the certificate.
 882      *
 883      * @params val the Object class value for the Subject
 884      * @exception CertificateException on invalid data.
 885      */
 886     private void setSubject(Object val) throws CertificateException {
 887         if (!(val instanceof X500Name)) {
 888             throw new CertificateException(
 889                              "Subject class type invalid.");
 890         }
 891         subject = (X500Name)val;
 892     }
 893 
 894     /**
 895      * Set the public key in the certificate.
 896      *
 897      * @params val the Object class value for the PublicKey
 898      * @exception CertificateException on invalid data.
 899      */
 900     private void setKey(Object val) throws CertificateException {
 901         if (!(val instanceof CertificateX509Key)) {
 902             throw new CertificateException(
 903                              "Key class type invalid.");
 904         }
 905         pubKey = (CertificateX509Key)val;
 906     }
 907 
 908     /**
 909      * Set the Issuer Unique Identity in the certificate.
 910      *
 911      * @params val the Object class value for the IssuerUniqueId
 912      * @exception CertificateException
 913      */
 914     private void setIssuerUniqueId(Object val) throws CertificateException {
 915         if (version.compare(CertificateVersion.V2) < 0) {
 916             throw new CertificateException("Invalid version");
 917         }
 918         if (!(val instanceof UniqueIdentity)) {
 919             throw new CertificateException(
 920                              "IssuerUniqueId class type invalid.");
 921         }
 922         issuerUniqueId = (UniqueIdentity)val;
 923     }
 924 
 925     /**
 926      * Set the Subject Unique Identity in the certificate.
 927      *
 928      * @params val the Object class value for the SubjectUniqueId
 929      * @exception CertificateException
 930      */
 931     private void setSubjectUniqueId(Object val) throws CertificateException {
 932         if (version.compare(CertificateVersion.V2) < 0) {
 933             throw new CertificateException("Invalid version");
 934         }
 935         if (!(val instanceof UniqueIdentity)) {
 936             throw new CertificateException(
 937                              "SubjectUniqueId class type invalid.");
 938         }
 939         subjectUniqueId = (UniqueIdentity)val;
 940     }
 941 
 942     /**
 943      * Set the extensions in the certificate.
 944      *
 945      * @params val the Object class value for the Extensions
 946      * @exception CertificateException
 947      */
 948     private void setExtensions(Object val) throws CertificateException {
 949         if (version.compare(CertificateVersion.V3) < 0) {
 950             throw new CertificateException("Invalid version");
 951         }
 952         if (!(val instanceof CertificateExtensions)) {
 953           throw new CertificateException(
 954                              "Extensions class type invalid.");
 955         }
 956         extensions = (CertificateExtensions)val;
 957     }
 958 }