1/*
2 * Copyright (c) 1997, 2009, 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
26package sun.security.x509;
27
28import java.io.IOException;
29import java.io.OutputStream;
30import java.util.Enumeration;
31
32import sun.security.util.*;
33
34/**
35 * This class represents the Authority Key Identifier Extension.
36 *
37 * <p>The authority key identifier extension provides a means of
38 * identifying the particular public key used to sign a certificate.
39 * This extension would be used where an issuer has multiple signing
40 * keys (either due to multiple concurrent key pairs or due to
41 * changeover).
42 * <p>
43 * The ASN.1 syntax for this is:
44 * <pre>
45 * AuthorityKeyIdentifier ::= SEQUENCE {
46 *    keyIdentifier             [0] KeyIdentifier           OPTIONAL,
47 *    authorityCertIssuer       [1] GeneralNames            OPTIONAL,
48 *    authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL
49 * }
50 * KeyIdentifier ::= OCTET STRING
51 * </pre>
52 * @author Amit Kapoor
53 * @author Hemma Prafullchandra
54 * @see Extension
55 * @see CertAttrSet
56 */
57public class AuthorityKeyIdentifierExtension extends Extension
58implements CertAttrSet<String> {
59    /**
60     * Identifier for this attribute, to be used with the
61     * get, set, delete methods of Certificate, x509 type.
62     */
63    public static final String IDENT =
64                         "x509.info.extensions.AuthorityKeyIdentifier";
65    /**
66     * Attribute names.
67     */
68    public static final String NAME = "AuthorityKeyIdentifier";
69    public static final String KEY_ID = "key_id";
70    public static final String AUTH_NAME = "auth_name";
71    public static final String SERIAL_NUMBER = "serial_number";
72
73    // Private data members
74    private static final byte TAG_ID = 0;
75    private static final byte TAG_NAMES = 1;
76    private static final byte TAG_SERIAL_NUM = 2;
77
78    private KeyIdentifier       id = null;
79    private GeneralNames        names = null;
80    private SerialNumber        serialNum = null;
81
82    // Encode only the extension value
83    private void encodeThis() throws IOException {
84        if (id == null && names == null && serialNum == null) {
85            this.extensionValue = null;
86            return;
87        }
88        DerOutputStream seq = new DerOutputStream();
89        DerOutputStream tmp = new DerOutputStream();
90        if (id != null) {
91            DerOutputStream tmp1 = new DerOutputStream();
92            id.encode(tmp1);
93            tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
94                              false, TAG_ID), tmp1);
95        }
96        try {
97            if (names != null) {
98                DerOutputStream tmp1 = new DerOutputStream();
99                names.encode(tmp1);
100                tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
101                                  true, TAG_NAMES), tmp1);
102            }
103        } catch (Exception e) {
104            throw new IOException(e.toString());
105        }
106        if (serialNum != null) {
107            DerOutputStream tmp1 = new DerOutputStream();
108            serialNum.encode(tmp1);
109            tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
110                              false, TAG_SERIAL_NUM), tmp1);
111        }
112        seq.write(DerValue.tag_Sequence, tmp);
113        this.extensionValue = seq.toByteArray();
114    }
115
116    /**
117     * The default constructor for this extension.  Null parameters make
118     * the element optional (not present).
119     *
120     * @param id the KeyIdentifier associated with this extension.
121     * @param names the GeneralNames associated with this extension
122     * @param serialNum the CertificateSerialNumber associated with
123     *         this extension.
124     * @exception IOException on error.
125     */
126    public AuthorityKeyIdentifierExtension(KeyIdentifier kid, GeneralNames name,
127                                           SerialNumber sn)
128    throws IOException {
129        this.id = kid;
130        this.names = name;
131        this.serialNum = sn;
132
133        this.extensionId = PKIXExtensions.AuthorityKey_Id;
134        this.critical = false;
135        encodeThis();
136    }
137
138    /**
139     * Create the extension from the passed DER encoded value of the same.
140     *
141     * @param critical true if the extension is to be treated as critical.
142     * @param value an array of DER encoded bytes of the actual value.
143     * @exception ClassCastException if value is not an array of bytes
144     * @exception IOException on error.
145     */
146    public AuthorityKeyIdentifierExtension(Boolean critical, Object value)
147    throws IOException {
148        this.extensionId = PKIXExtensions.AuthorityKey_Id;
149        this.critical = critical.booleanValue();
150
151        this.extensionValue = (byte[]) value;
152        DerValue val = new DerValue(this.extensionValue);
153        if (val.tag != DerValue.tag_Sequence) {
154            throw new IOException("Invalid encoding for " +
155                                  "AuthorityKeyIdentifierExtension.");
156        }
157
158        // Note that all the fields in AuthorityKeyIdentifier are defined as
159        // being OPTIONAL, i.e., there could be an empty SEQUENCE, resulting
160        // in val.data being null.
161        while ((val.data != null) && (val.data.available() != 0)) {
162            DerValue opt = val.data.getDerValue();
163
164            // NB. this is always encoded with the IMPLICIT tag
165            // The checks only make sense if we assume implicit tagging,
166            // with explicit tagging the form is always constructed.
167            if (opt.isContextSpecific(TAG_ID) && !opt.isConstructed()) {
168                if (id != null)
169                    throw new IOException("Duplicate KeyIdentifier in " +
170                                          "AuthorityKeyIdentifier.");
171                opt.resetTag(DerValue.tag_OctetString);
172                id = new KeyIdentifier(opt);
173
174            } else if (opt.isContextSpecific(TAG_NAMES) &&
175                       opt.isConstructed()) {
176                if (names != null)
177                    throw new IOException("Duplicate GeneralNames in " +
178                                          "AuthorityKeyIdentifier.");
179                opt.resetTag(DerValue.tag_Sequence);
180                names = new GeneralNames(opt);
181
182            } else if (opt.isContextSpecific(TAG_SERIAL_NUM) &&
183                       !opt.isConstructed()) {
184                if (serialNum != null)
185                    throw new IOException("Duplicate SerialNumber in " +
186                                          "AuthorityKeyIdentifier.");
187                opt.resetTag(DerValue.tag_Integer);
188                serialNum = new SerialNumber(opt);
189            } else
190                throw new IOException("Invalid encoding of " +
191                                      "AuthorityKeyIdentifierExtension.");
192        }
193    }
194
195    /**
196     * Return the object as a string.
197     */
198    public String toString() {
199        String s = super.toString() + "AuthorityKeyIdentifier [\n";
200        if (id != null) {
201            s += id.toString();     // id already has a newline
202        }
203        if (names != null) {
204            s += names.toString() + "\n";
205        }
206        if (serialNum != null) {
207            s += serialNum.toString() + "\n";
208        }
209        return (s + "]\n");
210    }
211
212    /**
213     * Write the extension to the OutputStream.
214     *
215     * @param out the OutputStream to write the extension to.
216     * @exception IOException on error.
217     */
218    public void encode(OutputStream out) throws IOException {
219        DerOutputStream tmp = new DerOutputStream();
220        if (this.extensionValue == null) {
221            extensionId = PKIXExtensions.AuthorityKey_Id;
222            critical = false;
223            encodeThis();
224        }
225        super.encode(tmp);
226        out.write(tmp.toByteArray());
227    }
228
229    /**
230     * Set the attribute value.
231     */
232    public void set(String name, Object obj) throws IOException {
233        if (name.equalsIgnoreCase(KEY_ID)) {
234            if (!(obj instanceof KeyIdentifier)) {
235              throw new IOException("Attribute value should be of " +
236                                    "type KeyIdentifier.");
237            }
238            id = (KeyIdentifier)obj;
239        } else if (name.equalsIgnoreCase(AUTH_NAME)) {
240            if (!(obj instanceof GeneralNames)) {
241              throw new IOException("Attribute value should be of " +
242                                    "type GeneralNames.");
243            }
244            names = (GeneralNames)obj;
245        } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) {
246            if (!(obj instanceof SerialNumber)) {
247              throw new IOException("Attribute value should be of " +
248                                    "type SerialNumber.");
249            }
250            serialNum = (SerialNumber)obj;
251        } else {
252          throw new IOException("Attribute name not recognized by " +
253                        "CertAttrSet:AuthorityKeyIdentifier.");
254        }
255        encodeThis();
256    }
257
258    /**
259     * Get the attribute value.
260     */
261    public Object get(String name) throws IOException {
262        if (name.equalsIgnoreCase(KEY_ID)) {
263            return (id);
264        } else if (name.equalsIgnoreCase(AUTH_NAME)) {
265            return (names);
266        } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) {
267            return (serialNum);
268        } else {
269          throw new IOException("Attribute name not recognized by " +
270                        "CertAttrSet:AuthorityKeyIdentifier.");
271        }
272    }
273
274    /**
275     * Delete the attribute value.
276     */
277    public void delete(String name) throws IOException {
278        if (name.equalsIgnoreCase(KEY_ID)) {
279            id = null;
280        } else if (name.equalsIgnoreCase(AUTH_NAME)) {
281            names = null;
282        } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) {
283            serialNum = null;
284        } else {
285          throw new IOException("Attribute name not recognized by " +
286                        "CertAttrSet:AuthorityKeyIdentifier.");
287        }
288        encodeThis();
289    }
290
291    /**
292     * Return an enumeration of names of attributes existing within this
293     * attribute.
294     */
295    public Enumeration<String> getElements() {
296        AttributeNameEnumeration elements = new AttributeNameEnumeration();
297        elements.addElement(KEY_ID);
298        elements.addElement(AUTH_NAME);
299        elements.addElement(SERIAL_NUMBER);
300
301        return (elements.elements());
302    }
303
304    /**
305     * Return the name of this attribute.
306     */
307    public String getName() {
308        return (NAME);
309    }
310
311    /**
312     * Return the encoded key identifier, or null if not specified.
313     */
314    public byte[] getEncodedKeyIdentifier() throws IOException {
315        if (id != null) {
316            DerOutputStream derOut = new DerOutputStream();
317            id.encode(derOut);
318            return derOut.toByteArray();
319        }
320        return null;
321    }
322}
323