org.deviceconnect.android.ssl.AbstractKeyStoreManager.java Source code

Java tutorial

Introduction

Here is the source code for org.deviceconnect.android.ssl.AbstractKeyStoreManager.java

Source

/*
 AbstractKeyStoreManager.java
 Copyright (c) 2018 NTT DOCOMO,INC.
 Released under the MIT license
 http://opensource.org/licenses/mit-license.php
 */
package org.deviceconnect.android.ssl;

import android.content.Context;

import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.deviceconnect.android.BuildConfig;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.security.auth.x500.X500Principal;

/**
 * ??.
 *
 * ??.
 *
 * @author NTT DOCOMO, INC.
 */
abstract class AbstractKeyStoreManager implements KeyStoreManager {

    /**
     * ??.
     */
    private static final String KEYSTORE_TYPE = "PKCS12";

    /**
     * ?.
     */
    private static final char[] KEYSTORE_PASSWORD = "0000".toCharArray();

    /**
     * .
     */
    final Context mContext;

    /**
     * .
     */
    final KeyStore mKeyStore;

    /**
     * ???.
     */
    private final String mKeyStoreFilePath;

    /**
     * .
     */
    private final Logger mLogger = Logger.getLogger("LocalCA");

    /**
     * .
     *
     * @param context 
     * @param keyStorePath ????
     */
    AbstractKeyStoreManager(final Context context, final String keyStorePath) {
        mContext = context;
        mKeyStoreFilePath = keyStorePath;
        try {
            mKeyStore = createKeyStore();
        } catch (GeneralSecurityException e) {
            // NOTE: PKCS12 ?API Level 1 ?????. ?????????.
            throw new IllegalStateException(KEYSTORE_TYPE + " is not supported.", e);
        }
        boolean isSavedKeyStore = isSavedKeyStore();

        if (BuildConfig.DEBUG) {
            mLogger.info("isSavedKeyStore: " + isSavedKeyStore);
        }

        if (isSavedKeyStore) {
            try {
                loadKeyStore();

                if (BuildConfig.DEBUG) {
                    mLogger.info("Loaded keystore: path = " + mKeyStoreFilePath);
                }
            } catch (Exception e) {
                mLogger.log(Level.SEVERE, "Failed to load keystore: path = " + mKeyStoreFilePath, e);
            }
        }
    }

    @Override
    public Certificate getCertificate(final String alias) {
        try {
            return mKeyStore.getCertificate(alias);
        } catch (KeyStoreException e) {
            return null;
        }
    }

    @Override
    public void exportKeyStore(final File outputFile) throws IOException {
        OutputStream out = null;
        try {
            out = new FileOutputStream(outputFile);
            saveKeyStore(out);
        } catch (GeneralSecurityException e) {
            throw new IOException("Failed to export keystore.", e);
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

    private KeyStore createKeyStore() throws GeneralSecurityException {
        KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
        try {
            keyStore.load(null, KEYSTORE_PASSWORD);
        } catch (IOException e) {
            throw new GeneralSecurityException("Unable to create empty keyStore", e);
        }
        return keyStore;
    }

    PrivateKey getPrivateKey(final String alias) {
        try {
            KeyStore.Entry entry = mKeyStore.getEntry(alias, new KeyStore.PasswordProtection(KEYSTORE_PASSWORD));
            if (entry instanceof KeyStore.PrivateKeyEntry) {
                KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) entry;
                return privateKeyEntry.getPrivateKey();
            }
        } catch (GeneralSecurityException e) {
            // NOP.
        }
        return null;
    }

    private boolean isSavedKeyStore() {
        File parentDir = mContext.getFilesDir();
        return new File(parentDir, mKeyStoreFilePath).exists();
    }

    private void loadKeyStore() throws IOException, NoSuchAlgorithmException, CertificateException {
        mKeyStore.load(mContext.openFileInput(mKeyStoreFilePath), KEYSTORE_PASSWORD);
    }

    void saveKeyStore() throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
        saveKeyStore(mContext.openFileOutput(mKeyStoreFilePath, Context.MODE_PRIVATE));
    }

    private void saveKeyStore(final OutputStream out)
            throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
        mKeyStore.store(out, KEYSTORE_PASSWORD);
    }

    private X509Certificate generateX509V3Certificate(final KeyPair keyPair, final X500Principal subject,
            final X500Principal issuer, final Date notBefore, final Date notAfter, final BigInteger serialNumber,
            final GeneralNames generalNames, final boolean isCA) throws GeneralSecurityException {
        Security.addProvider(new BouncyCastleProvider());
        X509V3CertificateGenerator generator = new X509V3CertificateGenerator();
        generator.setSerialNumber(serialNumber);
        generator.setIssuerDN(issuer);
        generator.setSubjectDN(subject);
        generator.setNotBefore(notBefore);
        generator.setNotAfter(notAfter);
        generator.setPublicKey(keyPair.getPublic());
        generator.setSignatureAlgorithm("SHA256WithRSAEncryption");
        generator.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(isCA));
        generator.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(160));
        generator.addExtension(X509Extensions.ExtendedKeyUsage, true,
                new ExtendedKeyUsage(KeyPurposeId.id_kp_serverAuth));
        if (generalNames != null) {
            generator.addExtension(X509Extensions.SubjectAlternativeName, false, generalNames);
        }
        return generator.generateX509Certificate(keyPair.getPrivate(), "BC");
    }

    @Override
    public X509Certificate generateX509V3Certificate(final KeyPair keyPair, final X500Principal subject,
            final X500Principal issuer, final GeneralNames generalNames, final boolean isCA)
            throws GeneralSecurityException {
        Calendar var2 = Calendar.getInstance();
        var2.set(2009, 0, 1);
        Date var3 = new Date(var2.getTimeInMillis());
        var2.set(2099, 0, 1);
        Date var4 = new Date(var2.getTimeInMillis());
        BigInteger serialNumber = BigInteger.valueOf(Math.abs(System.currentTimeMillis()));
        return generateX509V3Certificate(keyPair, subject, issuer, var3, var4, serialNumber, generalNames, isCA);
    }
}