2

I have been looking at Coinapult's API documentation which details the use of PEM format in python-ecdsa. Specifically, private keys and public keys need to be formatted using PEM.

Let's take Coinapult's specified public key:

-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEWp9wd4EuLhIZNaoUgZxQztSjrbqgTT0w
LBq8RwigNE6nOOXFEoGCjGfekugjrHWHUi8ms7bcfrowpaJKqMfZXg==
-----END PUBLIC KEY-----

Using python-ecdsa:

vk = ecdsa.VerifyingKey.from_pem(ECC_COINAPULT_PUB)
s = vk.to_string()
s.encode('hex')   
>>> "5a9f7077812e2e121935aa14819c50ced4a3adbaa04d3d302c1abc4708a0344ea738e5c51281828c67de92e823ac7587522f26b3b6dc7eba30a5a24aa8c7d95e"

Or if it were a Bitcoin public key, that'd be 045a9f7077812e2e121935aa14819c50ced4a3adbaa04d3d302c1abc4708a0344ea738e5c51281828c67de92e823ac7587522f26b3b6dc7eba30a5a24aa8c7d95e

OK, that makes sense, but when we take the base64 encoded data between the leading and trailing -----BEGIN-----:

base64_data = "".join(ECC_COINAPULT_PUB[27:-26 ].split('\n'))
s = base64.b64decode(base64_data)
result = s.encode("hex")
>>> "3056301006072a8648ce3d020106052b8104000a034200045a9f7077812e2e121935aa14819c50ced4a3adbaa04d3d302c1abc4708a0344ea738e5c51281828c67de92e823ac7587522f26b3b6dc7eba30a5a24aa8c7d95e"  # WTF is this??

Note that the PEM format decodes to some sort of DER string, 3056301006072a8648ce3d020106052b8104000a034200045a9f7077812e2e121935aa14819c50ced4a3adbaa04d3d302c1abc4708a0344ea738e5c51281828c67de92e823ac7587522f26b3b6dc7eba30a5a24aa8c7d95e, but what on earth is this DER string? I see 5a9f7077....a8c7d95e is in that string, so what are the leading bytes meaning?

Questions: 1. How do I encode a pubkey to PEM format without using python-ecdsa? 2. Is it the same procedure in #1 for private keys as it is public keys?

1 Answer 1

6

#0 what is the format? As you partly guessed, that is the "PEM" armoring of the DER encoding of the SubjectPublicKeyInfo ASN.1 type from X.509, republished for Internet use as RFC5280 section 4.1 particularly 4.1.2.7. To support multiple algorithms including new ones in the future, this structure is a SEQUENCE of an AlgorithmIdentifier which identifies the algorithm (using an OBJECT IDENTIFIER for the algorithm and various types for parameters depending on the algorithm), followed by a BIT STRING that contains the key value encoded in a format that varies depending on the algorithm. The AlgId and key format specific to ECDSA (and ECDH) are in RFC3279 section 2.3.5 although you can ignore the complicated parts about ECParameters because in practice everyone uses the simpler namedCurve OBJECT IDENTIFIER option, and the key format is just the SEC1/X9.62 point.

Here 301006072a8648ce3d020106052b8104000a is the AlgId and decodes as

    0:d=0  hl=2 l=  16 cons: SEQUENCE
    2:d=1  hl=2 l=   7 prim: OBJECT            :id-ecPublicKey
   11:d=1  hl=2 l=   5 prim: OBJECT            :secp256k1

(As you've already figured out, "PEM" is just base64 of the DER, with line breaks at intervals, plus a ----BEGIN whatever----- line at the top and an -----END whatever----- line at the bottom.)

#1 encode without python? Aside from implementing (part of) DER yourself, which isn't that hard if you want, I see the page you link to talks a lot about OpenSSL compatibility: for programs in C or languages that can (easily) call C, which is many, OpenSSL can generate, read, write, and use for various operations ECC keys in this format (and the un-armored DER form). Java 7+ cryptography can do it, but the standard providers only in DER; for PEM you have to en/de-armor yourself or use http://www.BouncyCastle.org instead. I expect dot-NET can do it, but I don't use that and can't say definitely. I know I've seen perl modules for DER, but somehow I can never find things in CPAN even though that's its purpose. If you want a more specific answer, revise your question (or if it's sufficiently different, ask a new one).

#2 private key? That page says it uses -----BEGIN EC PRIVATE KEY----- which could be either of two 'legacy' formats in OpenSSL: unencrypted or encrypted. Since the examples don't show any password, I'll assume the former. If you look at a key file and see lines about Proc-type and DEK-info inserted after the -----BEGIN line but before the base64 data, it's legacy-encrypted and that's more complicated.

Without that, it's the private key structure defined by the SEC1 document at http://www.secg.org/ in C.4, which contains an OCTET STRING for the private key (actually an integer, but not encoded as such), parameters defined in C.2 which if you examine carefully are essentially equivalent to those in RFC 3279, and once again a BIT STRING containing the public key (a point).

OpenSSL also supports the "new" (as of about 2000!) PKCS#8 format for private keys. Much like X.509 SPKI for public key, this is a generic wrapper that is basically an AlgorithmIdentifier for the algorithm plus an OCTET STRING containing the key value depending on the algorithm, except that PKCS#8 also has an option for encrypting at the PKCS#8 level (as opposed to the "PEM" level). I don't know if the python module handles these formats or not, and if not, whether you would want to use them and thereby be incompatible with the python module. If you don't want to use PKCS#8 and do want to use OpenSSL, be careful to use the specific EC routines not the generic ones.

3
  • Thanks! I figured it out eventually: der_string = "\x30{totallen}\x02\x01\x01\x04\x20{privkey}\xa0\x07\x06\x05\x2b\x81\x04\x00\x0a\xa1\x44\x03\x42\x00\x04{x}{y}" is for privkeys, and der_string = "\x30{totallen}\x30\x10\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x05\x2b\x81\x04\x00\x0a\x03\x42\x00\x04{pubkey}" for pubkeys Commented Dec 7, 2015 at 8:57
  • Thanks, this shed a lot of light on things! Just one question: Is the Bitcoin public key needed at all in the message signing process, when using DER encoded PEM-files to sign the message?
    – Kebman
    Commented Sep 27, 2017 at 17:45
  • 1
    @Kebman: ECDSA signing needs only privatekey not public. But Bitcoin doesn't sign messages, it signs transactions which (always or nearly) involve addresses which are hashed from the publickeys. EC privatekey files created by OpenSSL always include publickey, even though not required by SEC1 (and PKCS8), and in any case the publickey can always be recomputed from the privatekey. Commented Sep 29, 2017 at 4:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.