RSA加密Java/Kotlin

huangapple go评论63阅读模式
英文:

RSA Encryption Java/Kotlin

问题

I have translated the code for you as requested:

我一直在尝试使用预先生成的公钥在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")

请注意,这是代码的翻译部分,不包括任何其他内容。如果您需要更多帮助,请告诉我。

英文:

I been trying to encrypt a simple string in Kotlin/Java with a premade public key but I've had no success.
This is what I'm currently doing and commented is what I've currently tried.

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

Here is the code I've already tried:

/*
    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))
 */

and it actually generates a String but when using tools like: https://8gwifi.org/rsafunctions.jsp
it shows an error that it's not valid, even tough I generated the key there with a 1024 key size

RSA加密Java/Kotlin

My question is: How to cypher with that kind of key in Java/Kotlin. (you may generate that kind of key on any site you like or the site provided)

here is a pair I used:

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

here is the code I used: https://github.com/Raykud/TestEncryption

Edit: This is the generated encrypted text.

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

DEFAULT: 
c6nQMEFIrOWsPjB6W00DC6+5xaKm8R79bu8xLz9+yYhDTDepkiQGh0fWpyJuldNJit5CyL9n73TQ
xMjmtqsZsR/sAGEFjk7EGj8etwFO4MKpZY55BX1MsOVbWbfo2x31uCb/Ssd6nJnu897yCD5Md7xK
qbovZP8eoZrvp2azFOk=

答案1

得分: 7

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

如果不应用任何填充 (RSA/ECB/NoPadding,参见这里),则可以重现(使用发布的公钥)或解密(使用发布的私钥)所发布的密文。这种RSA变体称为教科书式RSA,在实践中不应该使用,因为它是不安全的。网站应用PKCS#1 v1.5填充(前三个选项)或OAEP(最后三个选项),不支持不安全的教科书式RSA。即填充方式不兼容,因此解密会失败。

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

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

因此,解决问题的方法是根据使用的环境明确指定填充方式,例如通常使用RSA/ECB/PKCS1PaddingRSA/NONE/PKCS1Padding来进行PKCS#1 v1.5填充,参见这里。请注意,算法/模式/填充 方案用于对称和非对称加密。虽然对称加密的模式已经定义,但通常不会为非对称加密(如RSA)定义模式,即在RSA的上下文中ECB没有意义,但仍然被一些提供程序用于规范。

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

英文:

The cause of your problem is that different paddings are used.

The posted ciphertext can be reproduced (with the posted public key) or decrypted (with the posted private key) if no padding is applied (RSA/ECB/NoPadding, see here). This RSA variant is called textbook RSA and shouldn't be used in practice because it's insecure. The website applies PKCS#1 v1.5 padding (the first three options) or OAEP (the last three options), the insecure textbook RSA is not supported at all. I.e. the paddings are incompatible and decryption therefore fails.

There are two ways to specify the encryption with Cipher#getInstance, the full variant algorithm/mode/padding or the short variant algorithm, see here. In the latter, mode and padding are determined by provider-specific default values. And because they are provider specific, they can be different in different environments, which can lead to cross-platform problems, as in this case. That is why the full variant should always be used!

Cipher#getInstance("RSA") obviously applies textbook RSA in your environment, i.e. no padding. I can reproduce this behavior e.g. in Android Studio (API level 28). In contrast, in Eclipse (Kotlin plugin 0.8.14) PKCS#1 v1.5 padding is used.

So the solution to the problem is to explicitly specify the padding according to the environment used, e.g. for PKCS#1 v1.5 padding usually with RSA/ECB/PKCS1Padding or RSA/NONE/PKCS1Padding, see here. Note that the scheme algorithm/mode/padding is used for both symmetric and asymmetric encryption. While the mode of operation is defined for symmetric encryption, it's generally not defined for asymmetric encryptionsuch as RSA, i.e. ECB has no meaning in the context of RSA, but is still used by some providers on the specification.

Another possible problem is that the website can't handle line breaks, but it doesn't remove them automatically, so decryption fails if the ciphertext contains line breaks. The option Base64.DEFAULT generates line breaks after 76 characters. These must therefore be removed (e.g. manually) before the ciphertext is decrypted using the website. Alternatively, Base64.NO_WRAP can be used, which produces the ciphertext on a single line.

huangapple
  • 本文由 发表于 2020年7月31日 09:52:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/63184699.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定