Android Java renewal certificate and secret keys in Android KeyStore

I have a system that uses HTTPS client certificates for authentication, but the certificates themselves are generated according to the following process:

  • The client device generates a certificate (including public and private keys)
  • The client device sends the public key to the server, which signs the public key and returns it as a signed certificate
  • The client stores the certificate in a secure manner and then uses it as an HTTPS client certificate

We have this system running on iOS, and I'm trying to connect to Android, but have run into a lot of problems with poorly documented and confusing Android APIs.

My code looks something like this:

Certificate Creation

keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
keyStore.load(null);

Date startDate = new Date();
Date endDate = new Date(startDate.getTime() + FORTY_YEARS_IN_MILLISECONDS);

KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
        .setAlias(alias)
        .setKeySize(2048)
        .setKeyType(KeyProperties.KEY_ALGORITHM_RSA)
        .setSubject(new X500Principal("CN=" + alias))
        .setSerialNumber(BigInteger.TEN)
        .setStartDate(startDate)
        .setEndDate(endDate)
        .build();

KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEYSTORE);
generator.initialize(spec);
KeyPair keyPair = generator.generateKeyPair(); // this will put a certificate and key pair in the keyStore.
dumpKeyStore(keyStore);

byte[] entireKey = keyPair.getPublic().getEncoded();
// chop off first 24 bytes; the java key pair generator puts an object ID of  1.2.840.113549.1.1.1 RSA (RSA_SIGN) before the key which gets mangled when the server signs and sends back the certificate
byte[] publicKeyBytes = Arrays.copyOfRange(entireKey, 24, entireKey.length);

dumpKeyStore - , , keyStore.getEntry, . , KeyStore.PrivateKeyEntry. , PrivateKeyEntry.

publicKeyBytes , x509, . , . , .

keyStore , () . :

KeyStore keyStore;
try {
    keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
    keyStore.load(null);
}catch (IOException | NoSuchAlgorithmException | CertificateException e) {
    Log.wtf(TAG, e);
    throw new FatalError(TAG, e);
}

CertificateFactory certificateFactory;
try {
    certificateFactory = CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
    Log.wtf(TAG, e);
    throw new FatalError(TAG, e);
}

Certificate cert = certificateFactory.generateCertificate(new ByteArrayInputStream(certificateFromServer));

// find the existing certificate, copy it private key out, then replace the certificate with the one from the server but keeping the private key
try {
    KeyStore.PrivateKeyEntry existingPrivateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);

    KeyStore.PrivateKeyEntry newEntry = new KeyStore.PrivateKeyEntry(existingPrivateKeyEntry.getPrivateKey(), new Certificate[]{ cert });
    keyStore.setEntry(alias, newEntry, null);
} catch (Exception e) {
    Log.wtf(TAG, e);
    throw new FatalError(TAG, e);
}
dumpKeyStore(keyStore);

dumpKeyStore , , "NoSuchAlgorithmException: Unknown key entry", keyStore.getEntry

, ( , ) android? , ? , .

+4
4

, . KeyStore, KeyManager, SSLContext, HttpsURLConnection, KeyManager , .

KeyStore.

  • / KeyPairGenerator X
  • ,
  • KeyStore setCertificateEntry X-Signed

HttpsURLConnection, :

KeyStore androidKeyStore = KeyStore.getInstance(LocalKeyStore.ANDROID_KEYSTORE);
androidKeyStore.load(null);

X509Certificate signedClientCertificate = (X509Certificate)androidKeyStore.getCertificate("X-Signed");
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)androidKeyStore.getEntry("X", null);

X509ExtendedKeyManager keyManager = new X509ExtendedKeyManager() {
    @Override
    public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
        return clientCertificateAlias;
    }
    @Override
    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
        return null; // different if you're validating the server cert
    }
    @Override
    public X509Certificate[] getCertificateChain(String alias) {
        return new X509Certificate[] { signedClientCertificate };
    }
    @Override
    public String[] getClientAliases(String keyType, Principal[] issuers) {
        return new String[]{ "X" };
    }

    @Override
    public String[] getServerAliases(String keyType, Principal[] issuers) {
        return null; // different if you're validating server cert
    }
    @Override
    public PrivateKey getPrivateKey(String alias) {
        if(alias != clientCertificateAlias) {
            Log.e(TAG, String.format("X509ExtendedKeyManager is asking for privateKey with unknown alias %s. Expecting it to ask for %s", alias, clientCertificateAlias));
            return null;
        }
        return privateKeyEntry.getPrivateKey();
    }
};

X509TrustManager trustServerCertificates = new X509TrustManager() {
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        // do nothing, this method doesn't get called
    }
    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) 
        // code to validate server cert in here
    }
    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return null; // any issuer
    }
};

m_sslContext = SSLContext.getInstance("TLS");
m_sslContext.init(new KeyManager[]{ keyManager }, new TrustManager[] { trustServerCertificates }, null);

// later on

conn = (HttpURLConnection)url.openConnection();
SSLContext sslContext = m_sslContext;

if(conn instanceof HttpsURLConnection && sslContext != null) {
    ((HttpsURLConnection)conn).setSSLSocketFactory(sslContext.getSocketFactory());
}

, AndroidKeyStore

+5

AndroidKeyStore. generator.generateKeyPair()

, OpenSSL Android . , , , KeyStore.PrivateKeyEntry, , : https://code.google.com/p/android/issues/detail?id=194955&q=keystore&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars

"BKS" Bouncy Castle key store, .

Update: KeyManager SSL, . Android , :

PrivateKey , X.509, . , .

+2

, , , null (protoParam) KeyStore.setEntry:

    KeyStore.PrivateKeyEntry newEntry = new KeyStore.PrivateKeyEntry(existingPrivateKeyEntry.getPrivateKey(), new Certificate[]{ cert });
    keyStore.setEntry(alias, newEntry, null);

, :

    KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)androidKeyStore.getEntry("X", null);

, KeyGenParameterSpec.Builder . , - , , AndroidKeyStore, TLS.

0

hey, I use your code for mutual TLS authentication, android studio always said

    Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x74a884a088: Failure in SSL library, usually a protocol error
    2019-05-09 10:34:07.460 16885-17098/com.example.myfirstloginapplication W/System.err: error:04000044:RSA routines:OPENSSL_internal:internal error (external/conscrypt/common/src/jni/main/cpp/conscrypt/native_crypto.cc:698 0x74a1936e07:0x00000000)

you know why?

0
source

Source: https://habr.com/ru/post/1616303/


All Articles