Расшифровать данные с помощью RSA между PHP и Java Проблема с Android

Я использую сервер PHP для шифрования некоторых данных, а затем расшифровываю их на устройстве Android. Но когда я пытаюсь расшифровать его на стороне устройства Android, я получаю следующую ошибку:

javax.crypto.BadPaddingException: ошибка: 0407106B: подпрограммы rsa: RSA_padding_check_PKCS1_type_2: тип блока не 02

Когда я нахожусь

Cipher.getInstance("RSA/ECB/PKCS1Padding");

Я шифрую значение на сервере PHP, используя библиотеку PHPSeclips (последняя версия github на данный момент), а также подписываю. Эта часть действительно работает, потому что уже использовалась для декодирования в программе Javacard, поэтому ошибка на самом деле не здесь.

Вот как я действую на стороне Android:

protected byte[] decryptData(String alias, byte[] data) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, BadPaddingException, IllegalBlockSizeException, UnsupportedOperationException {
        Log.i(TAG, "decryptData() Decrypt data " + HexStringConverter.byteArrayToHexString(data));
        byte[] decryptedData = null;
        PrivateKey privateKey = getPrivateKey(alias);
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        decryptedData = cipher.doFinal(data);
        Log.i(TAG, "decryptData() Decrypted data: " + HexStringConverter.byteArrayToHexString(decryptedData));
        return decryptedData;
    }

С помощью метода getPrivateKey():

protected RSAPrivateKey getPrivateKey(String alias) {
        try {
            KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
            ks.load(null);
            KeyStore.Entry entry = ks.getEntry(alias, null);
            if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
                Log.w(TAG, "getPrivateKey() Not an instance of a PrivateKeyEntry");
            }
            return (RSAPrivateKey) ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();
        } catch (NoSuchAlgorithmException e) {
            Log.w(TAG, "getPrivateKey() ", e);
        } catch (KeyStoreException e) {
            Log.w(TAG, "getPrivateKey() ", e);
        } catch (CertificateException e) {
            Log.w(TAG, "getPrivateKey() ", e);
        } catch (IOException e) {
            Log.w(TAG, "getPrivateKey() ", e);
        } catch (UnrecoverableEntryException e) {
            Log.w(TAG, "getPrivateKey() ", e);
        }
        return null;
    }

И как это зашифровано на стороне PHP:

//Function for encrypting with RSA
function rsa_encrypt($string, $key)
{
    require_once(__DIR__ . '/../phpseclib/Crypt/RSA.php');
    //Create an instance of the RSA cypher and load the key into it
    $cipher = new Crypt_RSA();
    $cipher->loadKey($key);
    //Set the encryption mode
    $cipher->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
    //Return the encrypted version
    return $cipher->encrypt($string);
}

//Function for decrypting with RSA 
function rsa_sign($string, $key)
{
    require_once(__DIR__ . '/../phpseclib/Crypt/RSA.php');
    //Create an instance of the RSA cypher and load the key into it
    $cipher = new Crypt_RSA();
    $cipher->loadKey($key);
    //Set the signature mode
    $cipher->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);

    //Return the signed message
    return $cipher->sign($string);
}

ОБНОВЛЕНИЕ (7/3/14) Тем, кто повторяет те же ошибки, я рекомендую посмотреть следующую страницу: http://hustoknow.blogspot.ca/2013/01/rsa-block-type-is-not-02-error.html

На самом деле это поставило меня на правильный путь, чтобы узнать, в чем проблема.

Возникшая ошибка говорит о том, что ключ дешифрования не соответствует ключу, используемому для его шифрования, поэтому я просмотрел каждый открытый ключ, используемый для этого обмена. Поскольку я использовал размер ключа 1024, я анализировал входное сообщение (модуль + общедоступная экспонента) с соответствующим размером каждого. И я заметил, что модуль не был получен полностью по сравнению с тем, который отображается на устройстве Android. Итак, вот ошибка, Android по умолчанию использует размер ключа 2048, когда вы используете объект KeyPairGenerator для генерации ключа. Просто установите вручную размер ключа на 1024, чтобы решить эту проблему.

Пример:

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
            Calendar notBefore = Calendar.getInstance();
            Calendar notAfter = Calendar.getInstance();
            notAfter.add(Calendar.YEAR, 1);
            KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(mContext)
                    .setAlias(alias)
                    .setKeySize(1024)
                    .setSubject(
                            new X500Principal(String.format("CN=%s, OU=%s", alias,
                                    mContext.getPackageName())))
                    .setSerialNumber(BigInteger.ONE).setStartDate(notBefore.getTime())
                    .setEndDate(notAfter.getTime()).build();

            keyPairGenerator.initialize(spec);
            KeyPair kp = keyPairGenerator.generateKeyPair();

Надеюсь, это поможет.


person Bibu    schedule 02.07.2014    source источник
comment
не могли бы вы поделиться тем, как вы установили размер ключа на 1024, используя .setKeySize(1024) в KeyPairGeneratorSpec, у меня не работает, у меня такая же проблема.   -  person Donal Rafferty    schedule 25.09.2014
comment
Извините за поздний ответ, я обновил свой пост рабочим кодом для создания пары ключей.   -  person Bibu    schedule 30.11.2014


Ответы (1)


Возможно, вам придется сделать define('CRYPT_RSA_PKCS15_COMPAT', true).

В следующем блоке комментариев уточняется:

https://github.com/phpseclib/phpseclib/blob/a8c2ff0fb013169193c649adab512cafef5068cf/phpseclib/Crypt/RSA.php#L2272

По сути, OpenSSL (и, вполне возможно, Java) реализует PKCS#1 v1.5, тогда как phpseclib реализует PKCS#1 v2.1. PKCS # 1 v2.1 модифицирует шифрование в стиле PKCS1, чтобы использовать рандомизированное заполнение, поэтому два зашифрованных текста никогда не будут одинаковыми.

person neubert    schedule 03.07.2014
comment
Спасибо, neubert, но, как я подозревал, ошибка возникла не из-за шифрования PHP, а на стороне Android. После многих часов я понял, почему это не работает. Как обычно, ошибка была ГЛУПОЙ ошибкой... Я отредактирую свой пост, чтобы детализировать ее. - person Bibu; 03.07.2014
comment
Привет @Bibu, по какой причине это произошло? У меня такая же проблема - person user3239558; 21.04.2017
comment
Это была проблема с длиной ключа, Android был 2048, а серверная часть была 1024. - person Bibu; 21.04.2017