1616
1717package android .security ;
1818
19+ import com .android .org .bouncycastle .x509 .X509V3CertificateGenerator ;
20+
21+ import org .apache .harmony .xnet .provider .jsse .OpenSSLEngine ;
22+
1923import android .test .AndroidTestCase ;
2024
2125import java .io .ByteArrayInputStream ;
2226import java .io .ByteArrayOutputStream ;
23- import java .io .IOException ;
2427import java .io .OutputStream ;
28+ import java .math .BigInteger ;
29+ import java .security .InvalidKeyException ;
2530import java .security .Key ;
2631import java .security .KeyFactory ;
2732import java .security .KeyStore .Entry ;
3035import java .security .KeyStoreException ;
3136import java .security .NoSuchAlgorithmException ;
3237import java .security .PrivateKey ;
38+ import java .security .PublicKey ;
3339import java .security .cert .Certificate ;
34- import java .security .cert .CertificateException ;
3540import java .security .cert .CertificateFactory ;
41+ import java .security .cert .X509Certificate ;
3642import java .security .interfaces .RSAPrivateKey ;
3743import java .security .spec .InvalidKeySpecException ;
3844import java .security .spec .PKCS8EncodedKeySpec ;
45+ import java .security .spec .X509EncodedKeySpec ;
3946import java .util .Arrays ;
4047import java .util .Collection ;
4148import java .util .Date ;
4451import java .util .Iterator ;
4552import java .util .Set ;
4653
54+ import javax .security .auth .x500 .X500Principal ;
55+
4756public class AndroidKeyStoreTest extends AndroidTestCase {
4857 private android .security .KeyStore mAndroidKeyStore ;
4958
@@ -55,6 +64,22 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
5564
5665 private static final String TEST_ALIAS_3 = "test3" ;
5766
67+ private static final X500Principal TEST_DN_1 = new X500Principal ("CN=test1" );
68+
69+ private static final X500Principal TEST_DN_2 = new X500Principal ("CN=test2" );
70+
71+ private static final BigInteger TEST_SERIAL_1 = BigInteger .ONE ;
72+
73+ private static final BigInteger TEST_SERIAL_2 = BigInteger .valueOf (2L );
74+
75+ private static final long NOW_MILLIS = System .currentTimeMillis ();
76+
77+ /* We have to round this off because X509v3 doesn't store milliseconds. */
78+ private static final Date NOW = new Date (NOW_MILLIS - (NOW_MILLIS % 1000L ));
79+
80+ @ SuppressWarnings ("deprecation" )
81+ private static final Date NOW_PLUS_10_YEARS = new Date (NOW .getYear () + 10 , 0 , 1 );
82+
5883 /*
5984 * The keys and certificates below are generated with:
6085 *
@@ -759,31 +784,41 @@ public void testKeyStore_GetEntry_NullParams_Success() throws Exception {
759784 assertPrivateKeyEntryEquals (keyEntry , FAKE_KEY_1 , FAKE_USER_1 , FAKE_CA_1 );
760785 }
761786
787+ @ SuppressWarnings ("unchecked" )
762788 private void assertPrivateKeyEntryEquals (PrivateKeyEntry keyEntry , byte [] key , byte [] cert ,
763789 byte [] ca ) throws Exception {
764790 KeyFactory keyFact = KeyFactory .getInstance ("RSA" );
765791 PrivateKey expectedKey = keyFact .generatePrivate (new PKCS8EncodedKeySpec (key ));
766792
767- assertEquals ("Returned PrivateKey should be what we inserted" , expectedKey ,
768- keyEntry .getPrivateKey ());
769-
770793 CertificateFactory certFact = CertificateFactory .getInstance ("X.509" );
771794 Certificate expectedCert = certFact .generateCertificate (new ByteArrayInputStream (cert ));
772795
796+ final Collection <Certificate > expectedChain ;
797+ if (ca != null ) {
798+ expectedChain = (Collection <Certificate >) certFact
799+ .generateCertificates (new ByteArrayInputStream (ca ));
800+ } else {
801+ expectedChain = null ;
802+ }
803+
804+ assertPrivateKeyEntryEquals (keyEntry , expectedKey , expectedCert , expectedChain );
805+ }
806+
807+ private void assertPrivateKeyEntryEquals (PrivateKeyEntry keyEntry , PrivateKey expectedKey ,
808+ Certificate expectedCert , Collection <Certificate > expectedChain ) throws Exception {
809+ assertEquals ("Returned PrivateKey should be what we inserted" , expectedKey ,
810+ keyEntry .getPrivateKey ());
811+
773812 assertEquals ("Returned Certificate should be what we inserted" , expectedCert ,
774813 keyEntry .getCertificate ());
775814
776815 Certificate [] actualChain = keyEntry .getCertificateChain ();
777816
778817 assertEquals ("First certificate in chain should be user cert" , expectedCert , actualChain [0 ]);
779818
780- if (ca == null ) {
819+ if (expectedChain == null ) {
781820 assertEquals ("Certificate chain should not include CAs" , 1 , actualChain .length );
782821 } else {
783- @ SuppressWarnings ("unchecked" )
784- Collection <Certificate > expectedChain = (Collection <Certificate >) certFact
785- .generateCertificates (new ByteArrayInputStream (ca ));
786-
787822 int i = 1 ;
788823 final Iterator <Certificate > it = expectedChain .iterator ();
789824 while (it .hasNext ()) {
@@ -1306,6 +1341,142 @@ public void testKeyStore_SetKeyEntry_Replaced_Success() throws Exception {
13061341 }
13071342 }
13081343
1344+ @ SuppressWarnings ("deprecation" )
1345+ private static X509Certificate generateCertificate (android .security .KeyStore keyStore ,
1346+ String alias , BigInteger serialNumber , X500Principal subjectDN , Date notBefore ,
1347+ Date notAfter ) throws Exception {
1348+ final String privateKeyAlias = Credentials .USER_PRIVATE_KEY + alias ;
1349+
1350+ final PrivateKey privKey ;
1351+ final OpenSSLEngine engine = OpenSSLEngine .getInstance ("keystore" );
1352+ try {
1353+ privKey = engine .getPrivateKeyById (privateKeyAlias );
1354+ } catch (InvalidKeyException e ) {
1355+ throw new RuntimeException ("Can't get key" , e );
1356+ }
1357+
1358+ final byte [] pubKeyBytes = keyStore .getPubkey (privateKeyAlias );
1359+
1360+ final PublicKey pubKey ;
1361+ try {
1362+ final KeyFactory keyFact = KeyFactory .getInstance ("RSA" );
1363+ pubKey = keyFact .generatePublic (new X509EncodedKeySpec (pubKeyBytes ));
1364+ } catch (NoSuchAlgorithmException e ) {
1365+ throw new IllegalStateException ("Can't instantiate RSA key generator" , e );
1366+ } catch (InvalidKeySpecException e ) {
1367+ throw new IllegalStateException ("keystore returned invalid key encoding" , e );
1368+ }
1369+
1370+ final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator ();
1371+ certGen .setPublicKey (pubKey );
1372+ certGen .setSerialNumber (serialNumber );
1373+ certGen .setSubjectDN (subjectDN );
1374+ certGen .setIssuerDN (subjectDN );
1375+ certGen .setNotBefore (notBefore );
1376+ certGen .setNotAfter (notAfter );
1377+ certGen .setSignatureAlgorithm ("sha1WithRSA" );
1378+
1379+ final X509Certificate cert = certGen .generate (privKey );
1380+
1381+ return cert ;
1382+ }
1383+
1384+ public void testKeyStore_SetKeyEntry_ReplacedChain_Success () throws Exception {
1385+ mKeyStore .load (null , null );
1386+
1387+ // Create key #1
1388+ {
1389+ final String privateKeyAlias = Credentials .USER_PRIVATE_KEY + TEST_ALIAS_1 ;
1390+ assertTrue (mAndroidKeyStore .generate (privateKeyAlias ));
1391+
1392+ Key key = mKeyStore .getKey (TEST_ALIAS_1 , null );
1393+
1394+ assertTrue (key instanceof PrivateKey );
1395+
1396+ PrivateKey expectedKey = (PrivateKey ) key ;
1397+
1398+ X509Certificate expectedCert = generateCertificate (mAndroidKeyStore , TEST_ALIAS_1 ,
1399+ TEST_SERIAL_1 , TEST_DN_1 , NOW , NOW_PLUS_10_YEARS );
1400+
1401+ assertTrue (mAndroidKeyStore .put (Credentials .USER_CERTIFICATE + TEST_ALIAS_1 ,
1402+ expectedCert .getEncoded ()));
1403+
1404+ Entry entry = mKeyStore .getEntry (TEST_ALIAS_1 , null );
1405+
1406+ assertTrue (entry instanceof PrivateKeyEntry );
1407+
1408+ PrivateKeyEntry keyEntry = (PrivateKeyEntry ) entry ;
1409+
1410+ assertPrivateKeyEntryEquals (keyEntry , expectedKey , expectedCert , null );
1411+ }
1412+
1413+ // Replace key #1 with new chain
1414+ {
1415+ Key key = mKeyStore .getKey (TEST_ALIAS_1 , null );
1416+
1417+ assertTrue (key instanceof PrivateKey );
1418+
1419+ PrivateKey expectedKey = (PrivateKey ) key ;
1420+
1421+ X509Certificate expectedCert = generateCertificate (mAndroidKeyStore , TEST_ALIAS_1 ,
1422+ TEST_SERIAL_2 , TEST_DN_2 , NOW , NOW_PLUS_10_YEARS );
1423+
1424+ mKeyStore .setKeyEntry (TEST_ALIAS_1 , expectedKey , null ,
1425+ new Certificate [] { expectedCert });
1426+
1427+ Entry entry = mKeyStore .getEntry (TEST_ALIAS_1 , null );
1428+
1429+ assertTrue (entry instanceof PrivateKeyEntry );
1430+
1431+ PrivateKeyEntry keyEntry = (PrivateKeyEntry ) entry ;
1432+
1433+ assertPrivateKeyEntryEquals (keyEntry , expectedKey , expectedCert , null );
1434+ }
1435+ }
1436+
1437+ public void testKeyStore_SetKeyEntry_ReplacedChain_DifferentPrivateKey_Failure ()
1438+ throws Exception {
1439+ mKeyStore .load (null , null );
1440+
1441+ // Create key #1
1442+ {
1443+ final String privateKeyAlias = Credentials .USER_PRIVATE_KEY + TEST_ALIAS_1 ;
1444+ assertTrue (mAndroidKeyStore .generate (privateKeyAlias ));
1445+
1446+ X509Certificate cert = generateCertificate (mAndroidKeyStore , TEST_ALIAS_1 ,
1447+ TEST_SERIAL_1 , TEST_DN_1 , NOW , NOW_PLUS_10_YEARS );
1448+
1449+ assertTrue (mAndroidKeyStore .put (Credentials .USER_CERTIFICATE + TEST_ALIAS_1 ,
1450+ cert .getEncoded ()));
1451+ }
1452+
1453+ // Create key #2
1454+ {
1455+ final String privateKeyAlias = Credentials .USER_PRIVATE_KEY + TEST_ALIAS_2 ;
1456+ assertTrue (mAndroidKeyStore .generate (privateKeyAlias ));
1457+
1458+ X509Certificate cert = generateCertificate (mAndroidKeyStore , TEST_ALIAS_2 ,
1459+ TEST_SERIAL_2 , TEST_DN_2 , NOW , NOW_PLUS_10_YEARS );
1460+
1461+ assertTrue (mAndroidKeyStore .put (Credentials .USER_CERTIFICATE + TEST_ALIAS_2 ,
1462+ cert .getEncoded ()));
1463+ }
1464+
1465+ // Replace key #1 with key #2
1466+ {
1467+ Key key1 = mKeyStore .getKey (TEST_ALIAS_2 , null );
1468+
1469+ X509Certificate cert = generateCertificate (mAndroidKeyStore , TEST_ALIAS_2 ,
1470+ TEST_SERIAL_2 , TEST_DN_2 , NOW , NOW_PLUS_10_YEARS );
1471+
1472+ try {
1473+ mKeyStore .setKeyEntry (TEST_ALIAS_1 , key1 , null , new Certificate [] { cert });
1474+ fail ("Should not allow setting of KeyEntry with wrong PrivaetKey" );
1475+ } catch (KeyStoreException success ) {
1476+ }
1477+ }
1478+ }
1479+
13091480 public void testKeyStore_Size_Success () throws Exception {
13101481 mKeyStore .load (null , null );
13111482
0 commit comments