различное поведение кода в модульном тесте и во время выполнения - PullRequest
0 голосов
/ 22 ноября 2018

Тот же код работает во время выполнения и не работает в тесте

Есть такой код

private fun generatePrivateKeyFromText(key: String): Key {
    val kf = KeyFactory.getInstance("RSA")
    val keySpecPKCS8 = PKCS8EncodedKeySpec(Base64.decodeBase64(key))
    return kf.generatePrivate(keySpecPKCS8)
}

Когда я запускаю или отлаживаю приложение, оно работает нормально, но этот код не работает на generatePrivate , в то время как тестирование

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence

@Test
fun decrypt() {
    val encrypt = "MoRxCpLJNqxfXGVeU73zZFi+X2j2TLUTyIn1XRqCoEfeN8rNBR/YrEtumAz+8/0AaEsvx0+qTilfbw+edZd8Wfum4McWQ8oWXifvWLgoXybhxWUmCdi2fwA9Gw0parY6CSNYUDA2UuLrLLaDGMz/Jj4s4XmXKp5zuec1zXVdrPM="
    val prkey = "MIICXAIBAAKBgQCAaTCQOxAZPfTXfgel2MMPUCLAf32TxfsXu71/c3mVFGtDPB+7IpmxCmEvAv6nlq1ymX1zRR5gIzNt4DZoM0RhE58eoksUXcmFcRnMX5V4bnI8DitHLdB2EZzdvnPX0Umzs+tE7I1ouDIocNQRsEoQeDcNPfz5av2zMPsg0Xl/yQIDAQABAoGAV8UOX5cvQsGZZ+2J1q8ZbI8OodrCf83z+V3mgYXxVZe2VSd0XNmiiWMZ2CNI4k3YUhtdpvtYbsfAsFpvdbuNAXMW82Zwsd0oluPzzoLELGkFvaUJlh2YGmizrBnEwvF0usJYwjsjUbXw3o1xKX8ILk5FBfdr2+L65YIIZ0UhqoECQQD/B0P8iZhoOTx7myhwlFCuVeSadwaOMGy2CgXRLvTFAtstz8YVO+D+yPKsEpAvMlFgEnkTt7tl8DRxMpKnfmO5AkEAgOZudjUD72xWfSJpCEcX76WGSASWE+fLCZ8C/HumIZ+0trW5/bsmBrI/6SldEJcy4b2bHh2nOggC/6R5rEUkkQJAAg779IDj0wuLOnAxLl90G0QkOT72tZUce4evLlYTsbdpL4B619cI5OWYV906frcIQx9DDO6xu4vp0HQZDPMPOQJAOVbH8ntY2ctmmdmRwWXmpusJ1cV8gTROJGSArpHOcAycFd628sCqhLYMKgsFZBjuQG7YrsfgGLdxpgijO1eykQJBAOE8+BrQwFWyOcgnUShPHo8mDOBkeplGr9VZdnWktac2aBr1Biovy+pipUyjSCAaFgOsSU0FDcK0I5ulTOpgMRg="
    val decrypt = CryptoService.decrypt(encrypt, prkey)
    assertEquals("Pika-pika", decrypt)
}

fun decrypt(ciphertext: String, key: String): String {
    var decodedBytes: ByteArray? = null
    try {
        val c = Cipher.getInstance("RSA")
        c.init(Cipher.DECRYPT_MODE, generatePrivateKeyFromText(key))
        decodedBytes = c.doFinal(Base64.decodeBase64(ciphertext))

    } catch (e: Exception) {
        Log.e("Crypto", "RSA decryption error: $e")
    }
    return String(decodedBytes ?: ByteArray(0))
}

Рабочая функция во фрагменте

 private fun testCrypto() {
        val encrypt = "MoRxCpLJNqxfXGVeU73zZFi+X2j2TLUTyIn1XRqCoEfeN8rNBR/YrEtumAz+8/0AaEsvx0+qTilfbw+edZd8Wfum4McWQ8oWXifvWLgoXybhxWUmCdi2fwA9Gw0parY6CSNYUDA2UuLrLLaDGMz/Jj4s4XmXKp5zuec1zXVdrPM="
        val prkey = "MIICXAIBAAKBgQCAaTCQOxAZPfTXfgel2MMPUCLAf32TxfsXu71/c3mVFGtDPB+7IpmxCmEvAv6nlq1ymX1zRR5gIzNt4DZoM0RhE58eoksUXcmFcRnMX5V4bnI8DitHLdB2EZzdvnPX0Umzs+tE7I1ouDIocNQRsEoQeDcNPfz5av2zMPsg0Xl/yQIDAQABAoGAV8UOX5cvQsGZZ+2J1q8ZbI8OodrCf83z+V3mgYXxVZe2VSd0XNmiiWMZ2CNI4k3YUhtdpvtYbsfAsFpvdbuNAXMW82Zwsd0oluPzzoLELGkFvaUJlh2YGmizrBnEwvF0usJYwjsjUbXw3o1xKX8ILk5FBfdr2+L65YIIZ0UhqoECQQD/B0P8iZhoOTx7myhwlFCuVeSadwaOMGy2CgXRLvTFAtstz8YVO+D+yPKsEpAvMlFgEnkTt7tl8DRxMpKnfmO5AkEAgOZudjUD72xWfSJpCEcX76WGSASWE+fLCZ8C/HumIZ+0trW5/bsmBrI/6SldEJcy4b2bHh2nOggC/6R5rEUkkQJAAg779IDj0wuLOnAxLl90G0QkOT72tZUce4evLlYTsbdpL4B619cI5OWYV906frcIQx9DDO6xu4vp0HQZDPMPOQJAOVbH8ntY2ctmmdmRwWXmpusJ1cV8gTROJGSArpHOcAycFd628sCqhLYMKgsFZBjuQG7YrsfgGLdxpgijO1eykQJBAOE8+BrQwFWyOcgnUShPHo8mDOBkeplGr9VZdnWktac2aBr1Biovy+pipUyjSCAaFgOsSU0FDcK0I5ulTOpgMRg="
        val decrypt = CryptoService.decrypt(encrypt, prkey)
        println(decrypt) // "Pika-pika"
    }

Я звоню на onViewCreated


Обновлено:

Я добавил поставщика BC (спасибо, @JamesKPolk)

private fun generatePrivateKeyFromText(key: String): Key {
    Security.addProvider(BouncyCastleProvider())
    val kf = KeyFactory.getInstance(algorithm)
    val keySpecPKCS8 = PKCS8EncodedKeySpec(Base64.decodeBase64(key))
    return kf.generatePrivate(keySpecPKCS8)
}

Но все равно нормально во время выполнения и нет во время тестирования

javax.crypto.BadPaddingException: Decryption error

Так что проблема с другим исполняемым кодом не исчезла.

Какая разница между временем выполнения и тестом, который выдает код?

1 Ответ

0 голосов
/ 22 ноября 2018

Проблема заключается в том, что закрытый ключ не является PKCS8EncodedKeySpec, а является объектом RSAPrivateKey из PKCS # 1.Однако поставщик BC все равно будет декодировать эту ошибку без жалоб.Тем не менее, другие поставщики будут справедливо жаловаться.Я предполагаю, что среда выполнения использует более старую версию Android, где поставщиком по умолчанию является BC, но ваш тест использует более новую версию, где это не так.

Исправление состоит в том, чтобы сделать ваш закрытый ключправильный PKCS8EncodedKeySpec.Кроме того, вы можете явно запросить поставщика «BC».Для этого вам нужно указать «BC» в вызове getInstance (): val keyFactory = KeyFactory.getInstance("RSA", "BC")

Однако обратите внимание, что, похоже, поддержка поставщика BC уже вышла .

Чтобы преобразовать закрытый ключ в формат PKCS # 1, либо оберните верхний и нижний колонтитулы в стиле 'BEGIN RSA PRIVATE KEY' и нижний колонтитул base64, либо декодируйте двоичный объект base64 и поместите его в файл, а затем выполните:

openssl pkcs8 -topk8 -in privkey.pem  -outform der -nocrypt | openssl base64 -A

или

openssl pkcs8 -topk8 -in privkey.der -inform der -nocrypt  | openssl base64 -A

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

val c = Cipher.getInstance("RSA")

, который получает значения по умолчанию для режима и заполнения и, следовательно, является непереносимым, всегда указывайте полную строку преобразования " алгоритм / режим / заполнение " в Cipher.getInstance(),В вашем случае кажется, что данные не дополняются (небезопасный режим), вам нужно что-то вроде

val c = Cipher.getInstance("RSA/ECB/NoPadding")

Однако вам действительно следует использовать правильное рандомизированное заполнение, и в настоящее время это заполнение OAEP.

Резюме

Среда выполнения - это Android, но я думаю, что среда тестирования - это Oracle Java (или, может быть, openjdk).Очевидно, в этих средах есть два критических различия:

  1. Android использует поставщик BC для KeyFactory, который будет обрабатывать закрытые ключи, закодированные в формате PKCS # 1 RSAPrivateKey.Oracle Java поддерживает только ключи, закодированные в PKCS8.
  2. В Android Cipher.getInstance("RSA") по умолчанию Cipher.getInstance("RSA/ECB/NoPadding"), но Oracle Java по умолчанию Cipher.getInstance("RSA/ECB/PKCS1Padding")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...