Как мне RSA зашифровать строку с помощью открытого текста, используя Java BouncyCastle API на Android - PullRequest
3 голосов
/ 29 августа 2011

Я пытаюсь зашифровать строку с помощью API BouncyCastle в Android для отправки на сервер.

У меня есть открытый ключ в открытом виде (в памяти, но не в файловой системе, разумеется! Нет необходимостикричать на меня, криптографы;)) и мне нужно использовать этот открытый открытый ключ для шифрования строки в зашифрованную строку RSA.

Это мой класс:

public class RSAEncryptor {
//Get certificate from base64 string
public static X509Certificate getCertificateFromBase64String(String string) 
        throws CertificateException, javax.security.cert.CertificateException 
{
    if(string.equals("") || string == null) {
        return null;
    }

    byte[] certBytes = Base64.decode(string.getBytes(), Base64.DEFAULT);

    X509Certificate cert = X509Certificate.getInstance(certBytes);

    return cert;
}

//Get public key from base64 encoded string
public static PublicKey getPublicKeyFromEncodedCertData(String encodedCertData) 
    throws CertificateException, javax.security.cert.CertificateException 
{
    if(encodedCertData == null || encodedCertData.equals("")) return null;

    X509Certificate cert = getCertificateFromBase64String(encodedCertData);

    if(cert == null) return null;

    return cert.getPublicKey();
}

public static String rsaEncrypt(String plainText, String keyFromResources)
        throws NoSuchAlgorithmException, InvalidKeySpecException,
        IOException, NoSuchPaddingException, InvalidKeyException,
        IllegalBlockSizeException, BadPaddingException //
{
    if(plainText == null || plainText.equals("")) return null;
    if(keyFromResources == null || keyFromResources.equals("")) return null;

    byte[] encryptedBytes;

    Cipher cipher = Cipher.getInstance("RSA");
    PublicKey publicKey = null;

    try {
        publicKey = getPublicKeyFromEncodedCertData(keyFromResources);
    }
    catch(Exception ex) {
        Logger.LogError("getPublicKeyFromEncodedCertData()", ex);
    }

    cipher.init(Cipher.ENCRYPT_MODE, publicKey);

    encryptedBytes = cipher.doFinal(plainText.getBytes());
    String encrypted = new String(encryptedBytes);
    return encrypted;
}

}

В настоящее время я не получаю обратно должным образом зашифрованную строку, просто искаженный беспорядок, подобный этому:

��RB��%����I��Q��F*�bd[@�y�_H]T{KƾuTN�Q� ��U�f��]�S �q|.t�t�9�Rˇ�����)��{�},ޱ�ª�ǥ#���@k=�WO���f�7t"yP�z�

(<?> - нулевые символы)

Я должен получить буквенно-цифровую строку, похожую на эту:

2+tSXez8JrAIX+VJ2Dy4IsA56XhWpTwF8X2yGGaI6novucXknwykDyqJZICpmYcqx75qBRgxwrW2kY9LmQR2xU17PLqTukAu2Bna8WXYTmJJQ7CWsN3SdABlETRfsYA+g3A2rO2Qp6aR9OCBcFVJpnZJjb9kaOUj5Pcj0tNPFdM= (очевидно, изменено по сравнению с фактическим ответом: D)

Буду признателен за любую помощь!

Спасибо!

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

1 Ответ

4 голосов
/ 29 августа 2011

Проблемы в коде:

String encrypted = new String(encryptedBytes);

Плохая идея! Cipher#doFinal возвращает byte[] по уважительной причине. Это похоже на случайные данные - превращение этого в String наверняка приведет к путанице, потому что кодировка платформы по умолчанию (UTF-8 в большинстве случаев) будет интерпретировать случайные байты неправильно почти наверняка. Так что если вы хотите получить строку из зашифрованных данных, то вам нужно кодировать Base64- или Hex-код байтового массива.

Из того, что вы сказали, что ожидали, я бы сказал, что вам нужны данные в кодировке Base64, поэтому вы должны кодировать Base64 вывод вашего шифра.

Тогда шифрование строки (это настоящий, читаемый человеком текст?) Также не оптимально. Сильно уязвимый, уменьшенная энтропия плюс характеристики режима ECB (это используется шифром RSA) резко снижают безопасность вашего решения.

Простой шифр RSA никогда не должен использоваться для шифрования данных, размер которых превышает один блок (то есть больше, чем размер ключа вашего ключа RSA), а также только в том случае, если данные криптографически защищены случайным образом. В 99% всех случаев это дается только для симметричных ключей, используемых для симметричных шифров, таких как AES и т. Д.

Используйте RSA только для шифрования симметричных ключей и цифровых подписей. Во всех остальных случаях, когда вы хотите фактически зашифровать конфиденциальные данные, вы используете симметричный шифр, AES - хороший выбор - 128 или 256 бит не имеет большого значения .

Рабочий процесс будет выглядеть так:

Создание симметричного ключа для AES (16/32 байта, если вы используете AES-128/256). Теперь вы будете RSA-шифровать этот симметричный ключ и ничего более, используя открытый ключ сервера и отправлять ключ на сервер, а затем шифровать ваши личные данные с использованием AES и симметричного ключа, сервер расшифрует симметричный ключ используя его личный ключ RSA и расшифровывайте отправленные на него пакеты.

Использовать TLS:

Обратите внимание, что я использовал . Вышесказанное - только часть истории. То, что вы только что изобрели, - это ключевой транспортный протокол. И если вы не спроектируете их для живого, высоки шансы, что вы не получите такой защиты с первой попытки (как в атаках «Человек посередине», «Атаки на отражение», «Атаки воспроизведения» ...).

Вот почему, на мой взгляд, широко распространенная защищенная опция only для установки безопасного канала между клиентским устройством и сервером заключается в использовании TLS (прежний SSL). Протокол был разработан специально для обмена частными данными с односторонней (только для сервера) или двухсторонней аутентификацией (клиент и сервер) (аутентификация - это та часть, для которой вы будете использовать RSA в вашем случае - настройка «сертификата сервера» «).

Он годами укреплялся и несколько раз пересматривался, чтобы противостоять всем известным атакам на такие протоколы. И я знаю, что каждый день появляются сообщения о том, как «SSL» был нарушен тем или иным человеком, но, тем не менее, если вы настроите его осторожно, он будет настолько же безопасным, насколько это возможно для простых смертных, не имеющих большого опыта в разработке протоколов.

И приятно то, что вам нужно только настроить его на сервере (что довольно легко по сравнению с созданием протокола с нуля), чтобы иметь возможность использовать полностью зашифрованную и безопасную связь между клиентом и сервером. Если вы настроили пару сертификат / ключ на сервере, который был куплен у «общедоступного ЦС», то использование TSL полностью прозрачно для ваших клиентов - они просто меняют свой URL-адрес доступа с «http» на «https» - сертификат сервера будет быть автоматически доверенным, имея возможность идентифицировать его в пути сертификата, который ведет к одному из корневых сертификатов, хранящихся в хранилище доверия Java по умолчанию cacerts.

...