RSA 加密 Java/Kotlin
RSA Encryption Java/Kotlin
我一直在尝试使用预制的 public 密钥对 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")
这是我已经尝试过的代码:
/*
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))
*/
它实际上生成了一个字符串,但是当使用像这样的工具时: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-----
这是我使用的代码:https://github.com/Raykud/TestEncryption
编辑:这是生成的密文。
NO_WRAP: c6nQMEFIrOWsPjB6W00DC6+5xaKm8R79bu8xLz9+yYhDTDepkiQGh0fWpyJuldNJit5CyL9n73TQxMjmtqsZsR/sAGEFjk7EGj8etwFO4MKpZY55BX1MsOVbWbfo2x31uCb/Ssd6nJnu897yCD5Md7xKqbovZP8eoZrvp2azFOk=
DEFAULT:
c6nQMEFIrOWsPjB6W00DC6+5xaKm8R79bu8xLz9+yYhDTDepkiQGh0fWpyJuldNJit5CyL9n73TQ
xMjmtqsZsR/sAGEFjk7EGj8etwFO4MKpZY55BX1MsOVbWbfo2x31uCb/Ssd6nJnu897yCD5Md7xK
qbovZP8eoZrvp2azFOk=
问题的原因是使用了不同的填充。
如果应用无填充(RSA/ECB/NoPadding
,请参阅 here). This RSA variant is called textbook RSA and shouldn't be used in practice because it's insecure. The website 应用 PKCS#1 v1.5 填充(前三个选项)或 OAEP(后三个选项),不支持不安全的教科书 RSA。即填充不兼容,因此解密失败。
有两种方法可以使用 Cipher#getInstance
指定加密,完整变体 algorithm/mode/padding 或短变体 algorithm,参见 here。在后者中,模式和填充由提供者特定的默认值决定。而且由于它们是特定于提供商的,因此它们在不同的环境中可能会有所不同,这可能会导致跨平台问题,如本例所示。这就是应该始终使用完整变体的原因!
Cipher#getInstance("RSA")
显然在您的环境中应用教科书 RSA,即没有填充。我可以重现这种行为,例如在 Android 工作室(API 28 级)。相比之下,在 Eclipse(Kotlin 插件 0.8.14)中使用 PKCS#1 v1.5 填充。
所以解决问题的方法是根据使用的环境显式指定padding,例如对于通常使用 RSA/ECB/PKCS1Padding
或 RSA/NONE/PKCS1Padding
的 PKCS#1 v1.5 填充,请参阅 here。请注意,方案 algorithm/mode/padding 用于对称和非对称加密。虽然操作模式是为对称加密定义的,但它通常不为非对称加密定义,例如 RSA,即 ECB 在 RSA 的上下文中没有意义,但仍被一些提供商在规范中使用。
另外一个可能的问题是网站无法处理换行符,但不会自动去除换行符,所以如果密文中有换行符,解密就会失败。选项 Base64.DEFAULT
在 76 个字符后生成换行符。因此,在使用网站解密密文之前,必须删除(例如手动)这些内容。或者,可以使用 Base64.NO_WRAP
,它会在一行中生成密文。
我一直在尝试使用预制的 public 密钥对 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")
这是我已经尝试过的代码:
/*
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))
*/
它实际上生成了一个字符串,但是当使用像这样的工具时: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-----
这是我使用的代码:https://github.com/Raykud/TestEncryption
编辑:这是生成的密文。
NO_WRAP: c6nQMEFIrOWsPjB6W00DC6+5xaKm8R79bu8xLz9+yYhDTDepkiQGh0fWpyJuldNJit5CyL9n73TQxMjmtqsZsR/sAGEFjk7EGj8etwFO4MKpZY55BX1MsOVbWbfo2x31uCb/Ssd6nJnu897yCD5Md7xKqbovZP8eoZrvp2azFOk=
DEFAULT:
c6nQMEFIrOWsPjB6W00DC6+5xaKm8R79bu8xLz9+yYhDTDepkiQGh0fWpyJuldNJit5CyL9n73TQ
xMjmtqsZsR/sAGEFjk7EGj8etwFO4MKpZY55BX1MsOVbWbfo2x31uCb/Ssd6nJnu897yCD5Md7xK
qbovZP8eoZrvp2azFOk=
问题的原因是使用了不同的填充。
如果应用无填充(RSA/ECB/NoPadding
,请参阅 here). This RSA variant is called textbook RSA and shouldn't be used in practice because it's insecure. The website 应用 PKCS#1 v1.5 填充(前三个选项)或 OAEP(后三个选项),不支持不安全的教科书 RSA。即填充不兼容,因此解密失败。
有两种方法可以使用 Cipher#getInstance
指定加密,完整变体 algorithm/mode/padding 或短变体 algorithm,参见 here。在后者中,模式和填充由提供者特定的默认值决定。而且由于它们是特定于提供商的,因此它们在不同的环境中可能会有所不同,这可能会导致跨平台问题,如本例所示。这就是应该始终使用完整变体的原因!
Cipher#getInstance("RSA")
显然在您的环境中应用教科书 RSA,即没有填充。我可以重现这种行为,例如在 Android 工作室(API 28 级)。相比之下,在 Eclipse(Kotlin 插件 0.8.14)中使用 PKCS#1 v1.5 填充。
所以解决问题的方法是根据使用的环境显式指定padding,例如对于通常使用 RSA/ECB/PKCS1Padding
或 RSA/NONE/PKCS1Padding
的 PKCS#1 v1.5 填充,请参阅 here。请注意,方案 algorithm/mode/padding 用于对称和非对称加密。虽然操作模式是为对称加密定义的,但它通常不为非对称加密定义,例如 RSA,即 ECB 在 RSA 的上下文中没有意义,但仍被一些提供商在规范中使用。
另外一个可能的问题是网站无法处理换行符,但不会自动去除换行符,所以如果密文中有换行符,解密就会失败。选项 Base64.DEFAULT
在 76 个字符后生成换行符。因此,在使用网站解密密文之前,必须删除(例如手动)这些内容。或者,可以使用 Base64.NO_WRAP
,它会在一行中生成密文。