1
- from cryptography .hazmat .primitives .asymmetric import ed25519
1
+ from cryptography .hazmat .primitives .asymmetric import ed25519 , ec
2
2
from cryptography .hazmat .primitives import serialization
3
+ from cryptography .hazmat .backends import default_backend
3
4
from hedera_sdk_python .crypto .public_key import PublicKey
4
5
6
+
5
7
class PrivateKey :
8
+ """
9
+ Represents a private key that can be either Ed25519 or ECDSA (secp256k1).
10
+ """
11
+
6
12
def __init__ (self , private_key ):
13
+ """
14
+ Initializes a PrivateKey from a cryptography PrivateKey object.
15
+ """
7
16
self ._private_key = private_key
8
17
9
18
@classmethod
10
- def generate (cls ):
19
+ def generate (cls , key_type : str = "ed25519" ):
11
20
"""
12
- Generates a new Ed25519 private key.
21
+ Generates a new private key. Defaults to an Ed25519 private key unless
22
+ 'ecdsa' is specified.
23
+
24
+ Args:
25
+ key_type (str): Either 'ed25519' or 'ecdsa'. Defaults to 'ed25519'.
13
26
14
27
Returns:
15
28
PrivateKey: A new instance of PrivateKey.
16
29
"""
17
- private_key = ed25519 .Ed25519PrivateKey .generate ()
30
+ if key_type .lower () == "ed25519" :
31
+ return cls .generate_ed25519 ()
32
+ elif key_type .lower () == "ecdsa" :
33
+ return cls .generate_ecdsa ()
34
+ else :
35
+ raise ValueError ("Invalid key_type. Use 'ed25519' or 'ecdsa'." )
36
+
37
+ @classmethod
38
+ def generate_ed25519 (cls ):
39
+ """
40
+ Generates a new Ed25519 private key.
41
+
42
+ Returns:
43
+ PrivateKey: A new instance of PrivateKey using Ed25519.
44
+ """
45
+ return cls (ed25519 .Ed25519PrivateKey .generate ())
46
+
47
+ @classmethod
48
+ def generate_ecdsa (cls ):
49
+ """
50
+ Generates a new ECDSA (secp256k1) private key.
51
+
52
+ Returns:
53
+ PrivateKey: A new instance of PrivateKey using ECDSA.
54
+ """
55
+ private_key = ec .generate_private_key (ec .SECP256K1 (), default_backend ())
18
56
return cls (private_key )
19
57
20
58
@classmethod
21
59
def from_string (cls , key_str ):
22
60
"""
23
- Load a private key from a hex-encoded string. Supports both raw private keys (32 bytes)
24
- and DER-encoded private keys.
61
+ Load a private key from a hex-encoded string. For Ed25519, expects 32 bytes.
62
+ For ECDSA (secp256k1), also expects 32 bytes (raw scalar).
63
+ If it's DER-encoded, tries to parse and detect Ed25519 vs ECDSA.
64
+
65
+ Args:
66
+ key_str (str): The hex-encoded private key string.
67
+
68
+ Returns:
69
+ PrivateKey: A new instance of PrivateKey.
70
+
71
+ Raises:
72
+ ValueError: If the key is invalid or unsupported.
25
73
"""
26
74
try :
27
75
key_bytes = bytes .fromhex (key_str )
28
76
except ValueError :
29
77
raise ValueError ("Invalid hex-encoded private key string." )
30
78
79
+ if len (key_bytes ) == 32 :
80
+ try :
81
+ ed_priv = ed25519 .Ed25519PrivateKey .from_private_bytes (key_bytes )
82
+ return cls (ed_priv )
83
+ except Exception :
84
+ pass
85
+ try :
86
+ private_int = int .from_bytes (key_bytes , "big" )
87
+ ec_priv = ec .derive_private_key (private_int , ec .SECP256K1 (), default_backend ())
88
+ return cls (ec_priv )
89
+ except Exception :
90
+ pass
91
+
31
92
try :
32
- if len (key_bytes ) == 32 :
33
- private_key = ed25519 .Ed25519PrivateKey .from_private_bytes (key_bytes )
34
- else :
35
- private_key = serialization .load_der_private_key (
36
- key_bytes , password = None
37
- )
38
- if not isinstance (private_key , ed25519 .Ed25519PrivateKey ):
39
- raise TypeError ("The key is not an Ed25519 private key." )
40
- return cls (private_key )
93
+ private_key = serialization .load_der_private_key (key_bytes , password = None )
41
94
except Exception as e :
42
- print (f"Error loading Ed25519 private key: { e } " )
43
- raise ValueError ("Failed to load private key." )
95
+ raise ValueError (f"Failed to load private key (DER): { e } " )
96
+
97
+ if isinstance (private_key , ed25519 .Ed25519PrivateKey ):
98
+ return cls (private_key )
99
+
100
+ if isinstance (private_key , ec .EllipticCurvePrivateKey ):
101
+ if not isinstance (private_key .curve , ec .SECP256K1 ):
102
+ raise ValueError ("Only secp256k1 ECDSA is supported." )
103
+ return cls (private_key )
104
+
105
+ raise ValueError ("Unsupported private key type." )
44
106
45
- def sign (self , data ) :
107
+ def sign (self , data : bytes ) -> bytes :
46
108
"""
47
- Signs the given data using the private key.
109
+ Signs the given data using this private key (Ed25519 or ECDSA) .
48
110
49
111
Args:
50
112
data (bytes): The data to sign.
@@ -54,40 +116,63 @@ def sign(self, data):
54
116
"""
55
117
return self ._private_key .sign (data )
56
118
57
- def public_key (self ):
119
+ def public_key (self ) -> PublicKey :
58
120
"""
59
- Retrieves the corresponding public key .
121
+ Retrieves the corresponding PublicKey .
60
122
61
123
Returns:
62
124
PublicKey: The public key associated with this private key.
63
125
"""
64
126
return PublicKey (self ._private_key .public_key ())
65
127
66
- def to_string (self ):
128
+ def to_bytes_raw (self ) -> bytes :
67
129
"""
68
- Returns the private key as a hex-encoded string .
130
+ Returns the private key bytes in raw form (32 bytes for both Ed25519 and ECDSA) .
69
131
70
132
Returns:
71
- str : The hex-encoded private key.
133
+ bytes : The raw private key bytes .
72
134
"""
73
- private_bytes = self ._private_key .private_bytes (
135
+ return self ._private_key .private_bytes (
74
136
encoding = serialization .Encoding .Raw ,
75
137
format = serialization .PrivateFormat .Raw ,
76
138
encryption_algorithm = serialization .NoEncryption ()
77
139
)
78
- return private_bytes .hex ()
79
140
141
+ def to_string_raw (self ) -> str :
142
+ """
143
+ Returns the raw private key as a hex-encoded string.
144
+
145
+ Returns:
146
+ str: The hex-encoded raw private key.
147
+ """
148
+ return self .to_bytes_raw ().hex ()
149
+
150
+ def to_string (self ) -> str :
151
+ """
152
+ Returns the private key as a hex string (raw).
153
+ """
154
+ return self .to_string_raw ()
155
+
156
+ def is_ed25519 (self ) -> bool :
157
+ """
158
+ Checks if this private key is Ed25519.
159
+
160
+ Returns:
161
+ bool: True if Ed25519, False otherwise.
162
+ """
163
+ return isinstance (self ._private_key , ed25519 .Ed25519PrivateKey )
80
164
81
- def to_bytes (self ):
165
+ def is_ecdsa (self ) -> bool :
82
166
"""
83
- Returns the private key as bytes .
167
+ Checks if this private key is ECDSA (secp256k1) .
84
168
85
169
Returns:
86
- bytes: The private key .
170
+ bool: True if ECDSA, False otherwise .
87
171
"""
88
- private_bytes = self ._private_key .private_bytes (
89
- encoding = serialization .Encoding .Raw ,
90
- format = serialization .PrivateFormat .Raw ,
91
- encryption_algorithm = serialization .NoEncryption ()
92
- )
93
- return private_bytes
172
+ from cryptography .hazmat .primitives .asymmetric import ec
173
+ return isinstance (self ._private_key , ec .EllipticCurvePrivateKey )
174
+
175
+ def __repr__ (self ):
176
+ if self .is_ed25519 ():
177
+ return f"<PrivateKey (Ed25519) hex={ self .to_string_raw ()} >"
178
+ return f"<PrivateKey (ECDSA) hex={ self .to_string_raw ()} >"
0 commit comments