Критичность: КРИТИЧНЫЙ
Способ обнаружения: DAST, КЛЮЧЕВАЯ ИНФОРМАЦИЯ

Описание

Приложение использует доступное на чтение хранилище ключей с приватными ключами, защищёнными слабым паролем. Ключ шифрования не должен храниться в общедоступном месте, а Keystore и хранящиеся в нём ключи должны быть защищены надёжным паролем.

При использовании криптографических операций на устройстве необходимо обеспечить максимальную безопасность основного секрета в таких операциях — ключа шифрования. При использовании ассиметричного шифрования — необходимо сохранить в секрете приватный ключ, в то время как в случае использования симметричных алгоритмов следует защищать ключ, который используется и для шифрования и для расшифрования sensitive-информации. Существует несколько основных способов хранения ключей в зависимости от версии операционной системы в хранилище AndroidKeyStore или в директории приложении в BKS. Наиболее безопасным вариантом, безусловно является хранение ключей в AndroidKeyStore. Но, если необходимо хранить ключи в BKS нужно не забывать о том, что и само хранилище и все ключи в нем должны быть защищены надежным паролем.

Компрометация ключевой информации, которая используется в приложении, может привести к катастрофическим последствиям, в зависимости от использования данной информации в приложении, начиная от расшифровки файлов, трафика, заканчивая компрометацией закрытого ключа, использующегося для подписи приложения.

Рекомендации

Ключи шифрования не должны храниться в общедоступном месте, даже если это директория приложения на SD-карте.

Существует несколько основных способов хранения ключей в зависимости от версии операционной системы:

  • На Android API<18 ключи шифрования должны храниться внутри директории приложения в BKS.
  • На Android API>=18 RSA ключи должны храниться в AndroidKeyStore, AES ключи в BKS.
  • На Android API>=23 RSA и AES ключи должны храниться в AndroidKeyStore

Не стоит забывать, что при использовании BKS во внутренней директории приложения необходимо дополнительно защищать его и хранящиеся в нем ключи с помощью надежного пароля. Как один из вариантов, сгенерированный пароль должен быть проверен в базе наиболее популярных паролей и должен соответствовать минимальным требованиям:

Длина пароля не меньше 20 символов

  • Обязательно содержание хотя бы одной прописной буквы
  • Обязательно содержание хотя бы одной строчной буквы
  • Обязательно содержание хотя бы одной цифры
  • Обязательно содержание хотя бы одного спец символа

 

Пример генерации защищенного хранилища BKS с паролем и ключом, также защищенным паролем:
keytool -importcert -v -trustcacerts -file "C:\Users\Indra\Documents\myapp.com.cer" -alias IntermediateCA -keystore "C:\Users\Indra\Documents\appKeyStore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "C:\Users\Indra\Downloads\bcprov-jdk15on-154.jar" -storetype BKS -storepass StorePass123


keytool -list -keystore "C:\Users\Indra\Documents\appKeyStore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "C:\Users\Indra\Downloads\bcprov-jdk15on-154.jar" -storetype BKS -storepass "StorePass123"

--------------------------------------------------------------------------------------------------------------------------------------------------------

openssl pkcs12 -export -in "/home/myapp/myapp_cert_2016/ssl_certificate.crt" -inkey "/home/myapp/myapp_cert_2016/domainname.key" -certfile "/home/myapp/myapp_cert_2016/ssl_certificate.crt" -out testkeystore.p12
Export password : exportpass123

keytool -importkeystore -srckeystore "C:\Users\Indra\myapp\testkeystore.p12" -srcstoretype pkcs12 -destkeystore ""C:\Users\Indra\myapp\wso2carbon.jks" -deststoretype JKS

Destination keystore password : exportpass123



----------------------------------------------------- Final JKS Keystore generation ------------------------------------------------------

# openssl pkcs12 -export -in "/home/myapp/myapp_cert_2016/ssl_certificate.crt" -inkey "/home/myapp/myapp_cert_2016/domainname.key" -certfile "/home/myapp/myapp_cert_2016/ssl_certificate.crt" -out myapp_cert.p12

Export Password : StorePass123

keytool -importkeystore -srckeystore "C:\Users\Indra\myapp\myapp_cert.p12" -srcstoretype pkcs12 -destkeystore "C:\Users\Indra\myapp\myapp_keystore.jks" -deststoretype JKS


Import Password : StorePass123


----------------------------------------------------- Final BKS Keystore generation ------------------------------------------------------

keytool -importkeystore -srckeystore "C:\Users\Indra\myapp\myapp_keystore.jks -deststoretype JKS" -destkeystore "C:\Users\Indra\myapp\myapp_keystore.bks" -srcstoretype JKS -deststoretype BKS -srcstorepass StorePass123 -deststorepass StorePass123 -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "C:\Users\Indra\Downloads\bcprov-jdk15on-154.jar"


On error or exception steps to be taken

- Comment above line and add the new line in java.security file in jre/lib/security

	#security.provider.7=com.sun.security.sasl.Provider
	security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider

- You need to install the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy 

 Для генерации и использования AndroidKeyStore возможно использовать вспомогательные библиотеки или же возможно реализовать генерацию и получение ключа самостоятельно.

 

Добавление нового ключа в KeyStore
     public void createNewKeys(View view) {
        String alias = aliasText.getText().toString();
        try {
            // Create new key if needed
            if (!keyStore.containsAlias(alias)) {
                Calendar start = Calendar.getInstance();
                Calendar end = Calendar.getInstance();
                end.add(Calendar.YEAR, 1);
                KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this)
                        .setAlias(alias)
                        .setSubject(new X500Principal("CN=Sample Name, O=Android Authority"))
                        .setSerialNumber(BigInteger.ONE)
                        .setStartDate(start.getTime())
                        .setEndDate(end.getTime())
                        .build();
                KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
                generator.initialize(spec);

                KeyPair keyPair = generator.generateKeyPair();
            }
        } catch (Exception e) {
            Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show();
            Log.e(TAG, Log.getStackTraceString(e));
        }
        refreshKeys();
    }
Удаление ключа из KeyStore
     public void deleteKey(final String alias) {
        AlertDialog alertDialog =new AlertDialog.Builder(this)
                .setTitle("Delete Key")
                .setMessage("Do you want to delete the key \"" + alias + "\" from the keystore?")
                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        try {
                            keyStore.deleteEntry(alias);
                            refreshKeys();
                        } catch (KeyStoreException e) {
                            Toast.makeText(MainActivity.this,
                                    "Exception " + e.getMessage() + " occured",
                                    Toast.LENGTH_LONG).show();
                            Log.e(TAG, Log.getStackTraceString(e));
                        }
                        dialog.dismiss();
                    }
                })
                .setNegativeButton("No", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                })
                .create();
        alertDialog.show();
    }
Применения ключа для шифрования
    public void encryptString(String alias) {
        try {
            KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
            RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();

            // Encrypt the text
            String initialText = startText.getText().toString();
            if(initialText.isEmpty()) {
                Toast.makeText(this, "Enter text in the 'Initial Text' widget", Toast.LENGTH_LONG).show();
                return;
            }

            Cipher input = Cipher.getInstance("RSA/CBC/PKCS7Padding", "AndroidOpenSSL");
            input.init(Cipher.ENCRYPT_MODE, publicKey);

            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            CipherOutputStream cipherOutputStream = new CipherOutputStream(
                    outputStream, input);
            cipherOutputStream.write(initialText.getBytes("UTF-8"));
            cipherOutputStream.close();

            byte [] vals = outputStream.toByteArray();
            encryptedText.setText(Base64.encodeToString(vals, Base64.DEFAULT));
        } catch (Exception e) {
            Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show();
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }
Применения ключа для расшифровки
     public void decryptString(String alias) {
        try {
            KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
            RSAPrivateKey privateKey = (RSAPrivateKey) privateKeyEntry.getPrivateKey();

            Cipher output = Cipher.getInstance("RSA/CBC/PKCS7Padding", "AndroidOpenSSL");
            output.init(Cipher.DECRYPT_MODE, privateKey);

            String cipherText = encryptedText.getText().toString();
            CipherInputStream cipherInputStream = new CipherInputStream(
                    new ByteArrayInputStream(Base64.decode(cipherText, Base64.DEFAULT)), output);
            ArrayList 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();
            }

            String finalText = new String(bytes, 0, bytes.length, "UTF-8");
            decryptedText.setText(finalText);

        } catch (Exception e) {
            Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show();
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }
Предыдущая Доступное на чтение хранилище ключей со слабым паролем, содержащее открытые ключи
Следующая Использование файлового хранилища ключей
Содержание: