1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.net.ipsec.ike.message;
18 
19 import android.annotation.IntDef;
20 import android.annotation.Nullable;
21 import android.net.ipsec.ike.exceptions.IkeProtocolException;
22 
23 import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException;
24 
25 import java.io.IOException;
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 import java.nio.ByteBuffer;
29 import java.security.KeyStore;
30 import java.security.KeyStoreException;
31 import java.security.NoSuchAlgorithmException;
32 import java.security.ProviderException;
33 import java.security.cert.CertificateException;
34 import java.security.cert.TrustAnchor;
35 import java.security.cert.X509CRL;
36 import java.security.cert.X509Certificate;
37 import java.util.List;
38 import java.util.Set;
39 
40 import javax.net.ssl.TrustManager;
41 import javax.net.ssl.TrustManagerFactory;
42 import javax.net.ssl.X509TrustManager;
43 
44 /**
45  * IkeCertPayload is an abstract class that represents the common information for all Certificate
46  * Payload carrying different types of certifciate-related data and static methods related to
47  * certificate validation.
48  *
49  * <p>Certificate Payload is only sent in IKE_AUTH exchange.
50  *
51  * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.6">RFC 7296, Internet Key Exchange
52  *     Protocol Version 2 (IKEv2)</a>
53  */
54 public abstract class IkeCertPayload extends IkePayload {
55     // Length of certificate encoding type field in octets.
56     protected static final int CERT_ENCODING_LEN = 1;
57 
58     private static final String KEY_STORE_TYPE_PKCS12 = "PKCS12";
59     private static final String CERT_PATH_ALGO_PKIX = "PKIX";
60     private static final String CERT_AUTH_TYPE_RSA = "RSA";
61 
62     @Retention(RetentionPolicy.SOURCE)
63     @IntDef({
64         CERTIFICATE_ENCODING_X509_CERT_SIGNATURE,
65         CERTIFICATE_ENCODING_CRL,
66         CERTIFICATE_ENCODING_X509_CERT_HASH_URL,
67     })
68     public @interface CertificateEncoding {}
69 
70     public static final int CERTIFICATE_ENCODING_X509_CERT_SIGNATURE = 4;
71     public static final int CERTIFICATE_ENCODING_CRL = 7;
72     public static final int CERTIFICATE_ENCODING_X509_CERT_HASH_URL = 12;
73 
74     @CertificateEncoding public final int certEncodingType;
75 
IkeCertPayload(@ertificateEncoding int encodingType)76     protected IkeCertPayload(@CertificateEncoding int encodingType) {
77         this(false /*critical*/, encodingType);
78     }
79 
IkeCertPayload(boolean critical, @CertificateEncoding int encodingType)80     protected IkeCertPayload(boolean critical, @CertificateEncoding int encodingType) {
81         super(PAYLOAD_TYPE_CERT, critical);
82         certEncodingType = encodingType;
83     }
84 
getIkeCertPayload(boolean critical, byte[] payloadBody)85     protected static IkeCertPayload getIkeCertPayload(boolean critical, byte[] payloadBody)
86             throws IkeProtocolException {
87         ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody);
88 
89         int certEncodingType = Byte.toUnsignedInt(inputBuffer.get());
90         byte[] certData = new byte[payloadBody.length - CERT_ENCODING_LEN];
91         inputBuffer.get(certData);
92         switch (certEncodingType) {
93             case CERTIFICATE_ENCODING_X509_CERT_SIGNATURE:
94                 return new IkeCertX509CertPayload(critical, certData);
95                 // TODO: Support decoding CRL and "Hash and URL".
96             case CERTIFICATE_ENCODING_CRL:
97                 throw new AuthenticationFailedException(
98                         "CERTIFICATE_ENCODING_CRL decoding is unsupported.");
99             case CERTIFICATE_ENCODING_X509_CERT_HASH_URL:
100                 throw new AuthenticationFailedException(
101                         "CERTIFICATE_ENCODING_X509_CERT_HASH_URL decoding is unsupported");
102             default:
103                 throw new AuthenticationFailedException("Unrecognized certificate encoding type.");
104         }
105     }
106 
107     /**
108      * Validate an end certificate against the received chain and trust anchors.
109      *
110      * <p>Validation is done by checking if there is a valid certificate path from end certificate
111      * to provided trust anchors.
112      *
113      * <p>TrustManager implementation used in this method MUST conforms RFC 4158 and RFC 5280. As
114      * indicated in RFC 4158, Key Identifiers(KIDs) are not required to match during certification
115      * path validation and cannot be used to eliminate certificates.
116      *
117      * <p>Validation will fail if any certficate in the certificate chain is using RSA public key
118      * whose RSA modulus is smaller than 1024 bits.
119      *
120      * @param endCert the end certificate that will be used to verify AUTH payload
121      * @param certList all the received certificates (include the end certificate)
122      * @param crlList the certificate revocation lists
123      * @param trustAnchorSet the certificate authority set to validate the end certificate
124      * @throws AuthenticationFailedException if there is no valid certificate path
125      */
validateCertificates( X509Certificate endCert, List<X509Certificate> certList, @Nullable List<X509CRL> crlList, Set<TrustAnchor> trustAnchorSet)126     public static void validateCertificates(
127             X509Certificate endCert,
128             List<X509Certificate> certList,
129             @Nullable List<X509CRL> crlList,
130             Set<TrustAnchor> trustAnchorSet)
131             throws AuthenticationFailedException {
132         try {
133             // TODO: b/122676944 Support CRL checking
134 
135             // By default, use system-trusted CAs
136             KeyStore keyStore = null;
137 
138             // But if a specific trust anchor is specified, use that instead
139             if (trustAnchorSet != null && !trustAnchorSet.isEmpty()) {
140                 keyStore = KeyStore.getInstance(KEY_STORE_TYPE_PKCS12);
141                 keyStore.load(null);
142                 for (TrustAnchor t : trustAnchorSet) {
143                     X509Certificate trustedCert = t.getTrustedCert();
144                     String alias =
145                             trustedCert.getSubjectX500Principal().getName()
146                                     + trustedCert.hashCode();
147                     keyStore.setCertificateEntry(alias, trustedCert);
148                 }
149             }
150 
151             // Build X509TrustManager with all keystore
152             TrustManagerFactory tmFactory =
153                     TrustManagerFactory.getInstance(
154                             CERT_PATH_ALGO_PKIX, IkeMessage.getTrustManagerProvider());
155             tmFactory.init(keyStore);
156 
157             X509TrustManager trustManager = null;
158             for (TrustManager tm : tmFactory.getTrustManagers()) {
159                 if (tm instanceof X509TrustManager) {
160                     trustManager = (X509TrustManager) tm;
161                 }
162             }
163             if (trustManager == null) {
164                 throw new ProviderException(
165                         "X509TrustManager is not supported by "
166                                 + IkeMessage.getTrustManagerProvider().getName());
167             }
168 
169             // Build and validate certificate path
170             trustManager.checkServerTrusted(
171                     certList.toArray(new X509Certificate[certList.size()]), CERT_AUTH_TYPE_RSA);
172         } catch (NoSuchAlgorithmException e) {
173             throw new ProviderException("Algorithm is not supported by the provider", e);
174         } catch (IOException | KeyStoreException e) {
175             throw new IllegalStateException(e);
176         } catch (CertificateException e) {
177             throw new AuthenticationFailedException(e);
178         }
179     }
180 }
181