How to reconstruct 33-byte compressed NIST P-256 p

2019-08-16 22:41发布

问题:

Supposed that the 33 bytes encoded Public Key can be created like this:

Security.addProvider(provider)
val generator = KeyPairGenerator.getInstance("ECDSA")
val ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1")
generator.initialize(ecSpec)
val keyPair = generator.generateKeyPair()
val privateKey = keyPair.private as ECPrivateKey
val publicKey = keyPair.public as ECPublicKey
val publicEncoded = publicKey.q.getEncoded(true)

How can I reconstruct it again on the other side (when I am having only the 33 bytes sent from here)?

I was trying below code:

val publicKey =KeyFactory.getInstance("EC").generatePublic(X509EncodedKeySpec(publicEncoded))

But I guess this is totally wrong, as I am getting the:

java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c000079:ASN.1 encoding routines:OPENSSL_internal:HEADER_TOO_LONG

I was trying also:

val generator = KeyPairGenerator.getInstance("ECDSA")
val ecPublicKey = generator
        .generatePublic(X509EncodedKeySpec((publicEncoded))) as ECPublicKey

But the error is:

java.security.spec.InvalidKeySpecException: encoded key spec not recognised

How to achieve my goal?

回答1:

This should do the job:

import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.spec.ECPublicKeySpec

fun publicKeyFromCompressed(compressedPublicKey: ByteArray): PublicKey {
    val ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1")
    val point = ecSpec.curve.decodePoint(compressedPublicKey)
    val publicKeySpec = ECPublicKeySpec(point, ecSpec)
    val keyFactory = KeyFactory.getInstance("ECDSA")
    val publicKey = keyFactory.generatePublic(publicKeySpec)
    return publicKey
}


回答2:

The primary problem is that your publicEncoded is not an encoded public key, but an encoded ECPoint (publicKey.q). This means you need to first reconstruct the point and then provide the appropriate curve to reconstruct the key to obtain the correct ECPublicKeySpec.

  1. First re-obtain the chosen curve spec with ECNamedCurveTable.getParameterSpec("secp256r1"). You can then use ecSpec.curve.decodePoint(publicEncoded) to reconstruct the BC ECPoint instance.
  2. Turn the BouncyCastle ECNamedCurveParameterSpec into the java.security.spec.ECParameterSpec and the BouncyCastle ECPoint into the java java.security.spec.ECPoint. Then construct the appropriate ECPublicKeySpec which can then be used by the ECDSA key generator to recreate the complete PublicKey.

References:

  • ECPoint.getEncoded() / ECCurve.decodePoint(byte[])
  • ECNamedCurveParameterSpec / ECPublicKeySpec
  • ECPublicKeySpec(ECPoint, ECParameterSpec)