válida una subido studio recuperar has generate generar firmar firma crear con android android-keystore

una - keytool android



¿Cómo puedo usar Android KeyStore para almacenar de forma segura cadenas arbitrarias? (2)

Me gustaría poder almacenar de forma segura algunas cadenas sensibles en Android KeyStore. Recibo las cadenas del servidor, pero tengo un caso de uso que me obliga a persistir. KeyStore solo permitirá el acceso desde el mismo UID que el asignado a mi aplicación, y cifrará los datos con la contraseña maestra del dispositivo, por lo que entiendo que no tengo que hacer ningún cifrado adicional para proteger mis datos. Mi problema es que me falta algo sobre cómo escribir los datos. El siguiente código funciona perfectamente, siempre que se omita la llamada a KeyStore.store (null). Ese código falla, y mientras no pueda almacenar los datos después de colocarlos en KeyStore, no puedo continuarlos.

Creo que me falta algo sobre la API de KeyStore, pero no sé qué. Cualquier ayuda apreciada!

String metaKey = "ourSecretKey"; String encodedKey = "this is supposed to be a secret"; byte[] encodedKeyBytes = new byte[(int)encodedKey.length()]; encodedKeyBytes = encodedKey.getBytes("UTF-8"); KeyStoreParameter ksp = null; //String algorithm = "DES"; String algorithm = "DESede"; SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(algorithm); SecretKeySpec secretKeySpec = new SecretKeySpec(encodedKeyBytes, algorithm); SecretKey secretKey = secretKeyFactory.generateSecret(secretKeySpec); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry(secretKey); keyStore.setEntry(metaKey, secretKeyEntry, ksp); keyStore.store(null); String recoveredSecret = ""; if (keyStore.containsAlias(metaKey)) { KeyStore.SecretKeyEntry recoveredEntry = (KeyStore.SecretKeyEntry)keyStore.getEntry(metaKey, ksp); byte[] bytes = recoveredEntry.getSecretKey().getEncoded(); for (byte b : bytes) { recoveredSecret += (char)b; } } Log.v(TAG, "recovered " + recoveredSecret);


Empecé con la premisa de que podía usar AndroidKeyStore para proteger blobs arbitrarios de datos y llamarlos "claves". Sin embargo, cuanto más profundizaba en esto, más claro se volvía que la API de KeyStore estaba profundamente enredada con los objetos relacionados con la seguridad: Certificados, KeySpecs, proveedores, etc. No está diseñada para almacenar datos arbitrarios, y no veo una solución directa. camino a doblarlo para ese propósito.

Sin embargo, AndroidKeyStore se puede usar para ayudarme a proteger mis datos confidenciales. Puedo usarlo para administrar las claves criptográficas que usaré para encriptar datos locales a la aplicación. Al usar una combinación de AndroidKeyStore, CipherOutputStream y CipherInputStream, podemos:

  • Generar, almacenar de forma segura y recuperar claves de cifrado en el dispositivo
  • Cifre los datos arbitrarios y guárdelos en el dispositivo (en el directorio de la aplicación, donde estarán más protegidos con los permisos del sistema de archivos)
  • Acceda y descifre los datos para su uso posterior.

Aquí hay un código de ejemplo que demuestra cómo se logra esto.

try { KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); String alias = "key3"; 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); KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this) .setAlias(alias) .setKeyType("RSA") .setKeySize(2048) .setSubject(new X500Principal("CN=test")) .setSerialNumber(BigInteger.ONE) .setStartDate(notBefore.getTime()) .setEndDate(notAfter.getTime()) .build(); KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore"); generator.initialize(spec); KeyPair keyPair = generator.generateKeyPair(); } int nAfter = keyStore.size(); Log.v(TAG, "Before = " + nBefore + " After = " + nAfter); // Retrieve the keys KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null); RSAPrivateKey privateKey = (RSAPrivateKey) privateKeyEntry.getPrivateKey(); RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey(); Log.v(TAG, "private key = " + privateKey.toString()); Log.v(TAG, "public key = " + publicKey.toString()); // Encrypt the text String plainText = "This text is supposed to be a secret!"; String dataDirectory = getApplicationInfo().dataDir; String filesDirectory = getFilesDir().getAbsolutePath(); String encryptedDataFilePath = filesDirectory + File.separator + "keep_yer_secrets_here"; Log.v(TAG, "plainText = " + plainText); Log.v(TAG, "dataDirectory = " + dataDirectory); Log.v(TAG, "filesDirectory = " + filesDirectory); Log.v(TAG, "encryptedDataFilePath = " + encryptedDataFilePath); Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL"); inCipher.init(Cipher.ENCRYPT_MODE, publicKey); Cipher outCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL"); outCipher.init(Cipher.DECRYPT_MODE, privateKey); CipherOutputStream cipherOutputStream = new CipherOutputStream( new FileOutputStream(encryptedDataFilePath), inCipher); cipherOutputStream.write(plainText.getBytes("UTF-8")); cipherOutputStream.close(); CipherInputStream cipherInputStream = new CipherInputStream(new FileInputStream(encryptedDataFilePath), outCipher); byte [] roundTrippedBytes = new byte[1000]; // TODO: dynamically resize as we get more data int index = 0; int nextByte; while ((nextByte = cipherInputStream.read()) != -1) { roundTrippedBytes[index] = (byte)nextByte; index++; } String roundTrippedString = new String(roundTrippedBytes, 0, index, "UTF-8"); Log.v(TAG, "round tripped string = " + roundTrippedString); } catch (NoSuchAlgorithmException e) { Log.e(TAG, Log.getStackTraceString(e)); } catch (NoSuchProviderException e) { Log.e(TAG, Log.getStackTraceString(e)); } catch (InvalidAlgorithmParameterException e) { Log.e(TAG, Log.getStackTraceString(e)); } catch (KeyStoreException e) { Log.e(TAG, Log.getStackTraceString(e)); } catch (CertificateException e) { Log.e(TAG, Log.getStackTraceString(e)); } catch (IOException e) { Log.e(TAG, Log.getStackTraceString(e)); } catch (UnrecoverableEntryException e) { Log.e(TAG, Log.getStackTraceString(e)); } catch (NoSuchPaddingException e) { Log.e(TAG, Log.getStackTraceString(e)); } catch (InvalidKeyException e) { Log.e(TAG, Log.getStackTraceString(e)); } catch (BadPaddingException e) { Log.e(TAG, Log.getStackTraceString(e)); } catch (IllegalBlockSizeException e) { Log.e(TAG, Log.getStackTraceString(e)); } catch (UnsupportedOperationException e) { Log.e(TAG, Log.getStackTraceString(e)); }


Es posible que hayas notado que hay problemas para manejar diferentes niveles de API con la Tienda de claves de Android.

Scytale es una biblioteca de código abierto que proporciona una envoltura conveniente alrededor de la Tienda de llaves de Android para que no tenga una placa de calderas de escritura y pueda sumergirse directamente en la encriptación / desencriptación.

Código de muestra:

// Create and save key Store store = new Store(getApplicationContext()); if (!store.hasKey("test")) { SecretKey key = store.generateSymmetricKey("test", null); } ... // Get key SecretKey key = store.getSymmetricKey("test", null); // Encrypt/Decrypt data Crypto crypto = new Crypto(Options.TRANSFORMATION_SYMMETRIC); String text = "Sample text"; String encryptedData = crypto.encrypt(text, key); Log.i("Scytale", "Encrypted data: " + encryptedData); String decryptedData = crypto.decrypt(encryptedData, key); Log.i("Scytale", "Decrypted data: " + decryptedData);