• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.keychain.tests;
18 
19 import static com.android.keychain.KeyChainActivity.CertificateParametersFilter;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.junit.Assert.fail;
24 
25 import android.util.Base64;
26 
27 import androidx.test.filters.LargeTest;
28 import androidx.test.runner.AndroidJUnit4;
29 
30 import org.junit.After;
31 import org.junit.Before;
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 
35 import java.io.ByteArrayInputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.security.KeyFactory;
39 import java.security.KeyStore;
40 import java.security.KeyStoreException;
41 import java.security.NoSuchAlgorithmException;
42 import java.security.PrivateKey;
43 import java.security.cert.Certificate;
44 import java.security.cert.CertificateException;
45 import java.security.cert.CertificateFactory;
46 import java.security.cert.X509Certificate;
47 import java.security.spec.InvalidKeySpecException;
48 import java.security.spec.PKCS8EncodedKeySpec;
49 import java.util.ArrayList;
50 import java.util.Collections;
51 import java.util.concurrent.CancellationException;
52 
53 import javax.security.auth.x500.X500Principal;
54 
55 @LargeTest
56 @RunWith(AndroidJUnit4.class)
57 public final class KeyChainActivityTest {
58     // Generated with:
59     // openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 3650
60     //   -out root_ca_certificate.pem
61     private static final String ROOT_CA_CERT_RSA =
62             "MIIDazCCAlOgAwIBAgIUKNVj8g/LG+6tqsK8HZqT6EuktpkwDQYJKoZIhvcNAQEL" +
63             "BQAwRTELMAkGA1UEBhMCVUsxCzAJBgNVBAgMAk5BMQ8wDQYDVQQHDAZMb25kb24x" +
64             "GDAWBgNVBAoMD0FuZHJvaWQgVGVzdCBDQTAeFw0yMTAyMjMwMDU0MzdaFw0zMTAy" +
65             "MjEwMDU0MzdaMEUxCzAJBgNVBAYTAlVLMQswCQYDVQQIDAJOQTEPMA0GA1UEBwwG" +
66             "TG9uZG9uMRgwFgYDVQQKDA9BbmRyb2lkIFRlc3QgQ0EwggEiMA0GCSqGSIb3DQEB" +
67             "AQUAA4IBDwAwggEKAoIBAQDLpcaoJijYhS3QUDgG8kVGrwTxaVTS0TE156fJa5za" +
68             "s3RI7TYKHzYwn1KMJJUwoc+cOkv+rFC7j5MQ+6SMK2GpDoCoGn4FV9dDPVnxIgjj" +
69             "/66kuf+we1ur3gz7m/8tFFdZhLFoFMRzcNg+F35jSur8y0dnc8O83gMwuf+91pU7" +
70             "HyNahHzDyMM5sR7u1K91R1MKiOnVqJNTHWVK+rl3G0m0rbDTz7/xbq3/FPBvw764" +
71             "QUJgkSEG15i5CFNn2ww5IYnF30Wbke3kUfdd/Q32MOyfkcp3El/TPJj/3mevGHed" +
72             "vTi0j6ovIXMrDnoJvmeI40p97EMIlaZ3x39i+krE02RfAgMBAAGjUzBRMB0GA1Ud" +
73             "DgQWBBRDpNR2iik8NGDmY5IvgiUy6dMRMjAfBgNVHSMEGDAWgBRDpNR2iik8NGDm" +
74             "Y5IvgiUy6dMRMjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQDG" +
75             "OIZdoopwX2PlkAnWMR6PIl4GJ9eiPg1cbQHHcqsB8oNYiiiZydJ8TniZYFptEIjQ" +
76             "LIuerSr/a+c353JNOOK76w0vciJld8uQDCTnMChUahJsQjJJgDCyhjkQqV9Srstu" +
77             "IClm5IP/LEYVSzfR+LqhwX5JYUU8zsDnbiAH3CIENhBw5ApXgfDHP54bHOBw5eHE" +
78             "ETATnib8uPZVHsyHQXXULDqMITY9pmjc9/9x6CqcMGEiSVvhDSujerVlnw3I17Te" +
79             "HWT8bf8mJyQFR8kr59NPQMNN4oUIfFzj2VgzjL21mKGR+hBGqoIMJ1ZdPGCJsw0W" +
80             "5WYM4TtQtL7yNVDtJzOL";
81 
82     // Generated with:
83     // openssl genrsa  -out intermediate_key.pem 2048
84     // openssl req -new -key intermediate_key.pem -out intermediate_ca.csr
85     // openssl x509 -req -days 3650 -in intermediate_ca.csr -CA root_ca_certificate.pem
86     //   -CAkey key.pem -CAcreateserial -out intermediate_ca.pem
87     private static final String INTERMEDIATE_CA_CERT_RSA =
88             "MIIDHjCCAgYCFHgZBbZMuJTvvm1wlBBoPE7peS4jMA0GCSqGSIb3DQEBCwUAMEUx" +
89             "CzAJBgNVBAYTAlVLMQswCQYDVQQIDAJOQTEPMA0GA1UEBwwGTG9uZG9uMRgwFgYD" +
90             "VQQKDA9BbmRyb2lkIFRlc3QgQ0EwHhcNMjEwMjIzMDA1ODQ5WhcNMzEwMjIxMDA1" +
91             "ODQ5WjBSMQswCQYDVQQGEwJVSzELMAkGA1UECAwCTkExDzANBgNVBAcMBkxvbmRv" +
92             "bjElMCMGA1UECgwcQW5kcm9pZCBJbnRlcm1lZGlhdGUgVGVzdCBDQTCCASIwDQYJ" +
93             "KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZ5tLvS8qiZGK8fBUPzZYWRG2kmf3Hi" +
94             "o6db8qjTvzkJ0l43zmI92v0pEy8a4/XlC7wGSaJuX/TqpAR43+a9kYqGWczkASpX" +
95             "7w1W5tjjDlLANZdRP2R7Wb3XWTTMxzDWOqsMGTHw+H92oFm4bGO9+PaBRRzifLmQ" +
96             "OI/Whw6/kHZlXyI7J68EwRbyaZXNJ6iqk8dF7B4iwZ9yNgW1H0m8uxdDAykEA2WX" +
97             "wbzITwD1zdMsQV7/eLe9RIMFN5VMeHtIFUi2AcioG0i4ZLZNFnVrFQKu7u3XQyxk" +
98             "u7ZzgeGtpluM71sDxnqhZv9NaZIq3mV3JrKHPsw6+uJjN4U5AVfzIYUCAwEAATAN" +
99             "BgkqhkiG9w0BAQsFAAOCAQEAXrRQUFlLyS3QlmwkGocLQISY9B8fF8LTH7sl6HFA" +
100             "VSVuhPDuNsmqVhsMH1981MY2rSVfM3fkUMz1WEH7ZbhooYPirax/AlW+oRdRB/xX" +
101             "WEAJRGgybK98PXogI4tqEvicVn2kfcyNzmfMn8yRClxD5GuZ0oOA50lpUwUmeJjo" +
102             "jb3DY8NF+bcA0lW5h7p86ezqjhB836XZRL47jZJj+jgKoiSsdcex7rzikW6bzdlV" +
103             "f9DCuBJMpM1y31AP15Gvg5Jhh9Wc4y8LLipTn7wGdJvvhclUe1U3roerhAEB+rU/" +
104             "Eu7h+Nqjogg3IzmfHlhDe4N8o/XLdc2FnhCZGZklDDDMvA==";
105 
106     // Generated with:
107     // openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr
108     // openssl x509 -req -days 3650 -in server.csr -CA intermediate_ca.pem
109     //   -CAkey intermediate_key.pem -CAcreateserial -out server.pem
110     private static final String LEAF_SERVER_CERT_RSA =
111             "MIIDIjCCAgoCFDV+Rg1WxBy4ThLxvu1lRJl1YUzwMA0GCSqGSIb3DQEBCwUAMFIx" +
112             "CzAJBgNVBAYTAlVLMQswCQYDVQQIDAJOQTEPMA0GA1UEBwwGTG9uZG9uMSUwIwYD" +
113             "VQQKDBxBbmRyb2lkIEludGVybWVkaWF0ZSBUZXN0IENBMB4XDTIxMDIyMzAxMDEw" +
114             "OFoXDTMxMDIyMTAxMDEwOFowSTELMAkGA1UEBhMCVUsxCzAJBgNVBAgMAk5BMQ8w" +
115             "DQYDVQQHDAZMb25kb24xHDAaBgNVBAoME0FuZHJvaWQgU2VydmVyIFRlc3QwggEi" +
116             "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7267U6iCagBJFiYMMUkteQliO" +
117             "ljOfnuSZJTG1xxNXBggLDdHmchjOPQgEICINpxz7Hhg+PLME3DEqrwUHo9k/bR3e" +
118             "Tglt1o6qxGirIEKROtdzNyi3medRex6FATXq1g4W/1U0tl8EbMv9kJPZP52Uj0Rq" +
119             "XYZ9Y27IGtWDcudpyJij/nBbV/kfufti2pFNHhnXytyBrQXz6AziZjHnNt316z64" +
120             "Tfqchr0jxqIF3Sup9AVKnGooGymwT8ez5C6VO7WoRZnp40pH78GzSALnRJC9w26V" +
121             "1IVlqjYPWvFwJ0ENb0Nuc8Jr3tW/0gf1UzTsuRsMU9vl+tMjweXS8HI8M4uRAgMB" +
122             "AAEwDQYJKoZIhvcNAQELBQADggEBAIcOLAdu9HU6+Bk/SkQW2qW2mY5+WFSYYRG5" +
123             "KfDiMQr0kUeddBJYk+bLN3Qqi6KUAZ+ITxyarVrjZjxaTr1JV6m9fVxdZ8elAx+7" +
124             "ci9ghBkiUPKFtVz3Y1F31Em4tLRr0LHF49Mjvr62+mQuZlAXZ3TuMdxrwc9AePhN" +
125             "btY8YwUsPPJ2vrIQB14NJ6EIaMaIyTowBPX9eo5K47ISbfnCUKhFYAEK4v5s4Hkt" +
126             "Us/209E0FNdFKOZDKDwclhZSFbyA1tknBRXsP7QCFuj/hPFxcRaj9R8CoHQAqEFr" +
127             "mV8JscA9dV40m+kRkj5TZeHjIw2xI39btv5aeRW2fBNB0MEoNPQ=";
128 
129     private static final String PRIVATE_SERVER_KEY =
130             "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC7267U6iCagBJF" +
131             "iYMMUkteQliOljOfnuSZJTG1xxNXBggLDdHmchjOPQgEICINpxz7Hhg+PLME3DEq" +
132             "rwUHo9k/bR3eTglt1o6qxGirIEKROtdzNyi3medRex6FATXq1g4W/1U0tl8EbMv9" +
133             "kJPZP52Uj0RqXYZ9Y27IGtWDcudpyJij/nBbV/kfufti2pFNHhnXytyBrQXz6Azi" +
134             "ZjHnNt316z64Tfqchr0jxqIF3Sup9AVKnGooGymwT8ez5C6VO7WoRZnp40pH78Gz" +
135             "SALnRJC9w26V1IVlqjYPWvFwJ0ENb0Nuc8Jr3tW/0gf1UzTsuRsMU9vl+tMjweXS" +
136             "8HI8M4uRAgMBAAECggEARWNUhYJhPpAVr6emRxPSkONysGAce1YGW+bYIKuCoj8x" +
137             "E1wsbrEwJmV2o4d27JIQa1TnYX2sJhxq8Lgq5HKJ2Rql0KoEY5S/p6Xaf3LwA5K3" +
138             "Z/A00vQ+8+LFGB2lW7NrCuWPBGRkXk8NXgBcC/+qZegxPhSDi6cBkVoQCXiUr4Zs" +
139             "4wacrmpQbl/FekvpuWQxnVAm95knZhJ87r7izwO6e3VP1VZseG7ld6PbxkLnuTP7" +
140             "Y9Q+viAPwkk1SedvYy6RtIRbyyOKzTVbh9SXirPsLM6N+a8k3J7LY+8HnZhte0O4" +
141             "BFoZwwhXt/kPHilro6gt69Bh2bas+lpP62c8e7Q3HQKBgQDwY6XJzLXV1UQNbwLL" +
142             "FHTDBdf3Afe6W5jcKpsOUaNP/424JeLpc5a2ccaytCPs2p3RCTZ3SHaxe+LNrKbU" +
143             "iKdloGO/XTqcAHbvw/uy5Zzsv6XqUjREmCvCatF7UU7PH9Pztjhb91IXT7taB1Ee" +
144             "yYyPPJU9ioZDEV45/PIUpGwjnwKBgQDIDrtHx5fBH0od8XWSBzuQ3QjLl3WZcEAv" +
145             "Bf2GcdYUV7migWOXm28k51l2VbQQRCVOulJjf45TZgZgzKInZZJ+rZ69FLrOx7q3" +
146             "7X+8rTyWKa2u/IlZ/EBtNIRuqH7A+OuY5qroFngtYa/qsaX58/eUAe64I6HslenG" +
147             "ktvmwdSCzwKBgAtQ1YQLU9/t+xcay6ndm6V2h/UDrbKjDy4F/2iMJUDlybkKZ4UP" +
148             "wN9zuaO94RcML3OgmGTDD3tJVqLR5sSIbkDVbPycGd8wEmk084s3TczDNL80AWvd" +
149             "Meoj9xpz+F69o8+MG1kQ6ldYlHwnbgUh/bDcbDYKaEmN7r6SDp80IjcHAoGAOrqQ" +
150             "Yf8G3qu3z1h94jN7Wgh5N4MsA7I/NU614Uzzwp8KINmJCg2YMCY2ThXUuV238gei" +
151             "fhEJEBSIVMxd4eDgg42mZu159ZAOkUYIVLQqcA6mLRN3otH5e9WJ9w5Bv5aTWxyE" +
152             "GYPXHcNqqCQkjF8BVBLJKIdVVqWfriqYoYJPR2MCgYAWWIrS8XN1c2iLParb+xOJ" +
153             "q1WKe8q5wFucqJCVFbJXjtpGgZFxZLAFlT8VpaiBwDLQBPC+CYhzLVwAvzW2h46W" +
154             "KONqO7zoxiuwOEj466iH4YrgviNw6lGtgSPB6wx91c7se/1lcZhviBMB5rUjtxxj" +
155             "qKIC9Y+gz77w1M3pwMlhDA==";
156 
157     private static final X500Principal LEAF_SUBJECT =
158             new X500Principal("O=Android Server Test, L=London, ST=NA, C=UK");
159 
160     private static final X500Principal INTERMEDIATE_SUBJECT =
161             new X500Principal("O=Android Intermediate Test CA, L=London, ST=NA, C=UK");
162 
163     private static final X500Principal ROOT_SUBJECT =
164             new X500Principal("O=Android Test CA, L=London, ST=NA, C=UK");
165 
166     private byte[] mPrivateKey;
167     private byte[] mLeafRsaCertificate;
168     private byte[] mIntermediateRsaCertificate;
169     private byte[] mRootRsaCertificate;
170 
171     @Before
setUp()172     public void setUp() {
173         mPrivateKey = Base64.decode(PRIVATE_SERVER_KEY, Base64.DEFAULT);
174         mLeafRsaCertificate = Base64.decode(LEAF_SERVER_CERT_RSA, Base64.DEFAULT);
175         mIntermediateRsaCertificate = Base64.decode(INTERMEDIATE_CA_CERT_RSA, Base64.DEFAULT);
176         mRootRsaCertificate = Base64.decode(ROOT_CA_CERT_RSA, Base64.DEFAULT);
177     }
178 
179     @After
tearDown()180     public void tearDown() {
181         KeyStore keyStore = null;
182         try {
183             keyStore = KeyStore.getInstance("AndroidKeyStore");
184             keyStore.load(null);
185             keyStore.deleteEntry("testCertificateParametersFilter_client");
186         } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException
187                 | IOException e) {
188             throw new RuntimeException(e);
189         }
190     }
191 
192     @Test
testCertificateParametersFilter_filtersByIntermediateIssuer()193     public void testCertificateParametersFilter_filtersByIntermediateIssuer()
194             throws CancellationException, IOException, CertificateException,
195             KeyStoreException, NoSuchAlgorithmException {
196         KeyStore keyStore = prepareKeyStoreWithLongChainCertificates();
197 
198         assertThat(createCheckerForIssuer(keyStore, ROOT_SUBJECT)
199                 .shouldPresentCertificate("testCertificateParametersFilter_client")).isTrue();
200 
201         assertThat(createCheckerForIssuer(keyStore, INTERMEDIATE_SUBJECT)
202                 .shouldPresentCertificate("testCertificateParametersFilter_client")).isTrue();
203 
204         assertThat(createCheckerForIssuer(keyStore, LEAF_SUBJECT)
205                 .shouldPresentCertificate("testCertificateParametersFilter_client")).isFalse();
206     }
207 
208     // Return a KeyStore instance that has both a client certificate as well as a certificate
209     // chain associated with it.
prepareKeyStoreWithLongChainCertificates()210     private KeyStore prepareKeyStoreWithLongChainCertificates()
211             throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
212 
213         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
214         keyStore.load(null);
215 
216         Certificate[] certs = new Certificate[3];
217         certs[0] = parseCertificate(mLeafRsaCertificate);
218         certs[1] = parseCertificate(mIntermediateRsaCertificate);
219         certs[2] = parseCertificate(mRootRsaCertificate);
220 
221         keyStore.setKeyEntry("testCertificateParametersFilter_client", parseKey(mPrivateKey),
222                 null, certs);
223 
224         return keyStore;
225     }
226 
227     // Create a CertificateParametersFilter instance that has the specified issuer as a requested
228     // issuer.
createCheckerForIssuer( KeyStore keyStore, X500Principal issuer)229     private static CertificateParametersFilter createCheckerForIssuer(
230             KeyStore keyStore, X500Principal issuer) {
231         return new CertificateParametersFilter(
232                 keyStore, new String[] {},
233                 new ArrayList<byte[]>(Collections.singletonList(issuer.getEncoded())));
234     }
235 
parseCertificate(byte[] certificateBytes)236     private static X509Certificate parseCertificate(byte[] certificateBytes) {
237         InputStream in = new ByteArrayInputStream(certificateBytes);
238         try {
239             CertificateFactory cf = CertificateFactory.getInstance("X.509");
240             return (X509Certificate)cf.generateCertificate(in);
241         } catch (CertificateException e) {
242             fail(String.format("Could not parse certificate: %s", e));
243             return null;
244         }
245     }
246 
parseKey(byte[] key)247     private static PrivateKey parseKey(byte[] key) {
248         try {
249             KeyFactory kf = KeyFactory.getInstance("RSA");
250             return kf.generatePrivate(new PKCS8EncodedKeySpec(key));
251         } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
252             fail(String.format("Could not parse private key: %s", e));
253             return null;
254         }
255     }
256 }
257