3

I need to generate such public key and do the additional signing of the bytes (which will include this generated previously key)

I need to construct bytes of: ASN.1 prefix + signature of (33-byte compressed NIST P-256 public key)

The signature should be delivered from other defined private key

The ECDSA specifications:

● Curve:

NIST P-256 Otherwise known as secp256r1 and prime256v1 (openssl)

● Signature format ASN.1. The r and s values of the ECDSA signature must be positive integers, and DER-encoded as such.

Is there API in Android to do such process? How can I use it then?

WHAT I've tried:

 try {
        val generator = KeyPairGenerator.getInstance("ECDSA")
        val ecSpec = ECNamedCurveTable
                .getParameterSpec("prime256v1")
        generator.initialize(ecSpec)
        val keyPair = generator.generateKeyPair()

        val privKey = keyPair.private
        val encodedPrivKey = privKey.encoded
        System.out.println(toHex(encodedPrivKey))

        val pubKey = keyPair.public
        val encodedPubKey = pubKey.encoded
        System.out.println(toHex(encodedPubKey))

        val keyFactory = KeyFactory.getInstance("ECDSA")
        val pubKey2 = keyFactory.generatePublic(X509EncodedKeySpec(encodedPubKey))
        if (Arrays.equals(pubKey2.getEncoded(), encodedPubKey)) {
            println("That worked for the public key")
        }

        val privKey2 = keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedPrivKey))
        if (Arrays.equals(privKey2.getEncoded(), encodedPrivKey)) {
            println("That worked for the private key")
        }

    } catch (e: GeneralSecurityException) {
        throw IllegalStateException(e)
    }

Here - the encoded public key has the lenght of 90 bytes which i guess i want it to be 33 bytes

K.Os
  • 5,123
  • 8
  • 40
  • 95

1 Answers1

3

To encode a Bouncy Castle elliptic curve public key in compressed format:

Security.addProvider(BouncyCastleProvider())  

generator = KeyPairGenerator.getInstance("ECDSA")
val ecSpec = ECNamedCurveTable.getParameterSpec("prime256v1")        
generator.initialize(ecSpec)
val keyPair = generator.generateKeyPair()

val publicKey = keyPair.public as org.bouncycastle.jce.interfaces.ECPublicKey
val compressedPublicKey = publicKey.q.getEncoded(true)

You are not including all the necessary details about how to sign the key and encode the signature, but my best guess would be a standard ECDSA signature on the public key with a standard encoding:

val signer = Signature.getInstance("SHA256withECDSA")
signer.initSign(otherPrivateKey)
signer.update(compressedPublicKey)
val signature = signer.sign()

This hashes the public key using SHA256, signs it using ECDSA and formats and serializes it as the DER encoding of the ASN.1 structure ECDSASignature.

ECDSASignature ::= SEQUENCE {
    r   INTEGER,
    s   INTEGER
}

r and s will be positive integers and encoded with DER. There are other ways of doing so, but this is by far the most common way (the only other common ECDSA signature format is just padding r and s with zeroes and concatenating them).

Rasmus Faber
  • 48,631
  • 24
  • 141
  • 189
  • Thanks, can you explain more if it covers the part that i described in my question: Signature format ASN.1. The r and s values of the ECDSA signature must be positive integers, and DER-encoded as such. – K.Os Dec 05 '18 at 13:30
  • Regarding the signature I will be using "SHA256withECDSA" – K.Os Dec 05 '18 at 13:31
  • @K.Os Perhaps this helps. – Rasmus Faber Dec 05 '18 at 13:50
  • The ASN.1 / DER signature format is X9.62 compatible and is the default for Java. The other format is sometimes called a "flat" signature as the r and s value are not embedded in a hierarchical structure. DER encoded ASN.1 INTEGER values are signed, but the numbers will be padded with zero bytes if the most significant bit is set, so the result is always positive. – Maarten Bodewes Dec 05 '18 at 14:17
  • @K.Os: with regard to the ECDSA signature format, [this answer to the question "How can I convert a DER ECDSA signature to ASN.1?"](https://crypto.stackexchange.com/a/1797/18307) is educational as well. – Reinier Torenbeek Dec 05 '18 at 18:36
  • 1
    @K.Os I think linehrr already have answered this correctly. Ping me again if his solution doesn't work. – Rasmus Faber Dec 06 '18 at 08:31
  • @RasmusFaber thanks - IMPORTANT NOTE to my question: Can you provide specifically what dependencies i need to be able to invoke publicKey.q.getEncoded(true)? I switch the machines and now cannot resolve this issue – K.Os Dec 06 '18 at 13:45
  • @RasmusFaber I cannot pass the "true" boolean to the getEncoded() method – K.Os Dec 06 '18 at 14:04
  • @K.Os It looks like you have figured it out yourself, but just to be sure, I think the reason you couldn't call getEncoded(Boolean) is because you were casting to the wrong ECPublicKey. I have updated the answer to use the full class name. – Rasmus Faber Dec 07 '18 at 09:18
  • @RasmusFaber Hi again, maybe you can help me again with https://stackoverflow.com/questions/53706767/how-to-reconstruct-33-byte-compressed-nist-p-256-public-key-from-33-bytes? – K.Os Dec 10 '18 at 13:33