Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.security.UnrecoverableKeyException: no match

Problem: need to save RSA private key in encrypted place. Try to use KeyStore for this purpose.

Code snippet:

package com.example.encryptiontest;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.UnrecoverableEntryException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Date;

import javax.security.auth.x500.X500Principal;

import org.bouncycastle.x509.X509V1CertificateGenerator;

import android.content.Context;
import android.util.Base64;

public class Encryption {
    private static final String ALIAS = "myAlias";
    private static final String FILE_NAME="key_store";
    private Context mContext;
    public Encryption(Context context){
        mContext = context;
    }
    public void save() throws NoSuchAlgorithmException, InvalidKeySpecException, CertificateEncodingException, InvalidKeyException, NoSuchProviderException, SignatureException, KeyStoreException, CertificateException, FileNotFoundException, UnsupportedEncodingException, IOException{
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(2048);
        KeyPair kp = kpg.genKeyPair();

        KeyFactory fact = KeyFactory.getInstance("RSA");
        RSAPublicKeySpec pub = fact.getKeySpec(kp.getPublic(),
                RSAPublicKeySpec.class);
        RSAPrivateKeySpec priv = fact.getKeySpec(kp.getPrivate(),
                RSAPrivateKeySpec.class);

        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(pub.getModulus(),
                pub.getPublicExponent());
        PublicKey pubKey = fact.generatePublic(keySpec);
        PrivateKey privateKey = fact.generatePrivate(new RSAPrivateKeySpec(
                priv.getModulus(), priv.getPrivateExponent()));

        saveToKeyStore(pubKey,ALIAS,mContext.getFilesDir() + FILE_NAME,privateKey);
    }

    public void load() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableEntryException{
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        char[] password = generatePassword(ALIAS);
        FileInputStream in = new FileInputStream(mContext.getFilesDir() + FILE_NAME);
        keyStore.load(in, password);
        KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) keyStore
                .getEntry(ALIAS, null);
        PrivateKey pubKey = (PrivateKey) keyEntry.getPrivateKey();
    }

    private void saveToKeyStore(PublicKey publicKey, String alias,
            String fileName, PrivateKey privateKey)
            throws CertificateEncodingException, NoSuchProviderException,
            NoSuchAlgorithmException, SignatureException, InvalidKeyException,
            KeyStoreException, IOException, CertificateException,
            FileNotFoundException, UnsupportedEncodingException {
        X509Certificate cert = getCertificate(publicKey, privateKey);
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        char[] password = generatePassword(alias);

        keyStore.load(null);
        keyStore.setKeyEntry(alias, privateKey, null,
                new Certificate[] { cert });
        FileOutputStream out = new FileOutputStream(fileName);

        keyStore.store(out, password);
        out.close();
    }

    private X509Certificate getCertificate(PublicKey publicKey,
            PrivateKey privateKey) throws CertificateEncodingException,
            NoSuchProviderException, NoSuchAlgorithmException,
            SignatureException, InvalidKeyException {
        Date startDate = new Date(); // time from which certificate is valid
        Date expiryDate = new Date(2050, 3, 3); // time after which certificate
                                                // is not valid
        BigInteger serialNumber = BigInteger.ONE; // serial number for
                                                    // certificate
        KeyPair keyPair = new KeyPair(publicKey, privateKey); // EC
                                                                // public/private
                                                                // key pair
        X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
        X500Principal dnName = new X500Principal("CN=Test CA Certificate");
        certGen.setSerialNumber(serialNumber);
        certGen.setIssuerDN(dnName);
        certGen.setNotBefore(startDate);
        certGen.setNotAfter(expiryDate);
        certGen.setSubjectDN(dnName); // note: same as issuer
        certGen.setPublicKey(keyPair.getPublic());
        certGen.setSignatureAlgorithm("SHA512withRSA");
        X509Certificate cert = certGen.generate(keyPair.getPrivate(), "BC");
        return cert;
    }

    private char[] generatePassword(String userName)
            throws NoSuchAlgorithmException, UnsupportedEncodingException {
        String defaultUdid = android.provider.Settings.System.getString(
                mContext.getContentResolver(),
                android.provider.Settings.Secure.ANDROID_ID);
        MessageDigest md = MessageDigest.getInstance("SHA-512");

        byte[] bSalt = defaultUdid.getBytes("UTF-8");
        byte[] bPw = userName.getBytes("UTF-8");
        md.update(bPw);
        byte[] r = md.digest(bSalt);
        return Base64.encodeToString(r, Base64.URL_SAFE).toCharArray();
    }
}

I use bouncycastle library for Certificate generation

Stack trace

03-16 14:00:52.106: W/System.err(27883): java.security.UnrecoverableKeyException: no match
03-16 14:00:52.108: W/System.err(27883):    at com.android.org.bouncycastle.jce.provider.JDKKeyStore$StoreEntry.getObject(JDKKeyStore.java:310)
03-16 14:00:52.110: W/System.err(27883):    at com.android.org.bouncycastle.jce.provider.JDKKeyStore.engineGetKey(JDKKeyStore.java:611)
03-16 14:00:52.111: W/System.err(27883):    at java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.java:372)
03-16 14:00:52.113: W/System.err(27883):    at java.security.KeyStore.getEntry(KeyStore.java:644)
03-16 14:00:52.114: W/System.err(27883):    at com.example.encryptiontest.Encryption.load(Encryption.java:71)
03-16 14:00:52.115: W/System.err(27883):    at com.example.encryptiontest.MainActivity.onCreate(MainActivity.java:16)
03-16 14:00:52.117: W/System.err(27883):    at android.app.Activity.performCreate(Activity.java:5122)
03-16 14:00:52.118: W/System.err(27883):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1081)
03-16 14:00:52.120: W/System.err(27883):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2270)
03-16 14:00:52.121: W/System.err(27883):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2358)
03-16 14:00:52.123: W/System.err(27883):    at android.app.ActivityThread.access$600(ActivityThread.java:156)
03-16 14:00:52.124: W/System.err(27883):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1340)
03-16 14:00:52.125: W/System.err(27883):    at android.os.Handler.dispatchMessage(Handler.java:99)
03-16 14:00:52.126: W/System.err(27883):    at android.os.Looper.loop(Looper.java:153)
03-16 14:00:52.128: W/System.err(27883):    at android.app.ActivityThread.main(ActivityThread.java:5299)
03-16 14:00:52.129: W/System.err(27883):    at java.lang.reflect.Method.invokeNative(Native Method)
03-16 14:00:52.130: W/System.err(27883):    at java.lang.reflect.Method.invoke(Method.java:511)
03-16 14:00:52.132: W/System.err(27883):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)
03-16 14:00:52.133: W/System.err(27883):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
03-16 14:00:52.134: W/System.err(27883):    at dalvik.system.NativeStart.main(Native Method)
like image 939
Alex Klimashevsky Avatar asked Mar 16 '14 10:03

Alex Klimashevsky


4 Answers

When I execute your code I get an exception that the private key entry must be protected with a password:

java.security.UnrecoverableKeyException: requested entry requires a password
    at java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.java:459)
    at java.security.KeyStore.getEntry(KeyStore.java:1290)
    at KeyStorage.load(KeyStorage.java:50)
    at PKI.main(PKI.java:518)

It looks like implementations of Java (both Oracle's and Android's) allow to create a private key entry without any protection (in keystore the keystore itself and each entry might have its own password protection), but during load they require a mandatory password. To fix that just specify password in KeyStore.getEntry()

KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(
    ALIAS, new KeyStore.PasswordProtection(password));

Another option is to use

PrivateKey key = (PrivateKey) keyStore.getKey(ALIAS, null);
like image 156
divanov Avatar answered Nov 03 '22 18:11

divanov


Could be a bad password. Write out your password from generatePassword to debug and use the keytool to check if it works to read the contents of the generated KeyStore.

Another Stack Overflow / Spongy Castle user said that it helped to specify a constructor when constructing his KeyStore. For him:

KeyStore ks = new KeyStore("BKS","SC");

instead of

KeyStore ks = new KeyStore("BKS");

I know you're not using the constructor to create a Keystore, but maybe this info will help.

like image 20
Barett Avatar answered Nov 03 '22 20:11

Barett


The link that @Barret is referring is this Unlimited Strength Jce and Android

I faced the similar problem. In my case the password while storing and retrieving was different courtesy toString method. Try providing hard coded password for debug purpose.

like image 27
Sumedh Jiwane Avatar answered Nov 03 '22 19:11

Sumedh Jiwane


There are several places to change:

1) I changed bouncycastle library to spongycastle

2) added

static {
    Security.addProvider(new BouncyCastleProvider());
}

3) used KeyStore keyStore = KeyStore.getInstance("BKS", "SC");

4) used

KeyFactory fact1 = KeyFactory.getInstance("RSA","SC");
PublicKey pubKey = fact1.generatePublic(keySpec);
PrivateKey privateKey = fact1.generatePrivate(new RSAPrivateKeySpec(priv
    .getModulus(), priv.getPrivateExponent()));

The reason is than different providers provide different binary representation of keys. So if you generate keys using one provider you can have issues if try to restore from binary stream using another provider

like image 20
Alex Klimashevsky Avatar answered Nov 03 '22 19:11

Alex Klimashevsky