使用RSA解密时出错

时间:2018-01-28 17:55:01

标签: android encryption aes rsa android-keystore

第一次,我正在使用Android Keystore。我需要用android keystore保存数据。在浏览文档和很少的互联网教程后。我想出了一个java类,它将处理Post-M和Pre-M android设备的Keystore操作,如下所示:

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.support.annotation.RequiresApi;
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.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
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.SecureRandom;
import java.security.UnrecoverableEntryException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Calendar;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.x500.X500Principal;

public class Cryptography {

    private static final String TAG = Cryptography.class.getSimpleName();

    private static final String ANDROID_KEY_STORE_NAME = "AndroidKeyStore";
    private static final String AES_MODE_M_OR_GREATER = "AES/GCM/NoPadding";
    private static final String AES_MODE_LESS_THAN_M = "AES/CBC/PKCS5Padding";
    // TODO update these bytes to be random for IV of encryption
    private static final byte[] FIXED_IV = new byte[]{ 55, 54, 53, 52, 51, 50,
            49, 48, 47,
            46, 45, 44 };
    private static final String CHARSET_NAME = "UTF-8";
    private static final String RSA_ALGORITHM_NAME = "RSA";
    private static final String RSA_MODE =  "RSA/ECB/PKCS1Padding";
    private static final String CIPHER_PROVIDER_NAME_ENCRYPTION_DECRYPTION_RSA = "AndroidOpenSSL";
    private static final String CIPHER_PROVIDER_NAME_ENCRYPTION_DECRYPTION_AES = "BC";
    private static final String SHARED_PREFERENCE_NAME = "XXXXXXXXXXXXXXX";
    private static final String PRE_M_KEY_NAME = "XXXXXXXX";
    private final String ENCRYPTED_KEY_NAME;
    private final String KEY_ALIAS;
    private final Context mContext;

    public Cryptography(Context context, String Encrypted_Key_Name,String Key_Alias) {
        this.mContext = context;
        this.ENCRYPTED_KEY_NAME = Encrypted_Key_Name;
        this.KEY_ALIAS = Key_Alias;
    }

    private void initKeys() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, NoSuchProviderException, InvalidAlgorithmParameterException,
            UnrecoverableEntryException, NoSuchPaddingException, InvalidKeyException {
        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_NAME);
        keyStore.load(null);

        if (!keyStore.containsAlias(KEY_ALIAS)) {
            initValidKeys();
        } else {
            boolean keyValid = false;
            KeyStore.Entry keyEntry = keyStore.getEntry(KEY_ALIAS, null);

            if (keyEntry instanceof KeyStore.SecretKeyEntry &&
                    android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
                keyValid = true;
            }

            if (keyEntry instanceof KeyStore.PrivateKeyEntry && android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
                keyValid = true;
            }

            if (!keyValid) {
                // System upgrade or something made key invalid
                removeKeys(keyStore);
                initValidKeys();
            }

        }

    }

    protected void removeKeys(KeyStore keyStore) throws KeyStoreException {
        keyStore.deleteEntry(KEY_ALIAS);
        removeSavedSharedPreferences();
    }

    private void initValidKeys() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, CertificateException, UnrecoverableEntryException, NoSuchPaddingException, KeyStoreException, InvalidKeyException, IOException {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            generateKeysForAPIMOrGreater();
        } else {
            generateKeysForAPILessThanM();
        }
    }

    @SuppressLint("ApplySharedPref")
    private void removeSavedSharedPreferences() {
        SharedPreferences pref = mContext.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE);
        pref.edit().clear().commit();
    }

    private void generateKeysForAPILessThanM() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, CertificateException, UnrecoverableEntryException, NoSuchPaddingException, KeyStoreException, InvalidKeyException, IOException {

        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            // Generate a key pair for encryption
            Calendar start = Calendar.getInstance();
            Calendar end = Calendar.getInstance();
            end.add(Calendar.YEAR, 30);
            KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(mContext)
                    .setAlias(KEY_ALIAS)
                    .setSubject(new X500Principal("CN=" + KEY_ALIAS))
                    .setSerialNumber(BigInteger.TEN)
                    .setStartDate(start.getTime())
                    .setEndDate(end.getTime())
                    .build();

            KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM_NAME, ANDROID_KEY_STORE_NAME);
            kpg.initialize(spec);
            kpg.generateKeyPair();

            saveEncryptedKey();
        }
    }

    @SuppressLint("ApplySharedPref")
    private void saveEncryptedKey() throws CertificateException, NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, KeyStoreException, NoSuchProviderException, UnrecoverableEntryException, IOException {
        SharedPreferences pref = mContext.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE);
        String encryptedKeyBase64encoded = pref.getString(ENCRYPTED_KEY_NAME, null);
        if (encryptedKeyBase64encoded == null) {
            Log.w(TAG, "saveEncryptedKey");
            byte[] key = new byte[16];
            SecureRandom secureRandom = new SecureRandom();
            secureRandom.nextBytes(key);
            byte[] encryptedKey = rsaEncryptKey(key);
            encryptedKeyBase64encoded = Base64.encodeToString(encryptedKey, Base64.DEFAULT);
            SharedPreferences.Editor edit = pref.edit();
            edit.putString(PRE_M_KEY_NAME, encryptedKeyBase64encoded);
            edit.commit();
            Log.w(TAG, "Data Committed");
        }

    }


    @RequiresApi(api = Build.VERSION_CODES.M)
    protected void generateKeysForAPIMOrGreater() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
        KeyGenerator keyGenerator;
        keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE_NAME);
        keyGenerator.init(
                new KeyGenParameterSpec.Builder(KEY_ALIAS,
                        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                        // NOTE no Random IV. According to above this is less secure but acceptably so.
                        .setRandomizedEncryptionRequired(false)
                        .build());
        // Note according to [docs](https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.html)
        // this generation will also add it to the keystore.
        keyGenerator.generateKey();
    }

    public String encryptData(String stringDataToEncrypt) throws NoSuchPaddingException, NoSuchAlgorithmException, UnrecoverableEntryException, CertificateException,
            KeyStoreException, IOException, InvalidAlgorithmParameterException, InvalidKeyException, NoSuchProviderException, BadPaddingException, IllegalBlockSizeException {

        initKeys();

        Log.w(TAG, "encryptData: stringDataToEncrypt - "+stringDataToEncrypt);

        if (stringDataToEncrypt == null) {
            throw new IllegalArgumentException("Data to be decrypted must be non null");
        }

        Cipher cipher;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            cipher = Cipher.getInstance(AES_MODE_M_OR_GREATER);
            cipher.init(Cipher.ENCRYPT_MODE, getSecretKeyAPIMorGreater(),
                    new GCMParameterSpec(128, FIXED_IV));
        } else {
            cipher = Cipher.getInstance(AES_MODE_LESS_THAN_M, CIPHER_PROVIDER_NAME_ENCRYPTION_DECRYPTION_AES);
            try {
                SecureRandom random = new SecureRandom();
//                byte[] iv = random.generateSeed(16);
                byte[] iv = new byte[16];
                random.nextBytes(iv);
                IvParameterSpec ivSpec = new IvParameterSpec(iv);
                cipher.init(Cipher.ENCRYPT_MODE, getSecretKeyAPILessThanM(),ivSpec);
            } catch (InvalidKeyException | IOException e) {
                // Since the keys can become bad (perhaps because of lock screen change)
                // drop keys in this case.
                removeKeys();
                throw e;
            }
        }

        byte[] encodedBytes = cipher.doFinal(stringDataToEncrypt.getBytes(CHARSET_NAME));
        String encryptedBase64Encoded = Base64.encodeToString(encodedBytes, Base64.DEFAULT);
        Log.w(TAG, "encryptData: after encrypt - "+encryptedBase64Encoded);
        return encryptedBase64Encoded;

    }

    public String decryptData(String encryptedData) throws NoSuchPaddingException, NoSuchAlgorithmException, UnrecoverableEntryException, CertificateException, KeyStoreException, IOException, InvalidAlgorithmParameterException, InvalidKeyException, NoSuchProviderException, BadPaddingException, IllegalBlockSizeException {

        initKeys();

        Log.w(TAG, "decryptData: encryptedData - "+encryptedData);

        if (encryptedData == null) {
            throw new IllegalArgumentException("Data to be decrypted must be non null");
        }

        byte[] encryptedDecodedData = Base64.decode(encryptedData, Base64.DEFAULT);

        Cipher c;
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                c = Cipher.getInstance(AES_MODE_M_OR_GREATER);
                c.init(Cipher.DECRYPT_MODE, getSecretKeyAPIMorGreater(), new GCMParameterSpec(128, FIXED_IV));

            } else {
                c = Cipher.getInstance(AES_MODE_LESS_THAN_M, CIPHER_PROVIDER_NAME_ENCRYPTION_DECRYPTION_AES);
                SecureRandom random = new SecureRandom();
                byte[] iv = new byte[16];
                random.nextBytes(iv);
                IvParameterSpec ivSpec = new IvParameterSpec(iv);
                c.init(Cipher.DECRYPT_MODE, getSecretKeyAPILessThanM(),ivSpec);
                //decodedBytes =  c.doFinal(encryptedDecodedData,0,16);
            }
        } catch (InvalidKeyException | IOException e) {
            // Since the keys can become bad (perhaps because of lock screen change)
            // drop keys in this case.
            removeKeys();
            throw e;
        }

        final byte[] decodedBytes = c.doFinal(encryptedDecodedData);

        Log.w(TAG, "encryptData: after decrypt - "+new String(decodedBytes, CHARSET_NAME));
        return new String(decodedBytes, CHARSET_NAME);

    }

    private Key getSecretKeyAPIMorGreater() throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, UnrecoverableKeyException {
        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_NAME);
        keyStore.load(null);
        try {
            return keyStore.getKey(KEY_ALIAS, null);
        } catch (UnrecoverableKeyException e) {
            throw new UnsupportedOperationException();
        }
    }

    private Key getSecretKeyAPILessThanM() throws CertificateException, NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, KeyStoreException, NoSuchProviderException, UnrecoverableEntryException, IOException {
        SharedPreferences pref = mContext.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE);
        String encryptedKeyBase64Encoded = pref.getString(PRE_M_KEY_NAME, null);

        Log.w(TAG, "getSecretKeyAPILessThanM: encryptedKeyBase64Encoded - "+encryptedKeyBase64Encoded);
        // need to check null, omitted here
        byte[] encryptedKey = Base64.decode(encryptedKeyBase64Encoded, Base64.DEFAULT);
        byte[] key = rsaDecryptKey(encryptedKey);
        return new SecretKeySpec(key, "AES");
    }

    private byte[] rsaEncryptKey(byte[] secret) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, NoSuchProviderException, NoSuchPaddingException, UnrecoverableEntryException, InvalidKeyException {

        Log.w(TAG, "rsaEncryptKe    y: secret - "+secret);

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

        KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(KEY_ALIAS, null);
        RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();

        Cipher inputCipher = Cipher.getInstance(RSA_MODE, CIPHER_PROVIDER_NAME_ENCRYPTION_DECRYPTION_RSA);
        inputCipher.init(Cipher.ENCRYPT_MODE, publicKey );

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, inputCipher);
        cipherOutputStream.write(secret);
        cipherOutputStream.close();

        byte[] encryptedKeyAsByteArray = outputStream.toByteArray();

        Log.w(TAG, "rsaEncryptKey: encryptedKeyAsByteArray - "+encryptedKeyAsByteArray);
        return encryptedKeyAsByteArray;
    }

    private  byte[] rsaDecryptKey(byte[] encrypted) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException,
            UnrecoverableEntryException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException {

        Log.w(TAG, "rsaDecryptKey: encrypted - "+encrypted);

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

        KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(KEY_ALIAS, null);
        RSAPrivateKey privateKey = (RSAPrivateKey)privateKeyEntry.getPrivateKey();

        Cipher output = Cipher.getInstance(RSA_MODE, CIPHER_PROVIDER_NAME_ENCRYPTION_DECRYPTION_RSA);
        output.init(Cipher.DECRYPT_MODE, privateKey);

        ByteArrayInputStream inputStream = new ByteArrayInputStream(encrypted);
        CipherInputStream cipherInputStream = new CipherInputStream(inputStream,output);

        ArrayList<Byte> values = new ArrayList<>();
        int nextByte;
        while ((nextByte = cipherInputStream.read())!=-1) {
            values.add((byte)nextByte);
        }

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

        inputStream.close();
        cipherInputStream.close();

        Log.w(TAG, "rsaDecryptKey: decryptedKeyAsBytes - "+decryptedKeyAsBytes);

        return decryptedKeyAsBytes;
    }

    public void removeKeys() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_NAME);
        keyStore.load(null);
        removeKeys(keyStore);
    }

}

这对于Post-M设备来说非常合适,但我遇到了Pre-M设备的问题。我有RSA解密问题,logcat如下所示:

java.io.IOException: Error while finalizing cipher
    at javax.crypto.CipherInputStream.fillBuffer(CipherInputStream.java:104)
     at javax.crypto.CipherInputStream.read(CipherInputStream.java:130)
    at com.xxxx.XXXXXXX.data.storage.Cryptography.rsaDecryptKey(Cryptography.java:337)
    at com.xxxx.XXXXXXX.data.storage.Cryptography.getSecretKeyAPILessThanM(Cryptography.java:290)
    at com.xxxx.XXXXXXX.data.storage.Cryptography.decryptData(Cryptography.java:256)
     at com.xxxx.XXXXXXX.ui.activities.SecurePinAuthentication.securePinLogin(SecurePinAuthentication.java:286)
    at com.xxxx.XXXXXXX.ui.activities.SecurePinAuthentication.onClick(SecurePinAuthentication.java:191)
     at android.view.View.performClick(View.java:5197)
    at android.view.View$PerformClick.run(View.java:20909)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
     at android.os.Looper.loop(Looper.java:145)
     at android.app.ActivityThread.main(ActivityThread.java:5944)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
 Caused by: javax.crypto.BadPaddingException: error:0407106B:rsa routines:RSA_padding_check_PKCS1_type_2:block type is not 02
    at com.android.org.conscrypt.NativeCrypto.RSA_private_decrypt(Native Method)
    at com.android.org.conscrypt.OpenSSLCipherRSA.engineDoFinal(OpenSSLCipherRSA.java:273)
    at com.android.org.conscrypt.OpenSSLCipherRSA.engineDoFinal(OpenSSLCipherRSA.java:297)
     at javax.crypto.Cipher.doFinal(Cipher.java:1314)
    at javax.crypto.CipherInputStream.fillBuffer(CipherInputStream.java:102)    ... 16 more


    javax.crypto.BadPaddingException: pad block corrupted
01-28 17:17:53.274      at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:854)
01-28 17:17:53.274      at javax.crypto.Cipher.doFinal(Cipher.java:1340)
01-28 17:17:53.274      at com.xxxx.XXXXXXX.data.storage.Cryptography.decryptData(Cryptography.java:266)
01-28 17:17:53.274      at com.xxxx.XXXXXXX.ui.activities.SecurePinAuthentication.securePinLogin(SecurePinAuthentication.java:286)
01-28 17:17:53.274      at com.xxxx.XXXXXXX.ui.activities.SecurePinAuthentication.onClick(SecurePinAuthentication.java:191)
01-28 17:17:53.274      at android.view.View.performClick(View.java:5197)
01-28 17:17:53.274      at android.view.View$PerformClick.run(View.java:20909)
01-28 17:17:53.274      at android.os.Handler.handleCallback(Handler.java:739)
01-28 17:17:53.274      at android.os.Handler.dispatchMessage(Handler.java:95)
01-28 17:17:53.274      at android.os.Looper.loop(Looper.java:145)
01-28 17:17:53.274      at android.app.ActivityThread.main(ActivityThread.java:5944)
01-28 17:17:53.274      at java.lang.reflect.Method.invoke(Native Method)
01-28 17:17:53.274      at java.lang.reflect.Method.invoke(Method.java:372)
01-28 17:17:53.274      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
01-28 17:17:53.274      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)

我在互联网上搜索并尝试了很多方法&amp;失败。最后,我在这里发帖。非常感谢帮助。

0 个答案:

没有答案