'How to retrieve value from a KeyEntry using its alias using android Keystore API

I want to encrypt some arbitrary string using public key and decrypt the string using private key. I am able to achieve this. I am sharing the class I have written

import static android.security.keystore.KeyProperties.DIGEST_SHA256;

import android.content.Context;
import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Base64;
import android.util.Log;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Calendar;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.security.auth.x500.X500Principal;

public class CipherUtils {

    private static final String TAG = CipherUtils.class.getSimpleName();
    public static final String ANDROID_KEY_STORE = "AndroidKeyStore";
    private static final String TRANSFORMATION = "RSA/ECB/PKCS1Padding";

    public static void generateKeyWithDynamicAlias(Context context, String alias) {
        try {
            KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
            keyStore.load(null);

            int nBefore = keyStore.size();

            // Create the keys if necessary
            if (!keyStore.containsAlias(alias)) {

                Calendar notBefore = Calendar.getInstance();
                Calendar notAfter = Calendar.getInstance();
                notAfter.add(Calendar.YEAR, 1);
                KeyPairGenerator generator = null;
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
                    generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE);
                    generator.initialize(new
                            KeyGenParameterSpec.Builder(alias,
                            KeyProperties.PURPOSE_ENCRYPT |
                                    KeyProperties.PURPOSE_DECRYPT)
                            .setKeySize(1024)
                            .setDigests(DIGEST_SHA256)
                            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
                            .setCertificateSubject(new X500Principal("CN=" + alias))
                            .setCertificateSerialNumber(BigInteger.ONE)
                            .setCertificateNotBefore(notBefore.getTime())
                            .setCertificateNotAfter(notAfter.getTime())
                            .build());
                } else {
                    generator = KeyPairGenerator.getInstance("RSA", ANDROID_KEY_STORE);
                    generator.initialize( new KeyPairGeneratorSpec.Builder(context)
                            .setAlias(alias)
                            .setKeySize(1024)
                            .setSubject(new X500Principal("CN=" + alias))
                            .setSerialNumber(BigInteger.ONE)
                            .setStartDate(notBefore.getTime())
                            .setEndDate(notAfter.getTime())
                            .build());
                }

                KeyPair keyPair = generator.generateKeyPair();
                PrivateKey privateKey = keyPair.getPrivate();
                PublicKey publicKey = keyPair.getPublic();
            }
            int nAfter = keyStore.size();
            Log.d(TAG, "Before = " + nBefore + " After = " + nAfter);

        } catch (NoSuchAlgorithmException | NoSuchProviderException | KeyStoreException | InvalidAlgorithmParameterException | CertificateException | IOException | UnsupportedOperationException e) {
            Log.e(TAG, "exception: " + e.getMessage());
        }
    }

    public static PublicKey getPublicKeyUsingAlias(String alias) {
        try {
            KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
            keyStore.load(null);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && keyStore.containsAlias(alias)) {
                return keyStore.getCertificate(alias).getPublicKey();
            } else if (keyStore.containsAlias(alias)) {
                KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
                return privateKeyEntry.getCertificate().getPublicKey();
            } else {
                return null;
            }
        } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | UnrecoverableEntryException | IOException | UnsupportedOperationException e) {
            Log.e(TAG, e.getMessage());
            return null;
        }
    }

    public static PrivateKey getPrivateKeyUsingAlias(Context context, String alias) {
        try {
            KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
            keyStore.load(null);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && keyStore.containsAlias(alias)) {
                return (PrivateKey) keyStore.getKey(alias, null);
            } else if (keyStore.containsAlias(alias)) {
                KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
                return privateKeyEntry.getPrivateKey();
            } else {
                return null;
            }
        } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException | UnsupportedOperationException e) {
            Log.e(TAG, e.getMessage());
            return null;
        } catch (UnrecoverableEntryException e) {
            Log.e(TAG, e.getMessage());
            try {
                generateKeyWithDynamicAlias(context, alias);
                KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
                keyStore.load(null);

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && keyStore.containsAlias(alias)) {
                    return (PrivateKey) keyStore.getKey(alias, null);
                } else if (keyStore.containsAlias(alias)) {
                    KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
                    return privateKeyEntry.getPrivateKey();
                } else {
                    return null;
                }
            } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException | UnrecoverableEntryException e1) {
                Log.e(TAG, e1.getMessage());
                return null;
            }
        }
    }

    public static String encryptString(String stringToEncrypt, String alias) {
        PublicKey publicKey = getPublicKeyUsingAlias(alias);

        if (publicKey != null) {
            // Encrypt the text
            Cipher input = null;
            try {
                input = Cipher.getInstance(TRANSFORMATION);
                input.init(Cipher.ENCRYPT_MODE, publicKey);

                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                CipherOutputStream cipherOutputStream = new CipherOutputStream(
                        outputStream, input);
                cipherOutputStream.write(stringToEncrypt.getBytes(StandardCharsets.UTF_8));
                cipherOutputStream.close();

                byte[] vals = outputStream.toByteArray();
                return Base64.encodeToString(vals, Base64.DEFAULT);
            } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public static String decryptString(Context context, String alias, String encrypted) {
        try {
            final PrivateKey privateKey = getPrivateKeyUsingAlias(context, alias);
            if (privateKey != null) {
                Cipher output = Cipher.getInstance(TRANSFORMATION);
                output.init(Cipher.DECRYPT_MODE, privateKey);

                CipherInputStream cipherInputStream = new CipherInputStream(
                        new ByteArrayInputStream(Base64.decode(encrypted, Base64.DEFAULT)), output);
                ArrayList<Byte> values = new ArrayList<>();
                int nextByte;
                while ((nextByte = cipherInputStream.read()) != -1) {
                    values.add((byte) nextByte);
                }

                byte[] bytes = new byte[values.size()];
                for (int i = 0; i < bytes.length; i++) {
                    bytes[i] = values.get(i);
                }

                return new String(bytes, 0, bytes.length, StandardCharsets.UTF_8);
            }

        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, e.getMessage());
            return null;
        }
        return null;
    }
}

I need to fetch the value stored in KeyStore and use the decrypt method I have written to get the actual value we stored previously.

try {
            KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
            keyStore.load(null);
            ArrayList<String> storedAliases = Collections.list(keyStore.aliases());
            if (storedAliases.contains(KEY_ALIAS)) {
                //need to fetch the stored value using this alias 
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

Note: we can't use shared preference, local database or file in data package to write that encrypted string

Any help will be appreciated.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source