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