7878from .ecdsa import RSZeroError
7979from .util import string_to_number , number_to_string , randrange
8080from .util import sigencode_string , sigdecode_string
81- from .util import oid_ecPublicKey , encoded_oid_ecPublicKey , MalformedSignature
81+ from .util import (
82+ oid_ecPublicKey ,
83+ encoded_oid_ecPublicKey ,
84+ oid_ecDH ,
85+ oid_ecMQV ,
86+ MalformedSignature ,
87+ )
8288from ._compat import normalise_bytes
8389
8490
@@ -841,16 +847,13 @@ def from_pem(cls, string, hashfunc=sha1):
841847 """
842848 Initialise from key stored in :term:`PEM` format.
843849
844- Note, the only PEM format supported is the un-encrypted RFC5915
845- (the sslay format) supported by OpenSSL, the more common PKCS#8 format
846- is NOT supported (see:
847- https://github.com/warner/python-ecdsa/issues/113 )
848-
849- ``openssl ec -in pkcs8.pem -out sslay.pem`` can be used to
850- convert PKCS#8 file to this legacy format.
850+ The PEM formats supported are the un-encrypted RFC5915
851+ (the sslay format) supported by OpenSSL, and the more common RFC5958
852+ (the PKCS #8 format).
851853
852854 The legacy format files have the header with the string
853855 ``BEGIN EC PRIVATE KEY``.
856+ PKCS#8 files have the header ``BEGIN PRIVATE KEY``.
854857 Encrypted files (ones that include the string
855858 ``Proc-Type: 4,ENCRYPTED``
856859 right after the PEM header) are not supported.
@@ -870,30 +873,36 @@ def from_pem(cls, string, hashfunc=sha1):
870873 :return: Initialised SigningKey object
871874 :rtype: SigningKey
872875 """
873- # the privkey pem may have multiple sections, commonly it also has
874- # "EC PARAMETERS", we need just "EC PRIVATE KEY".
875876 if not PY2 and isinstance (string , str ):
876877 string = string .encode ()
877- privkey_pem = string [
878- string .index (b ("-----BEGIN EC PRIVATE KEY-----" )) :
879- ]
880- return cls .from_der (der .unpem (privkey_pem ), hashfunc )
878+
879+ # the privkey pem may have multiple sections, commonly it also has
880+ # "EC PARAMETERS", we need just "EC PRIVATE KEY".
881+ ec_private_key_index = string .find (b"-----BEGIN EC PRIVATE KEY-----" )
882+ if ec_private_key_index != - 1 :
883+ return cls .from_der (
884+ der .unpem (string [ec_private_key_index :]), hashfunc , pkcs8 = False
885+ )
886+
887+ private_key_index = string .find (b"-----BEGIN PRIVATE KEY-----" )
888+ if private_key_index != - 1 :
889+ return cls .from_der (
890+ der .unpem (string [private_key_index :]), hashfunc , pkcs8 = True
891+ )
892+
893+ raise ValueError ("No EC PRIVATE KEY or PRIVATE KEY section in PEM" )
881894
882895 @classmethod
883- def from_der (cls , string , hashfunc = sha1 ):
896+ def from_der (cls , string , hashfunc = sha1 , pkcs8 = False ):
884897 """
885898 Initialise from key stored in :term:`DER` format.
886899
887- Note, the only DER format supported is the RFC5915
888- (the sslay format) supported by OpenSSL, the more common PKCS#8 format
889- is NOT supported (see:
890- https://github.com/warner/python-ecdsa/issues/113 )
900+ The DER formats supported are the un-encrypted RFC5915
901+ (the sslay format) supported by OpenSSL, and the more common RFC5958
902+ (the PKCS #8 format).
891903
892- ``openssl ec -in pkcs8.pem -outform der -out sslay.der`` can be
893- used to convert PKCS#8 file to this legacy format.
894-
895- The encoding of the ASN.1 object in those files follows following
896- syntax specified in RFC5915::
904+ Both formats contain an ASN.1 object following the syntax specified
905+ in RFC5915::
897906
898907 ECPrivateKey ::= SEQUENCE {
899908 version INTEGER { ecPrivkeyVer1(1) }} (ecPrivkeyVer1),
@@ -904,16 +913,31 @@ def from_der(cls, string, hashfunc=sha1):
904913
905914 The only format supported for the `parameters` field is the named
906915 curve method. Explicit encoding of curve parameters is not supported.
907-
908- While `parameters` field is defined as optional, this implementation
909- requires its presence for correct parsing of the keys.
916+ In the legacy sslay format, this implementation requires the optional
917+ `parameters` field to get the curve name.
918+
919+ The PKCS #8 format includes this object as the `privateKey` field
920+ within a larger structure:
921+
922+ OneAsymmetricKey ::= SEQUENCE {
923+ version Version,
924+ privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
925+ privateKey PrivateKey,
926+ attributes [0] Attributes OPTIONAL,
927+ ...,
928+ [[2: publicKey [1] PublicKey OPTIONAL ]],
929+ ...
930+ }
910931
911932 `publicKey` field is ignored completely (errors, if any, in it will
912933 be undetected).
913934
914935 :param string: binary string with DER-encoded private ECDSA key
915936 :type string: bytes like object
916937
938+ :param pkcs8: whether to expect the data in PKCS #8 format
939+ :type pkcs8: boolean
940+
917941 :raises MalformedPointError: if the length of encoding doesn't match
918942 the provided curve or the encoded values is too large
919943 :raises RuntimeError: if the generation of public key from private
@@ -923,8 +947,48 @@ def from_der(cls, string, hashfunc=sha1):
923947 :return: Initialised SigningKey object
924948 :rtype: SigningKey
925949 """
926- string = normalise_bytes (string )
927- s , empty = der .remove_sequence (string )
950+ s = normalise_bytes (string )
951+ curve = None
952+
953+ # PKCS #8 has the algorithm identifier, including the curve name,
954+ # before the actual key. Then it contains the key data within an
955+ # octet string.
956+ if pkcs8 :
957+ s , empty = der .remove_sequence (s )
958+ if empty != b ("" ):
959+ raise der .UnexpectedDER (
960+ "trailing junk after DER privkey: %s"
961+ % binascii .hexlify (empty )
962+ )
963+
964+ version , s = der .remove_integer (s )
965+ if version != 0 and version != 1 :
966+ raise der .UnexpectedDER (
967+ "expected '0' or '1' at start of DER privkey, got %d"
968+ % version
969+ )
970+
971+ algorithm_identifier , s = der .remove_sequence (s )
972+ algorithm_oid , algorithm_identifier = der .remove_object (
973+ algorithm_identifier
974+ )
975+ curve_oid , empty = der .remove_object (algorithm_identifier )
976+ curve = find_curve (curve_oid )
977+
978+ if algorithm_oid not in (oid_ecPublicKey , oid_ecDH , oid_ecMQV ):
979+ raise der .UnexpectedDER (
980+ "unexpected algorithm identifier '%s'" % algorithm_oid
981+ )
982+ if empty != b"" :
983+ raise der .UnexpectedDER (
984+ "unexpected data after algorithm identifier: %s"
985+ % binascii .hexlify (empty )
986+ )
987+
988+ # We don't care about the optional fields after the key data.
989+ s , _ = der .remove_octet_string (s )
990+
991+ s , empty = der .remove_sequence (s )
928992 if empty != b ("" ):
929993 raise der .UnexpectedDER (
930994 "trailing junk after DER privkey: %s" % binascii .hexlify (empty )
@@ -935,18 +999,20 @@ def from_der(cls, string, hashfunc=sha1):
935999 "expected '1' at start of DER privkey, got %d" % one
9361000 )
9371001 privkey_str , s = der .remove_octet_string (s )
938- tag , curve_oid_str , s = der .remove_constructed (s )
939- if tag != 0 :
940- raise der .UnexpectedDER (
941- "expected tag 0 in DER privkey, got %d" % tag
942- )
943- curve_oid , empty = der .remove_object (curve_oid_str )
944- if empty != b ("" ):
945- raise der .UnexpectedDER (
946- "trailing junk after DER privkey "
947- "curve_oid: %s" % binascii .hexlify (empty )
948- )
949- curve = find_curve (curve_oid )
1002+
1003+ if not curve :
1004+ tag , curve_oid_str , s = der .remove_constructed (s )
1005+ if tag != 0 :
1006+ raise der .UnexpectedDER (
1007+ "expected tag 0 in DER privkey, got %d" % tag
1008+ )
1009+ curve_oid , empty = der .remove_object (curve_oid_str )
1010+ if empty != b ("" ):
1011+ raise der .UnexpectedDER (
1012+ "trailing junk after DER privkey "
1013+ "curve_oid: %s" % binascii .hexlify (empty )
1014+ )
1015+ curve = find_curve (curve_oid )
9501016
9511017 # we don't actually care about the following fields
9521018 #
0 commit comments