RSA 加密 Java/Kotlin

Ray*_*kud 4 java encryption rsa kotlin

我一直在尝试使用预制公钥加密 Kotlin/Java 中的一个简单字符串,但没有成功。这是我目前正在做的事情,评论是我目前尝试过的。

val toEncrypt = "8uUrfe4OcJVUT5lkAP07WKrlGhIlAAwTRwAksBztVaa0hHdZp50EFjOmhrAmFsLQ"
val publicKeyRaw =
        "-----BEGIN PUBLIC KEY-----\n" +
                "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAW4WQxF2/qzqYwoQlwkkQIjQJ\n" +
                "hCm2Hjl00QGkxeO12Py+jytTNYAopHCPpR4SbhE1cFdYx1qjEnFbgeJBxFENyqDg\n" +
                "GvBhlwrWQXfI9LdA2M3xbr/4wur7ph1c+aQxOpImzslCtHJ5df7cyFrOTnkY+XYY\n" +
                "yGK2Fsnu67FKWjgVvQIDAQAB\n" +
                "-----END PUBLIC KEY-----"

val reader = PemReader(StringReader(publicKeyRaw))
val pemObject = reader.readPemObject()
val keyBytes: ByteArray = pemObject.content
val keySpec: EncodedKeySpec = X509EncodedKeySpec(keyBytes)
val keyFactory = KeyFactory.getInstance("RSA")
val key = keyFactory.generatePublic(keySpec)
val cipher = Cipher.getInstance("RSA")
cipher.init(Cipher.ENCRYPT_MODE, key)
val cipherData: ByteArray = cipher.doFinal(toEncrypt.toByteArray())

val encryptedData =  Base64.encodeToString(cipherData, Base64.DEFAULT)

Log.e("TAG", "encryptedData: $encryptedData")
Run Code Online (Sandbox Code Playgroud)

这是我已经尝试过的代码:

/*
    val publicKey = publicKeyRaw.replace("\n", "")
        .replace("\\n", "")
        .replace("-----BEGIN PUBLIC KEY-----", "")
        .replace("-----END PUBLIC KEY-----", "")
   */

    /*
    val pemParser = PEMParser(StringReader(publicKeyRaw))
    val pemKeyPair : PEMKeyPair = pemParser.readObject() as PEMKeyPair
    val key = JcaPEMKeyConverter().getPublicKey(pemKeyPair.publicKeyInfo)
    */

/*
   val keyFactory = KeyFactory.getInstance("RSA")
   val keyBytes: ByteArray = Base64.decode(publicKey.toByteArray(), Base64.DEFAULT)
   val spec = X509EncodedKeySpec(keyBytes)
   val fileGeneratedPublicKey = keyFactory.generatePublic(spec)
   val rsaPub: RSAPublicKey = fileGeneratedPublicKey as RSAPublicKey
   val publicKeyModulus: BigInteger = rsaPub.modulus
   val publicKeyExponent: BigInteger = rsaPub.publicExponent


   val keyFactoryAlt = KeyFactory.getInstance("RSA")
   val pubKeySpec = RSAPublicKeySpec(publicKeyModulus, publicKeyExponent)
   val key = keyFactoryAlt.generatePublic(pubKeySpec) as RSAPublicKey
*/

/*
    val reader = PemReader(StringReader(publicKeyRaw))
    val pemObject = reader.readPemObject()
    val keyBytes: ByteArray = pemObject.content
    val keySpec: EncodedKeySpec = X509EncodedKeySpec(keyBytes)
    val keyFactory = KeyFactory.getInstance("RSA")
    val key = keyFactory.generatePublic(keySpec)
    */
/*
    val keyFactory = KeyFactory.getInstance("RSA")
    val keyBytes: ByteArray = Base64.decode(publicKey.toByteArray(), Base64.DEFAULT)
    val spec = X509EncodedKeySpec(keyBytes)
    val fileGeneratedPublicKey = keyFactory.generatePublic(spec)
    val rsaPub: RSAPublicKey = fileGeneratedPublicKey as RSAPublicKey
    val publicKeyModulus: BigInteger = rsaPub.modulus
    val publicKeyExponent: BigInteger = rsaPub.publicExponent
    */

/*
val pemParser = PEMParser(StringReader(publicKey))
val pemKeyPair : PEMKeyPair = pemParser.readObject() as PEMKeyPair
val encoded : ByteArray = pemKeyPair.publicKeyInfo.encoded
val keyFactory = KeyFactory.getInstance("RSA")
val key = keyFactory.generatePublic(PKCS8EncodedKeySpec(encoded))
 */
Run Code Online (Sandbox Code Playgroud)

它实际上生成一个字符串,但是当使用以下工具时: https: //8gwifi.org/rsafunctions.jsp 它显示一个错误,它无效,即使很困难我在那里生成了密钥大小为 1024 的密钥

错误

我的问题是:如何在 Java/Kotlin 中使用这种密钥进行加密。(您可以在您喜欢的任何网站或提供的网站上生成此类密钥)

这是我用过的一对:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAW4WQxF2/qzqYwoQlwkkQIjQJ
hCm2Hjl00QGkxeO12Py+jytTNYAopHCPpR4SbhE1cFdYx1qjEnFbgeJBxFENyqDg
GvBhlwrWQXfI9LdA2M3xbr/4wur7ph1c+aQxOpImzslCtHJ5df7cyFrOTnkY+XYY
yGK2Fsnu67FKWjgVvQIDAQAB
-----END PUBLIC KEY-----



-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCAW4WQxF2/qzqYwoQlwkkQIjQJhCm2Hjl00QGkxeO12Py+jytT
NYAopHCPpR4SbhE1cFdYx1qjEnFbgeJBxFENyqDgGvBhlwrWQXfI9LdA2M3xbr/4
wur7ph1c+aQxOpImzslCtHJ5df7cyFrOTnkY+XYYyGK2Fsnu67FKWjgVvQIDAQAB
AoGActBq8wmTSiVh7s7f4d6d+D6ACZscrHjwsBtcuwUAIOONgO8TtASBNNmSjgsG
kTm/TuvEVfdMjd2rZE0UE/wE+2BOoHTlkVjcKMxoM8KbP/4RBDYlYmWTseiS8zmp
dGwchOzsoWKnhZtnvMrki0f1SdMq4J6g9RncFIrUSKWJ1MECQQDp0s4v+sKo423X
2YSAhB8j1LMPoRlioXSmvVrHGINzGoHt2tRvGqqHaHbd/9QkkhpfeeBcdrv/xOaH
fVH08dnJAkEAjIgFRe6QEDNvm1qCRx6ata047N188MxdHgKHwQBsv48dxqljQrFS
N1yEfXsv6PjLk3DCD8Wi3FTOgftpZVeWVQJBAIpc+TABJkGEW1KYX8Ug6cBtNAxy
my/3NK0abeZUxixNqkcS8BRS5kg8c+KIaYO+hSasWyy8AiGm5XeVm/LjTqkCQEGQ
dGVcF/p3BOsGHyHvNV7tolFgRJpTvl3x8EQrXpFAxDObc6P59tG9aFLi1kdrTA9N
3DxfiMwjBPW/xjxx0MECQBtaSSfTNUYBP64+evjY4HaV9GI5AK83webyF73axXIq
4dyadIdIo78Yaz+f2myX7vyfUlU5iM8QuPMN2KCM3CE=
-----END RSA PRIVATE KEY-----
Run Code Online (Sandbox Code Playgroud)

这是我使用的代码: https: //github.com/Raykud/TestEncryption

编辑:这是生成的加密文本。

NO_WRAP: c6nQMEFIrOWsPjB6W00DC6+5xaKm8R79bu8xLz9+yYhDTDepkiQGh0fWpyJuldNJit5CyL9n73TQxMjmtqsZsR/sAGEFjk7EGj8etwFO4MKpZY55BX1MsOVbWbfo2x31uCb/Ssd6nJnu897yCD5Md7xKqbovZP8eoZrvp2azFOk=

DEFAULT: 
c6nQMEFIrOWsPjB6W00DC6+5xaKm8R79bu8xLz9+yYhDTDepkiQGh0fWpyJuldNJit5CyL9n73TQ
xMjmtqsZsR/sAGEFjk7EGj8etwFO4MKpZY55BX1MsOVbWbfo2x31uCb/Ssd6nJnu897yCD5Md7xK
qbovZP8eoZrvp2azFOk=
Run Code Online (Sandbox Code Playgroud)

Top*_*aco 7

问题的原因是使用了不同的填充。

如果不应用填充,则可以复制(使用发布的公钥)或解密(使用发布的私钥)发布的密文(RSA/ECB/NoPadding请参阅此处)。这种 RSA 变体称为教科书 RSA,不应该在实践中使用,因为它不安全。该网站应用PKCS#1 v1.5 padding(前三个选项)或OAEP(后三个选项),完全不支持不安全的教科书RSA。即填充不兼容,因此解密失败。

有两种方法可以指定加密Cipher#getInstance,完整变体算法/模式/填充或短变体算法,请参见此处。在后者中,模式和填充由特定于提供者的默认值确定。由于它们是特定于提供商的,因此它们在不同的环境中可能会有所不同,这可能会导致跨平台问题,就像在本例中一样。这就是为什么应始终使用完整版本的原因!

Cipher#getInstance("RSA")显然在您的环境中应用了教科书 RSA,即没有填充。我可以在 Android Studio(API 级别 28)中重现此行为。相反,在 Eclipse(Kotlin 插件 0.8.14)中使用 PKCS#1 v1.5 填充。

所以问题的解决方案是根据使用的环境显式指定填充,例如对于 PKCS#1 v1.5 填充通常使用RSA/ECB/PKCS1PaddingRSA/NONE/PKCS1Padding,请参见此处。请注意,方案算法/模式/填充可用于对称和非对称加密。虽然操作模式是为对称加密定义的,但通常没有为非对称加密(例如 RSA)定义,即 ECB 在 RSA 上下文中没有任何意义,但仍被某些提供商在规范中使用。

另一个可能的问题是网站无法处理换行符,但它不会自动删除换行符,因此如果密文包含换行符,解密就会失败。该选项Base64.DEFAULT在 76 个字符后生成换行符。因此,在使用网站对密文进行解密之前,必须将这些内容删除(例如手动)。或者,Base64.NO_WRAP可以使用,它在一行上生成密文。