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 ssleay format) supported by OpenSSL, and the more common
852+ un-encrypted RFC5958 (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,29 @@ 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". PKCS#8 should not
881+ # have the "EC PARAMETERS" section; it's just "PRIVATE KEY".
882+ private_key_index = string .find (b"-----BEGIN EC PRIVATE KEY-----" )
883+ if private_key_index == - 1 :
884+ private_key_index = string .index (b"-----BEGIN PRIVATE KEY-----" )
885+
886+ return cls .from_der (der .unpem (string [private_key_index :]), hashfunc )
881887
882888 @classmethod
883889 def from_der (cls , string , hashfunc = sha1 ):
884890 """
885891 Initialise from key stored in :term:`DER` format.
886892
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 )
891-
892- ``openssl ec -in pkcs8.pem -outform der -out sslay.der`` can be
893- used to convert PKCS#8 file to this legacy format.
893+ The DER formats supported are the un-encrypted RFC5915
894+ (the ssleay format) supported by OpenSSL, and the more common
895+ un-encrypted RFC5958 (the PKCS #8 format).
894896
895- The encoding of the ASN.1 object in those files follows following
896- syntax specified in RFC5915::
897+ Both formats contain an ASN.1 object following the syntax specified
898+ in RFC5915::
897899
898900 ECPrivateKey ::= SEQUENCE {
899901 version INTEGER { ecPrivkeyVer1(1) }} (ecPrivkeyVer1),
@@ -902,14 +904,30 @@ def from_der(cls, string, hashfunc=sha1):
902904 publicKey [1] BIT STRING OPTIONAL
903905 }
904906
907+ `publicKey` field is ignored completely (errors, if any, in it will
908+ be undetected).
909+
905910 The only format supported for the `parameters` field is the named
906911 curve method. Explicit encoding of curve parameters is not supported.
912+ In the legacy ssleay format, this implementation requires the optional
913+ `parameters` field to get the curve name. In PKCS #8 format, the curve
914+ is part of the PrivateKeyAlgorithmIdentifier.
915+
916+ The PKCS #8 format includes an ECPrivateKey object as the `privateKey`
917+ field within a larger structure:
918+
919+ OneAsymmetricKey ::= SEQUENCE {
920+ version Version,
921+ privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
922+ privateKey PrivateKey,
923+ attributes [0] Attributes OPTIONAL,
924+ ...,
925+ [[2: publicKey [1] PublicKey OPTIONAL ]],
926+ ...
927+ }
907928
908- While `parameters` field is defined as optional, this implementation
909- requires its presence for correct parsing of the keys.
910-
911- `publicKey` field is ignored completely (errors, if any, in it will
912- be undetected).
929+ The `attributes` and `publicKey` fields are completely ignored; errors
930+ in them will not be detected.
913931
914932 :param string: binary string with DER-encoded private ECDSA key
915933 :type string: bytes like object
@@ -923,30 +941,80 @@ def from_der(cls, string, hashfunc=sha1):
923941 :return: Initialised SigningKey object
924942 :rtype: SigningKey
925943 """
926- string = normalise_bytes (string )
927- s , empty = der .remove_sequence (string )
944+ s = normalise_bytes (string )
945+ curve = None
946+
947+ s , empty = der .remove_sequence (s )
928948 if empty != b ("" ):
929949 raise der .UnexpectedDER (
930950 "trailing junk after DER privkey: %s" % binascii .hexlify (empty )
931951 )
932- one , s = der .remove_integer (s )
933- if one != 1 :
952+
953+ version , s = der .remove_integer (s )
954+
955+ # At this point, PKCS #8 has a sequence containing the algorithm
956+ # identifier and the curve identifier. The ssleay format instead has
957+ # an octet string containing the key data, so this is how we can
958+ # distinguish the two formats.
959+ if der .is_sequence (s ):
960+ if version not in (0 , 1 ):
961+ raise der .UnexpectedDER (
962+ "expected version '0' or '1' at start of privkey, got %d"
963+ % version
964+ )
965+
966+ sequence , s = der .remove_sequence (s )
967+ algorithm_oid , algorithm_identifier = der .remove_object (sequence )
968+ curve_oid , empty = der .remove_object (algorithm_identifier )
969+ curve = find_curve (curve_oid )
970+
971+ if algorithm_oid not in (oid_ecPublicKey , oid_ecDH , oid_ecMQV ):
972+ raise der .UnexpectedDER (
973+ "unexpected algorithm identifier '%s'" % (algorithm_oid ,)
974+ )
975+ if empty != b"" :
976+ raise der .UnexpectedDER (
977+ "unexpected data after algorithm identifier: %s"
978+ % binascii .hexlify (empty )
979+ )
980+
981+ # Up next is an octet string containing an ECPrivateKey. Ignore
982+ # the optional "attributes" and "publicKey" fields that come after.
983+ s , _ = der .remove_octet_string (s )
984+
985+ # Unpack the ECPrivateKey to get to the key data octet string,
986+ # and rejoin the ssleay parsing path.
987+ s , empty = der .remove_sequence (s )
988+ if empty != b ("" ):
989+ raise der .UnexpectedDER (
990+ "trailing junk after DER privkey: %s"
991+ % binascii .hexlify (empty )
992+ )
993+
994+ version , s = der .remove_integer (s )
995+
996+ # The version of the ECPrivateKey must be 1.
997+ if version != 1 :
934998 raise der .UnexpectedDER (
935- "expected '1' at start of DER privkey, got %d" % one
999+ "expected version '1' at start of DER privkey, got %d"
1000+ % version
9361001 )
1002+
9371003 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 )
1004+
1005+ if not curve :
1006+ tag , curve_oid_str , s = der .remove_constructed (s )
1007+ if tag != 0 :
1008+ raise der .UnexpectedDER (
1009+ "expected tag 0 in DER privkey, got %d" % tag
1010+ )
1011+ curve_oid , empty = der .remove_object (curve_oid_str )
1012+ if empty != b ("" ):
1013+ raise der .UnexpectedDER (
1014+ "trailing junk after DER privkey "
1015+ "curve_oid: %s" % binascii .hexlify (empty )
1016+ )
1017+ curve = find_curve (curve_oid )
9501018
9511019 # we don't actually care about the following fields
9521020 #
@@ -981,7 +1049,7 @@ def to_string(self):
9811049 s = number_to_string (secexp , self .privkey .order )
9821050 return s
9831051
984- def to_pem (self , point_encoding = "uncompressed" ):
1052+ def to_pem (self , point_encoding = "uncompressed" , format = "ssleay" ):
9851053 """
9861054 Convert the private key to the :term:`PEM` format.
9871055
@@ -990,9 +1058,11 @@ def to_pem(self, point_encoding="uncompressed"):
9901058 Only the named curve format is supported.
9911059 The public key will be included in generated string.
9921060
993- The PEM header will specify ``BEGIN EC PRIVATE KEY``
1061+ The PEM header will specify ``BEGIN EC PRIVATE KEY`` or
1062+ ``BEGIN PRIVATE KEY``, depending on the desired format.
9941063
9951064 :param str point_encoding: format to use for encoding public point
1065+ :param str format: either ``ssleay`` (default) or ``pkcs8``
9961066
9971067 :return: PEM encoded private key
9981068 :rtype: bytes
@@ -1001,9 +1071,11 @@ def to_pem(self, point_encoding="uncompressed"):
10011071 re-encoded if the system is incompatible (e.g. uses UTF-16)
10021072 """
10031073 # TODO: "BEGIN ECPARAMETERS"
1004- return der .topem (self .to_der (point_encoding ), "EC PRIVATE KEY" )
1074+ assert format in ("ssleay" , "pkcs8" )
1075+ header = "EC PRIVATE KEY" if format == "ssleay" else "PRIVATE KEY"
1076+ return der .topem (self .to_der (point_encoding , format ), header )
10051077
1006- def to_der (self , point_encoding = "uncompressed" ):
1078+ def to_der (self , point_encoding = "uncompressed" , format = "ssleay" ):
10071079 """
10081080 Convert the private key to the :term:`DER` format.
10091081
@@ -1013,6 +1085,7 @@ def to_der(self, point_encoding="uncompressed"):
10131085 The public key will be included in the generated string.
10141086
10151087 :param str point_encoding: format to use for encoding public point
1088+ :param str format: either ``ssleay`` (default) or ``pkcs8``
10161089
10171090 :return: DER encoded private key
10181091 :rtype: bytes
@@ -1021,16 +1094,30 @@ def to_der(self, point_encoding="uncompressed"):
10211094 # cont[1],bitstring])
10221095 if point_encoding == "raw" :
10231096 raise ValueError ("raw encoding not allowed in DER" )
1097+ assert format in ("ssleay" , "pkcs8" )
10241098 encoded_vk = self .get_verifying_key ().to_string (point_encoding )
10251099 # the 0 in encode_bitstring specifies the number of unused bits
10261100 # in the `encoded_vk` string
1027- return der .encode_sequence (
1101+ ec_private_key = der .encode_sequence (
10281102 der .encode_integer (1 ),
10291103 der .encode_octet_string (self .to_string ()),
10301104 der .encode_constructed (0 , self .curve .encoded_oid ),
10311105 der .encode_constructed (1 , der .encode_bitstring (encoded_vk , 0 )),
10321106 )
10331107
1108+ if format == "ssleay" :
1109+ return ec_private_key
1110+ else :
1111+ return der .encode_sequence (
1112+ # version = 1 means the public key is not present in the
1113+ # top-level structure.
1114+ der .encode_integer (1 ),
1115+ der .encode_sequence (
1116+ der .encode_oid (* oid_ecPublicKey ), self .curve .encoded_oid
1117+ ),
1118+ der .encode_octet_string (ec_private_key ),
1119+ )
1120+
10341121 def get_verifying_key (self ):
10351122 """
10361123 Return the VerifyingKey associated with this private key.
0 commit comments