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 org.conscrypt;
19
20import java.io.IOException;
21import java.security.cert.CertificateEncodingException;
22import java.security.cert.CertificateException;
23import java.security.cert.CertificateFactory;
24import java.security.cert.X509Certificate;
25import java.util.ArrayList;
26
27/**
28 * Represents server/client certificate message
29 * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS
30 * 1.0 spec., 7.4.2. Server certificate; 7.4.6. Client certificate</a>
31 *
32 */
33public class CertificateMessage extends Message {
34
35    /**
36     * Certificates
37     */
38    X509Certificate[] certs;
39
40    /**
41     * Certificates in encoded form
42     */
43    byte[][] encoded_certs;
44
45    /**
46     * Creates inbound message
47     *
48     * @param in
49     * @param length
50     * @throws IOException
51     */
52    public CertificateMessage(HandshakeIODataStream in, int length) throws IOException {
53        int l = in.readUint24(); // total_length
54        if (l == 0) {  // message contais no certificates
55            if (length != 3) { // no more bytes after total_length
56                fatalAlert(AlertProtocol.DECODE_ERROR,
57                        "DECODE ERROR: incorrect CertificateMessage");
58            }
59            certs = new X509Certificate[0];
60            encoded_certs = new byte[0][0];
61            this.length = 3;
62            return;
63        }
64        CertificateFactory cf;
65        try {
66            cf = CertificateFactory.getInstance("X509");
67        } catch (CertificateException e) {
68            fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
69            return;
70        }
71        ArrayList<X509Certificate> certsList = new ArrayList<X509Certificate>();
72        int size = 0;
73        int enc_size = 0;
74        while (l > 0) {
75            size = in.readUint24();
76            l -= 3;
77            try {
78                certsList.add((X509Certificate) cf.generateCertificate(in));
79            } catch (CertificateException e) {
80                fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR", e);
81            }
82            l -= size;
83            enc_size += size;
84        }
85        certs = certsList.toArray(new X509Certificate[certsList.size()]);
86        this.length = 3 + 3 * certs.length + enc_size;
87        if (this.length != length) {
88            fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect CertificateMessage");
89        }
90    }
91
92    /**
93     * Creates outbound message
94     *
95     * @param certs
96     */
97    public CertificateMessage(X509Certificate[] certs) {
98        if (certs == null) {
99            this.certs = new X509Certificate[0];
100            encoded_certs = new byte[0][0];
101            length = 3;
102            return;
103        }
104        this.certs = certs;
105        if (encoded_certs == null) {
106            encoded_certs = new byte[certs.length][];
107            for (int i = 0; i < certs.length; i++) {
108                try {
109                    encoded_certs[i] = certs[i].getEncoded();
110                } catch (CertificateEncodingException e) {
111                    fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR",
112                            e);
113                }
114            }
115        }
116        length = 3 + 3 * encoded_certs.length;
117        for (int i = 0; i < encoded_certs.length; i++) {
118            length += encoded_certs[i].length;
119        }
120    }
121
122    /**
123     * Sends message
124     *
125     * @param out
126     */
127    @Override
128    public void send(HandshakeIODataStream out) {
129
130        int total_length = 0;
131        if (encoded_certs == null) {
132            encoded_certs = new byte[certs.length][];
133            for (int i = 0; i < certs.length; i++) {
134                try {
135                    encoded_certs[i] = certs[i].getEncoded();
136                } catch (CertificateEncodingException e) {
137                    fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR",
138                            e);
139                }
140            }
141        }
142        total_length = 3 * encoded_certs.length;
143        for (int i = 0; i < encoded_certs.length; i++) {
144            total_length += encoded_certs[i].length;
145        }
146        out.writeUint24(total_length);
147        for (int i = 0; i < encoded_certs.length; i++) {
148            out.writeUint24(encoded_certs[i].length);
149            out.write(encoded_certs[i]);
150        }
151
152    }
153
154    public String getAuthType() {
155        return certs[0].getPublicKey().getAlgorithm();
156    }
157
158    /**
159     * Returns message type
160     */
161    @Override
162    public int getType() {
163        return Handshake.CERTIFICATE;
164    }
165
166}
167