代码之家  ›  专栏  ›  技术社区  ›  Kirill Matrosov YWILLS

单元测试和运行时中不同代码的行为

  •  0
  • Kirill Matrosov YWILLS  · 技术社区  · 6 年前

    相同的代码在运行时工作,在测试中不工作

    有这样的代码

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

    当我运行或调试应用程序时,它工作正常,但此代码在运行时失败 虽然 测试

    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"
        }
    


    更新:

    我添加了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 回复  |  直到 6 年前
        1
  •  1
  •   President James K. Polk    6 年前

    问题是私钥是无效的 PKCS8EncodedKeySpec,而是来自PKCS#1的RSAPrivateKey对象。但是,BC提供程序仍将毫无怨言地解码此错误。然而,其他供应商也会提出合理的投诉。我的猜测是,运行时使用的是旧版本的Android,默认提供程序是BC,但您的测试使用的是新版本,而实际情况并非如此。

    val keyFactory = KeyFactory.getInstance("RSA", "BC")

    但是,请注意,似乎 BC provider support is on its way out .

    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-provider-for-KeyFactory来处理以PKCS#1 RSAPrivateKey格式编码的私钥。Oracle Java仅支持PKCS8编码的密钥。
    2. 在安卓系统中, Cipher.getInstance("RSA") 默认为 Cipher.getInstance("RSA/ECB/NoPadding") ,但Oracle Java默认为 Cipher.getInstance("RSA/ECB/PKCS1Padding")