1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18/**
19* @author Alexander Y. Kleymenov
20* @version $Revision$
21*/
22
23package org.apache.harmony.security.x509;
24
25import java.security.KeyFactory;
26import java.security.NoSuchAlgorithmException;
27import java.security.PublicKey;
28import java.security.spec.InvalidKeySpecException;
29import java.security.spec.KeySpec;
30import java.security.spec.X509EncodedKeySpec;
31import org.apache.harmony.security.asn1.ASN1BitString;
32import org.apache.harmony.security.asn1.ASN1Sequence;
33import org.apache.harmony.security.asn1.ASN1Type;
34import org.apache.harmony.security.asn1.BerInputStream;
35import org.apache.harmony.security.asn1.BitString;
36
37/**
38 * The class encapsulates the ASN.1 DER encoding/decoding work
39 * with the following structure which is a part of X.509 certificate
40 * (as specified in RFC 3280 -
41 *  Internet X.509 Public Key Infrastructure.
42 *  Certificate and Certificate Revocation List (CRL) Profile.
43 *  http://www.ietf.org/rfc/rfc3280.txt):
44 *
45 * <pre>
46 *  SubjectPublicKeyInfo  ::=  SEQUENCE  {
47 *      algorithm            AlgorithmIdentifier,
48 *      subjectPublicKey     BIT STRING
49 *  }
50 * </pre>
51 */
52public final class SubjectPublicKeyInfo {
53    /** the value of algorithmID field of the structure */
54    private AlgorithmIdentifier algorithmID;
55    /** the value of subjectPublicKey field of the structure */
56    private byte[] subjectPublicKey;
57    /** the public key corresponding to this SubjectPublicKeyInfo */
58    private PublicKey publicKey;
59    /** the value of unusedBits field of the structure */
60    private int unusedBits;
61    /** the ASN.1 encoded form of SubjectPublicKeyInfo */
62    private byte[] encoding;
63
64    public SubjectPublicKeyInfo(AlgorithmIdentifier algID, byte[] subjectPublicKey) {
65        this(algID, subjectPublicKey, 0);
66    }
67
68    public SubjectPublicKeyInfo(AlgorithmIdentifier algID, byte[] subjectPublicKey, int unused) {
69        this(algID, subjectPublicKey, 0, null);
70    }
71
72    private SubjectPublicKeyInfo(AlgorithmIdentifier algID,
73                                 byte[] subjectPublicKey, int unused,
74                                 byte[] encoding) {
75        this.algorithmID = algID;
76        this.subjectPublicKey = subjectPublicKey;
77        this.unusedBits = unused;
78        this.encoding = encoding;
79    }
80
81    /**
82     * Returns the value of algorithmIdentifier field of the structure.
83     */
84    public AlgorithmIdentifier getAlgorithmIdentifier() {
85        return algorithmID;
86    }
87
88    /**
89     * Returns the value of subjectPublicKey field of the structure.
90     */
91    public byte[] getSubjectPublicKey() {
92        return subjectPublicKey;
93    }
94
95    /**
96     * Returns ASN.1 encoded form of this X.509 SubjectPublicKeyInfo value.
97     */
98    public byte[] getEncoded() {
99        if (encoding == null) {
100            encoding = ASN1.encode(this);
101        }
102        return encoding;
103    }
104
105    /**
106     * Returns the PublicKey corresponding to this SubjectPublicKeyInfo
107     * instance.
108     */
109    public PublicKey getPublicKey() {
110        if (publicKey == null) {
111            final byte[] encoded = getEncoded();
112            final KeySpec keySpec = new X509EncodedKeySpec(encoded);
113
114            /* Try using the algorithm name first. */
115            final String algName = algorithmID.getAlgorithmName();
116            publicKey = generateKeyForAlgorithm(keySpec, algName);
117
118            /*
119             * Fall back to using the algorithm OID if it's not the same as the
120             * algorithm name.
121             */
122            final String algOid = algorithmID.getAlgorithm();
123            if (publicKey == null && !algOid.equals(algName)) {
124                publicKey = generateKeyForAlgorithm(keySpec, algOid);
125            }
126
127            /*
128             * Encode this as an X.509 public key since we didn't have any
129             * KeyFactory that could handle this algorithm name or OID. Perhaps
130             * the thing that's using this can decode it.
131             */
132            if (publicKey == null) {
133                publicKey = new X509PublicKey(algOid, encoded, subjectPublicKey);
134            }
135        }
136        return publicKey;
137    }
138
139    /**
140     * Try to generate a PublicKey for a given {@code keySpec} and
141     * {@code algorithm} identifier. If there a problem generating the key like
142     * a missing {@code KeyFactory} or invalid {@code KeySpec}, it will return
143     * {@code null}.
144     */
145    private static PublicKey generateKeyForAlgorithm(KeySpec keySpec, String algorithm) {
146        try {
147            return KeyFactory.getInstance(algorithm).generatePublic(keySpec);
148        } catch (InvalidKeySpecException ignored) {
149        } catch (NoSuchAlgorithmException ignored) {
150        }
151        return null;
152    }
153
154    public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] {
155            AlgorithmIdentifier.ASN1, ASN1BitString.getInstance() }) {
156        @Override protected Object getDecodedObject(BerInputStream in) {
157            Object[] values = (Object[]) in.content;
158            return new SubjectPublicKeyInfo(
159                    (AlgorithmIdentifier) values[0],
160                    ((BitString) values[1]).bytes,
161                    ((BitString) values[1]).unusedBits,
162                    in.getEncoded());
163        }
164
165        @Override protected void getValues(Object object, Object[] values) {
166            SubjectPublicKeyInfo spki = (SubjectPublicKeyInfo) object;
167            values[0] = spki.algorithmID;
168            values[1] = new BitString(spki.subjectPublicKey, spki.unusedBits);
169        }
170    };
171}
172
173