View Javadoc
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 }