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
18package java.security.cert;
19
20import java.io.IOException;
21import java.security.PublicKey;
22import javax.security.auth.x500.X500Principal;
23import org.apache.harmony.security.utils.Array;
24import org.apache.harmony.security.x509.NameConstraints;
25
26
27
28/**
29 * This class represents a trust anchor for validation of X.509 certification
30 * path.
31 * <p>
32 * It is a <i>trusted</i> certificate authority (CA) and includes the public key
33 * of the CA, the CA's name and the constraints for the validation of
34 * certification paths. The constructor also allows to specify a binary
35 * representation of a so called "Name Constraints" extension as a byte array.
36 */
37public class TrustAnchor {
38    // Most trusted CA as a X500Principal
39    private final X500Principal caPrincipal;
40    // Most trusted CA name
41    private final String caName;
42    // Most trusted CA public key
43    private final PublicKey caPublicKey;
44    // Most trusted CA certificate
45    private final X509Certificate trustedCert;
46    // Name constraints extension
47    private final byte[] nameConstraints;
48
49    /**
50     * Creates a new {@code TrustAnchor} with the specified certificate and name
51     * constraints.
52     * <p>
53     * The name constraints will be used as additional constraints during the
54     * validation of certification paths.
55     *
56     * @param trustedCert
57     *            the trusted certificate
58     * @param nameConstraints
59     *            the ASN.1 DER encoded form of the name constraints or {@code
60     *            null} if none.
61     * @throws IllegalArgumentException
62     *             if the decoding of the name constraints fail.
63     */
64    public TrustAnchor(X509Certificate trustedCert, byte[] nameConstraints) {
65        if (trustedCert == null) {
66            throw new NullPointerException("trustedCert == null");
67        }
68        this.trustedCert = trustedCert;
69        // copy nameConstraints if not null
70        if (nameConstraints != null) {
71            this.nameConstraints = new byte[nameConstraints.length];
72            System.arraycopy(nameConstraints, 0,
73                    this.nameConstraints, 0, this.nameConstraints.length);
74            processNameConstraints();
75        } else {
76            this.nameConstraints = null;
77        }
78        this.caName = null;
79        this.caPrincipal = null;
80        this.caPublicKey = null;
81    }
82
83    /**
84     * Creates a new {@code TrustAnchor} with the specified certificate
85     * authority name, its public key and the specified name constraints.
86     * <p>
87     * The name constraints will be used as additional constraints during the
88     * validation of certification paths.
89     *
90     * @param caName
91     *            the X.500 name of the certificate authority in RFC 2253
92     *            {@code String} format.
93     * @param caPublicKey
94     *            the public key of the certificate authority
95     * @param nameConstraints
96     *            the ASN.1 DER encoded form of the name constraints or {@code
97     *            null} if none.
98     * @throws IllegalArgumentException
99     *             if the {@code caName} is empty or if decoding of the name
100     *             constraints fail.
101     */
102    public TrustAnchor(String caName, PublicKey caPublicKey,
103            byte[] nameConstraints) {
104        if (caName == null) {
105            throw new NullPointerException("caName == null");
106        }
107        this.caName = caName;
108        if (caPublicKey == null) {
109            throw new NullPointerException("caPublicKey == null");
110        }
111        this.caPublicKey = caPublicKey;
112        // copy nameConstraints if not null
113        if (nameConstraints != null) {
114            this.nameConstraints = new byte[nameConstraints.length];
115            System.arraycopy(nameConstraints, 0,
116                    this.nameConstraints, 0, this.nameConstraints.length);
117            processNameConstraints();
118        } else {
119            this.nameConstraints = null;
120        }
121
122        this.trustedCert = null;
123
124        // X500Principal checks caName validity
125        if (caName.isEmpty()) {
126            throw new IllegalArgumentException("caName.isEmpty()");
127        }
128        this.caPrincipal = new X500Principal(this.caName);
129    }
130
131    /**
132     * Creates a new {@code TrustAnchor} with the specified certificate
133     * authority name as principal, its public key and the specified name
134     * constraints.
135     * <p>
136     * The name constraints will be used as additional constraints during the
137     * validation of certification paths.
138     *
139     * @param caPrincipal
140     *            the name of the certificate authority as X500 principal.
141     * @param caPublicKey
142     *            the public key of the certificate authority.
143     * @param nameConstraints
144     *            the ASN.1 DER encoded form of the name constraints or {@code
145     *            null} if none.
146     * @throws IllegalArgumentException
147     *             if decoding of the name constraints fail.
148     */
149    public TrustAnchor(X500Principal caPrincipal,
150            PublicKey caPublicKey, byte[] nameConstraints) {
151        if (caPrincipal == null) {
152            throw new NullPointerException("caPrincipal == null");
153        }
154        this.caPrincipal = caPrincipal;
155        if (caPublicKey == null) {
156            throw new NullPointerException("caPublicKey == null");
157        }
158        this.caPublicKey = caPublicKey;
159        // copy nameConstraints if not null
160        if (nameConstraints != null) {
161            this.nameConstraints = new byte[nameConstraints.length];
162            System.arraycopy(nameConstraints, 0,
163                    this.nameConstraints, 0, this.nameConstraints.length);
164            processNameConstraints();
165        } else {
166            this.nameConstraints = null;
167        }
168
169        this.trustedCert = null;
170        this.caName = caPrincipal.getName();
171    }
172
173    /**
174     * Returns a copy of the name constraints in ASN.1 DER encoded form.
175     *
176     * @return a copy of the name constraints in ASN.1 DER encoded form.
177     */
178    public final byte[] getNameConstraints() {
179        if (nameConstraints == null) {
180            return null;
181        }
182        byte[] ret = new byte[nameConstraints.length];
183            System.arraycopy(nameConstraints, 0,
184                    ret, 0, nameConstraints.length);
185        return ret;
186    }
187
188    /**
189     * Returns the certificate of this <i>trusted</i> certificate authority.
190     *
191     * @return the certificate of this CA or {@code null}, if the trust anchor
192     *         of this instance was not created with a certificate.
193     */
194    public final X509Certificate getTrustedCert() {
195        return trustedCert;
196    }
197
198    /**
199     * Returns the name of the certificate authority as {@code X500Principal}.
200     *
201     * @return the name of the certificate authority or {@code null} if the
202     *         trust anchor of this instance was not created with a {@code
203     *         X500Principal}.
204     */
205    public final X500Principal getCA() {
206        return caPrincipal;
207    }
208
209    /**
210     * Returns the name of the certificate authority as {@code String} in RFC
211     * 2253 format.
212     *
213     * @return the name of the certificate authority as {@code String} in RFC
214     *         2253 format or {@code null} if the trust anchor of this instance
215     *         was not created with a CA name.
216     */
217    public final String getCAName() {
218        return caName;
219    }
220
221    /**
222     * Returns the public key of the certificate authority.
223     *
224     * @return the public key of the certificate authority or {@code null} if
225     *         the trust anchor if this instance was not created with a public
226     *         key.
227     */
228    public final PublicKey getCAPublicKey() {
229        return caPublicKey;
230    }
231
232    /**
233     * Returns a string representation of this {@code TrustAnchor} instance.
234     *
235     * @return a string representation of this {@code TrustAnchor} instance.
236     */
237    public String toString() {
238        StringBuilder sb = new StringBuilder("TrustAnchor: [\n");
239        if (trustedCert != null) {
240            sb.append("Trusted CA certificate: ");
241            sb.append(trustedCert);
242            sb.append("\n");
243        }
244        if (caPrincipal != null) {
245            sb.append("Trusted CA Name: ");
246            sb.append(caPrincipal);
247            sb.append("\n");
248        }
249        if (caPublicKey != null) {
250            sb.append("Trusted CA Public Key: ");
251            sb.append(caPublicKey);
252            sb.append("\n");
253        }
254        // FIXME if needed:
255        if (nameConstraints != null) {
256            sb.append("Name Constraints:\n");
257            sb.append(Array.toString(nameConstraints, "    "));
258        }
259        sb.append("\n]");
260        return sb.toString();
261    }
262
263    //
264    // Private stuff
265    //
266
267    // Decodes and checks NameConstraints structure.
268    // Throws IllegalArgumentException if NameConstraints
269    // encoding is invalid.
270    private void processNameConstraints() {
271        try {
272            // decode and check nameConstraints
273            NameConstraints.ASN1.decode(nameConstraints);
274        } catch (IOException e) {
275            throw new IllegalArgumentException(e.getMessage());
276        }
277    }
278}
279