Dear all, all versions of OpenSSL do not encode private EC key correctly. This shows up every time the private key is at least one byte shorter than the order. If the private key has full length then the encoding looks correct.
Consider the following three ECPrivateKey encondings:
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/oAoGCCqGSM49
AwEHoUQDQgAE9Es5dZoubbcjpvkCSZct/QjpU4Dx/KRw6s0dA+Xt8hS++vzPIjyg
ZfCg207qk/8GohFvyoH3pKlDao2RegLe3g==
-----END EC PRIVATE KEY-----
-----BEGIN EC PRIVATE KEY-----
MGYCAQEEDwAAAAAAAAAAAAAAAAAA/6AKBggqhkjOPQMBB6FEA0IABPRLOXWaLm23
I6b5AkmXLf0I6VOA8fykcOrNHQPl7fIUvvr8zyI8oGXwoNtO6pP/BqIRb8qB96Sp
Q2qNkXoC3t4=
-----END EC PRIVATE KEY-----
-----BEGIN EC PRIVATE KEY-----
MFgCAQEEAf+gCgYIKoZIzj0DAQehRANCAAT0Szl1mi5ttyOm+QJJly39COlTgPH8
pHDqzR0D5e3yFL76/M8iPKBl8KDbTuqT/waiEW/KgfekqUNqjZF6At7e
-----END EC PRIVATE KEY-----
Despite the different base64 encodings all the corresponding private keys
are the same, and therefore any signature made by one can be verified by
any other. You may try it out:
openssl dgst -sign key1.pem -out ec.sig test.txt
openssl dgst prverify key2.pem -signature ec.sig test.txt
Try also the OpenSSL "ec" command with the -text option on these keys. You
can see that the private key is always the same (0xFF) and that OpenSSL
recodes the keys. The PEM output is always identical to the last of the
three.
This is not correct. The SEC1 specification OpenSSL is using requires the
full encoding in byte length of the order as e.g. given by the first of
these three encodings.
The patch that works is given as annex. It is applicable to 1.0.1j as well
as to 1.0.2-beta3. There is no strong need to change any other things,
even the documentation ec.pod is already correct (beside the description
of the very strange -modulus option in line 96++.
Attached is also a deeper analysis made by an answer to Douglas E Engert,
who replies to my e-mail on openssl-dev with a different but
non-appropriate subject line (EC key generation is broken in all
versions). Sorry for this misleading subject line, not the key generation
is broken, but the encoding.
Regards,
/Ann.
1.0.2-beta3.ec_pk_enc.patch
Description: Binary data
Hi Douglas,
thank you for pointing me to PCKS#15 encoding of PrivateECKeyAttributes and
thanks for spending time on this issue.
Sorry for the long answer, there were many points to be considered.
Keep in mind: The proposed patch does not change any bits on the wire.
Kind regards,
/Ann.
1. PrivateECKeyAttributes vs. ECPrivateKey
OpenSSL generates an EC key with the command
openssl ecparam -genkey -noout -name prime256v1
not in the PKCS#15 format but according to a SEC1 structure (cf.
crypto/ec/ec_asn1.c line 191), which is defined in
http://www.secg.org/sec1-v2.pdf (version 2.0 Annex C.4 p.108):
ECPrivateKey ::= SEQUENCE {
version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
privateKey OCTET STRING,
parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL,
publicKey [1] BIT STRING OPTIONAL
}
The ASN.1 definition which is used by OpenSSL can be found in ec_asn1.c too
(cf. lines 266-271):
ASN1_SEQUENCE(EC_PRIVATEKEY) = {
ASN1_SIMPLE(EC_PRIVATEKEY, version, LONG),
ASN1_SIMPLE(EC_PRIVATEKEY, privateKey, ASN1_OCTET_STRING),
ASN1_EXP_OPT(EC_PRIVATEKEY, parameters, ECPKPARAMETERS, 0),
ASN1_EXP_OPT(EC_PRIVATEKEY, publicKey, ASN1_BIT_STRING, 1)
} ASN1_SEQUENCE_END(EC_PRIVATEKEY)
So OpenSSL uses the structure of SEC1 and refers to SEC1 (cf. also
doc/apps/ec.pod lines 31++).
Why not to follow consequently the requirement of SEC1 (see v2.0 p. 109)?
-----BEGIN LaTeX-----
\item The component \texttt{privateKey} is the private key defined to be the
octet string of length $\lceil \log_2 n/8\rceil$ (where $n$ is the order of the
curve) obtained from the unsigned integer via the encoding of Section 2.3.7.
-----END LaTeX-----
This is almost the same text as given in RFC 5915. The encoding to be used is
given in the Section 2.3.7 of SEC1. The specifcations in PKCS#15 and RFC 3447
are not applicable here.
2. SEC1 vs. RFC 5480
Note the subtle difference in the definitions of SEC1 and RFC 5915. SEC1 uses
ECDomainParameters{{ SECGCurveNames }}, restricted to SECGCurveNames, whereas
RFC 5480 and RFC 5915 use (unrestricted) ECParameters, defined in RFC 5480.
Note also that the corrected definition of RFC5915 is
ECPrivateKey ::= SEQUENCE {
version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
privateKey OCTET STRING,
parameters [0] ECParameters OPTIONAL,
publicKey [1] BIT STRING OPTIONAL
}
because the type ECParameters defined in RFC 5480 is not parametrized (see
http://www.rfc-editor.org/errata_search.php?rfc=5915).
OpenSSL uses the definition of RFC 3279 (ECpkParameters) obsoleted by RFC 5480,
which changes only the naming but not the bits on the wire ("ECpkParameters" of
RFC 3279 becomes "ECParameters" in RFC 5480, and "ECParameters" of RFC 3279
becomes "SpecifiedECDomain" in RFC 5480 according to SEC1v2).
3. Issue #3465 of RT
http://rt.openssl.org/Ticket/Display.html?id=3465&user=guest&pass=guest
This is *not* releated to the wrong private key encoding. It addresses the fact
that the OpenSSL command "ec" fails to parse a generic (without parameters
component) ECPrivateKey. But this is not failure at all, because the OpenSSL
command ec is certainly restricted to the structures generated and used by
OpenSSL itself. OpenSSL does not claim that it parses or accepts an arbitrary
ECPrivateKey structure.
The Doctor said "A private key without parameters is unusable anyway".
Additionally the RFC 5915 mandates the use of the parameters component (RFC
5915 p. 3):
Though the ASN.1 indicates that the parameters field is
OPTIONAL, implementations that conform to this document MUST
always include the parameters field.
Therefore OpenSSL is fully conformant with the RFC 5915 (in this case
Truth be told, OpenSSL fails to parse an ECPrivateKey structure even if the
parameters field is present but is implicitCurve (aka implicitCA aka NULL).
This is in line with RFC 5480 which deprecates the use of NULL parameter in
PKIX:
implicitCurve allows the elliptic curve domain parameters
to be inherited. This choice MUST NOT be used.
But for OpenSSL a private key with NULL parameters is unusable too.
Note that OpenSSL can handle keys with the specifiedCurve parameters and also
ECPrivateKey with the optional publicKey field missing.
4. Recoding of private keys
Consider the following "three" ECPrivateKeys:
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/oAoGCCqGSM49
AwEHoUQDQgAE9Es5dZoubbcjpvkCSZct/QjpU4Dx/KRw6s0dA+Xt8hS++vzPIjyg
ZfCg207qk/8GohFvyoH3pKlDao2RegLe3g==
-----END EC PRIVATE KEY-----
-----BEGIN EC PRIVATE KEY-----
MGYCAQEEDwAAAAAAAAAAAAAAAAAA/6AKBggqhkjOPQMBB6FEA0IABPRLOXWaLm23
I6b5AkmXLf0I6VOA8fykcOrNHQPl7fIUvvr8zyI8oGXwoNtO6pP/BqIRb8qB96Sp
Q2qNkXoC3t4=
-----END EC PRIVATE KEY-----
-----BEGIN EC PRIVATE KEY-----
MFgCAQEEAf+gCgYIKoZIzj0DAQehRANCAAT0Szl1mi5ttyOm+QJJly39COlTgPH8
pHDqzR0D5e3yFL76/M8iPKBl8KDbTuqT/waiEW/KgfekqUNqjZF6At7e
-----END EC PRIVATE KEY-----
Despite the different base64 encodings all the corresponding private keys are
the same, so any signature made by one can be verified by any other:
openssl dgst -sign key1.pem -out ec.sig test.txt
openssl dgst prverify key2.pem -signature ec.sig test.txt
Try out the OpenSSL "ec" command with the -text option. You can see that
OpenSSL recodes the keys and the PEM output is always the last of them.
But this is the wrong one. Only the first encoding conforms to the
specification.
This recoding must be corrected to solve the contradiction of description and
realization. To conform to its own specification OpenSSL shall recode an
ECPrivateKey into the first of the given three variants (fixed length OCTET
STRING). The patch that works is given as annex. It is applicable to 1.0.1j as
well as to 1.0.2-beta3. There is no strong need to change any other things even
the documentation ec.pod is already correct (beside the description of the very
strange -modulus option in line 96++ .
Please check the patches carefully.
5. full SEC1/RFC5480 conformance
To acchieve a complete SEC1/RFC5480 conformance some identifiers should be
renamed. Apply the following sed commands (in this order) to all files in apps
and crypto directory:
s/ec_parameters_st/ec_specifiedDomain_st/g
s/ecpk_parameters_st/ec_parameters_st/g
s/implicitlyCA/implicitCurve/g
s/ECPARAMETERS/SPECIFIEDCURVE/g
s/ECParameters/SpecifiedCurve/g
s/ECPKPARAMETERS/ECPARAMETERS/g
s/ECPKParameters/ECParameters/g
s/ecpkparameters/ecparameters/g
s/parameters2group/specifiedCurve2group/g
s/group2parameters/group2specifiedCurve/g
s/pkparameters2group/ecparameters2group/g
s/group2pkparameters/group2ecparameters/g
There are two header files to be considered (ec.h and pem.h) carefully. Here
the old names may be kept as aliases because there are no name collisions, if
I'm not wrong. I will provide the full patch on request.
Note also that this renaming does not change any bits on the wire, too. _______________________________________________ openssl-dev mailing list [email protected] https://mta.opensslfoundation.net/mailman/listinfo/openssl-dev
