【问题标题】:How securely store sensitive data (short string) into keystore如何安全地将敏感数据(短字符串)存储到密钥库中
【发布时间】:2016-11-02 15:39:58
【问题描述】:

我需要将敏感数据(小字符串)存储到密钥库中。 此处描述的示例几乎正是我需要做的。 问题:我不太了解它,我是那个主题的新手(密码,加密,RSA ...)

https://medium.com/@ericfu/securely-storing-secrets-in-an-android-application-501f030ae5a3#.oqvxbjn8m

这就是我正在研究的课程(基于上面提到的这篇文章):

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.security.KeyPairGeneratorSpec;
import android.security.keystore.KeyProperties;
import android.support.v7.app.AppCompatActivity;
import android.util.Base64;
import android.util.Log;
import android.widget.Toast;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Calendar;

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


public class SecondActivity extends AppCompatActivity {

    static final String TAG = "SimpleKeystoreApp";
    static final String CIPHER_PROVIDER = "AndroidOpenSSL";
    private static final String RSA_MODE = "RSA/ECB/PKCS1Padding";
    private static final String AES_MODE = "AES/ECB/PKCS7Padding";
    private static final String KEY_ALIAS = "this is my alias";
    private static final String SHARED_PREFENCE_NAME = "my shared_prefs";
    private static final String ENCRYPTEDKEY_KEY = "encrypted_key";
    private static final String TO_BE_ENCRYPTED_KEY = "this is my test";
    private static final String ANDROID_KEYSTORE = "AndroidKeyStore";

    KeyStore keyStore;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        try {
            keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
            keyStore.load(null);
        } catch (Exception e) {
            Log.d(TAG, e.getMessage());
        }

        createNewKeys();

        try {
            generateAndStoreAESKey();
        } catch (Exception e) {
            Log.d(TAG, "couldn't generateAndStoreAESKey:" + e.getMessage());
        }
        String encrypted = null;
        try {
            encrypted = encrypt(getApplicationContext(), TO_BE_ENCRYPTED_KEY.getBytes());
            Log.d(TAG, "encrypted:" + encrypted);
        } catch (Exception e) {
            Log.d(TAG, "couldn't encrypt:" + e.getMessage());
        }

        try {
            decrypt(getApplicationContext(), encrypted.getBytes());
            Log.d(TAG, "decrypted:" + encrypted);
        } catch (Exception e) {
            Log.d(TAG, "couldn't decrypt:" + e.getMessage());
        }
        setContentView(R.layout.activity_main);
    }

    public void createNewKeys() {
        Log.d(TAG, "___ createNewKeys");
        try {
            // Create new key if needed
            // Generate the RSA key pairs
            keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
            keyStore.load(null);
            // Generate the RSA key pairs
            if (!keyStore.containsAlias(KEY_ALIAS)) {
                // Generate a key pair for encryption
                Calendar start = Calendar.getInstance();
                Calendar end = Calendar.getInstance();
                end.add(Calendar.YEAR, 30);
                KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this)
                        .setAlias(KEY_ALIAS)
                        .setSubject(new X500Principal("CN=" + KEY_ALIAS))
                        .setSerialNumber(BigInteger.TEN)
                        .setStartDate(start.getTime())
                        .setEndDate(end.getTime())
                        .build();
                KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEYSTORE);
                kpg.initialize(spec);
                kpg.generateKeyPair();

                KeyPair keyPair = kpg.generateKeyPair();
                Log.d(TAG, "Public Key is: " + keyPair.getPublic().toString());
            }
        } catch (Exception e) {
            Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show();
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }

    private void generateAndStoreAESKey() throws Exception{
        Log.d(TAG, "___ generateAndStoreAESKey");

        SharedPreferences pref = getApplicationContext().getSharedPreferences(SHARED_PREFENCE_NAME, Context.MODE_PRIVATE);
        String enryptedKeyB64 = pref.getString(ENCRYPTEDKEY_KEY, null);
        if (enryptedKeyB64 == null) {
            byte[] key = new byte[16];
            SecureRandom secureRandom = new SecureRandom();
            secureRandom.nextBytes(key);
            byte[] encryptedKey = rsaEncrypt(key);
            enryptedKeyB64 = Base64.encodeToString(encryptedKey, Base64.DEFAULT);
            SharedPreferences.Editor edit = pref.edit();
            edit.putString(ENCRYPTEDKEY_KEY, enryptedKeyB64);
            edit.commit();
        }
    }
    private SecretKeySpec getSecretKey(Context context) throws Exception{
        SharedPreferences pref = context.getSharedPreferences(SHARED_PREFENCE_NAME, Context.MODE_PRIVATE);
        String enryptedKeyB64 = pref.getString(ENCRYPTEDKEY_KEY, null);
        // need to check null, omitted here
        byte[] encryptedKey = Base64.decode(enryptedKeyB64, Base64.DEFAULT);
        byte[] key = rsaDecrypt(encryptedKey);
        return new SecretKeySpec(key, "AES");
    }

    public String encrypt(Context context, byte[] input) throws Exception{
        Cipher c = Cipher.getInstance(AES_MODE, "BC");
        c.init(Cipher.ENCRYPT_MODE, getSecretKey(context));
        byte[] encodedBytes = c.doFinal(input);
        String encryptedBase64Encoded =  Base64.encodeToString(encodedBytes, Base64.DEFAULT);
        return encryptedBase64Encoded;
    }


    public byte[] decrypt(Context context, byte[] encrypted) throws Exception{
        Cipher c = Cipher.getInstance(AES_MODE, "BC");
        c.init(Cipher.DECRYPT_MODE, getSecretKey(context));
        byte[] decodedBytes = c.doFinal(encrypted);
        return decodedBytes;
    }


    private byte[] rsaEncrypt(byte[] secret) throws Exception{
        KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(KEY_ALIAS, null);
        // Encrypt the text
        Cipher inputCipher = Cipher.getInstance(RSA_MODE, CIPHER_PROVIDER);
        inputCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.getCertificate().getPublicKey());

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

        byte[] vals = outputStream.toByteArray();
        return vals;
    }

    private  byte[]  rsaDecrypt(byte[] encrypted) throws Exception {
        KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(KEY_ALIAS, null);
        Cipher output = Cipher.getInstance(RSA_MODE, CIPHER_PROVIDER);
        output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
        CipherInputStream cipherInputStream = new CipherInputStream(
                new ByteArrayInputStream(encrypted), 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).byteValue();
        }
        return bytes;
    }
}

我有一个 InvalidKeyException:当我调用时需要抛出 RSA 私钥或公钥

outputCipher.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());

private  byte[]  rsaDecrypt(byte[] encrypted)

所以我需要能够解密存储在密钥库中的私钥,但是这个操作给了我一个错误。

我不明白为什么。也许流程中有问题? 请用简单的话解释我做错了什么。

【问题讨论】:

  • 也许你可以发一个minimal reproducible example
  • 我了解@khelwood,但这是我能做的最小的......它可以在Android项目的主要活动中运行。
  • 你检查过 logcat 是否有错误吗?
  • @Robert 当然。 resDecrypt 函数抛出错误:当我尝试: outputCipher.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());我收到此错误消息:InvalidKeyException: Need RSA private or public key
  • 您使用了错误的CIPHER_PROVIDERAndroidOpenSSL 无法处理 AndroidKeyStoreRSAPrivateKey - 这永远不会工作!您为什么不尝试删除提供程序规范。只需致电Cipher.getInstance(RSA_MODE)

标签: java android encryption android-keystore


【解决方案1】:

@Robert 在评论中的回答解决了我的问题: 我从

中删除了 CIPHER_PROVIDER

密码输入密码 = Cipher.getInstance(RSA_MODE, CIPHER_PROVIDER);

【讨论】:

    猜你喜欢
    • 2012-03-28
    • 2013-08-01
    • 2012-08-14
    • 1970-01-01
    • 2015-11-24
    • 2017-04-09
    • 2022-12-07
    • 2020-10-27
    • 2012-08-02
    相关资源
    最近更新 更多