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