¿Las claves públicas RSA originales y restauradas son idénticas, pero sus representaciones codificadas en X509 no lo son?
Estoy generando claves públicas y privadas RSA de 2048 bits, codificándolas en formato X509, convirtiendo el resultado al formato PKCS#1, codificando eso como una cadena Base64 y guardándolo en una base de datos. Luego, recupero la cadena de la base de datos, la restauro al formato PKCS#1, la convierto al formato X509 y luego restauro el objeto de clave pública.
Cuando llamo println() en los objetos de clave pública original y restaurado, confirmo que tienen el mismo módulo y exponente público. Cuando compruebo su igualdad con .equals(), la aserción devuelve verdadero. Sin embargo, cuando comparo la representación codificada en X509 del objeto de clave pública original y la del objeto de clave pública restaurado, son diferentes. Esto es problemático para mí porque uso un hash derivado de este valor para identificar las claves.
¿Por qué está sucediendo esto y cómo puedo solucionarlo?
Debo almacenar las claves en la base de datos en formato PKCS#1 porque el archivo de la base de datos se trasladará a un dispositivo iOS y las claves se restaurarán allí, y el marco de seguridad de Apple solo permite la exportación o importación de claves RSA en formato PKCS#1.
Estoy usando Kotlin en JVM; el código a continuación reproduce el problema:
import org.bouncycastle.asn1.ASN1Primitive
import org.bouncycastle.asn1.DERNull
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import java.security.KeyFactory
import java.security.KeyPair
import java.security.KeyPairGenerator
import java.security.PublicKey
import java.security.spec.X509EncodedKeySpec
import java.util.*
import kotlin.test.Test
class IssueExample {
@Test
fun demonstrateProblem() {
//Generate RSA key pair
val keyPairGenerator: KeyPairGenerator = KeyPairGenerator.getInstance("RSA")
keyPairGenerator.initialize(2048)
val keyPair = keyPairGenerator.generateKeyPair()
//Convert public key to X509, then to PKCS1 format
val pubKeyX509Bytes = keyPair.public.encoded
val sPubKeyInfo: SubjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(pubKeyX509Bytes)
val pubKeyPrimitive: ASN1Primitive = sPubKeyInfo.parsePublicKey()
val pubKeyPkcs1Bytes: ByteArray = pubKeyPrimitive.encoded
//Convert PKCS1 to Base64 string
val encoder = Base64.getEncoder()
val base64PubKey = encoder.encodeToString(pubKeyX509Bytes)
//Restore Base64 string to PKCS#1
val decoder = Base64.getDecoder()
val restoredPubKeyPKCS1Bytes = decoder.decode(base64PubKey)
//Restore PKCS#1 to X509
val algorithmIdentifier: AlgorithmIdentifier = AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
val restoredPubKeyX509Bytes: ByteArray = SubjectPublicKeyInfo(algorithmIdentifier, pubKeyPkcs1Bytes).encoded
//Restore X509 to public key
val restoredPubKey: PublicKey = KeyFactory.getInstance("RSA").generatePublic(X509EncodedKeySpec(pubKeyX509Bytes))
//Comparing the two public key objects returns true
println(restoredPubKey.equals(keyPair.public))
//And calling println() yields identical results for both
println(restoredPubKey)
println(keyPair.public)
//Yet their X509 representations return false!!
println(restoredPubKey.encoded.equals(keyPair.public.encoded))
println(restoredPubKey.encoded)
println(keyPair.public.encoded)
}
}
davy.ai
El problema es que estás restaurando la cadena codificada en Base64 al formato PKCS # 1 en lugar de restaurarla al formato X509. Esto significa que la clave restaurada no está en el mismo formato que la clave original.
Para solucionar esto, debes restaurar la cadena codificada en Base64 al formato PKCS # 1, como lo estás haciendo actualmente, y luego convertirla al formato X509 antes de restaurarla al objeto de clave pública. Para hacer esto, reemplaza la línea:
`
`val restoredPubKeyX509Bytes: ByteArray = SubjectPublicKeyInfo(algorithmIdentifier, pubKeyPkcs1Bytes).encoded
con:
`
`val restoredPubKeyX509Bytes: ByteArray = SubjectPublicKeyInfo.getInstance(pubKeyPkcs1Bytes).encoded
Esto asegurará que la clave restaurada esté en el formato correcto antes de ser convertida en un objeto de clave pública.
Una vez que hagas este cambio, la representación codificada en X509 de los objetos de clave pública original y restaurada debe ser la misma, y deberías poder utilizar el hash derivado de este valor para identificar las claves.
Aquí está el código modificado:
“ `
import org.bouncycastle.asn1.ASN1Primitive
import org.bouncycastle.asn1.DERNull
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import java.security.KeyFactory
import java.security.KeyPair
import java.security.KeyPairGenerator
import java.security.PublicKey
import java.security.spec.X509EncodedKeySpec
import java.util.*
import kotlin.test.Test
class IssueExample {
}
“`