2 This module provides interface for low-level private/public keypair operation
4 PKey object of this module is wrapper around OpenSSL EVP_PKEY object.
8 from ctypes import c_char_p,c_void_p,byref,c_int,c_long, c_longlong, create_string_buffer,CFUNCTYPE,POINTER
9 from ctypescrypto import libcrypto
10 from ctypescrypto.exception import LibCryptoError,clear_err_stack
11 from ctypescrypto.bio import Membio
14 __all__ = ['PKeyError','password_callback','PKey']
15 class PKeyError(LibCryptoError):
18 CALLBACK_FUNC=CFUNCTYPE(c_int,c_char_p,c_int,c_int,c_char_p)
19 def password_callback(buf,length,rwflag,u):
21 Example password callback for private key. Assumes that
22 password is store in the userdata parameter, so allows to pass password
23 from constructor arguments to the libcrypto keyloading functions
31 _cb=CALLBACK_FUNC(password_callback)
34 def __init__(self,ptr=None,privkey=None,pubkey=None,format="PEM",cansign=False,password=None):
38 if not privkey is None or not pubkey is None:
39 raise TypeError("Just one of pubkey or privkey can be specified")
40 elif not privkey is None:
41 if not pubkey is None:
42 raise TypeError("Just one of pubkey or privkey can be specified")
46 self.key=libcrypto.PEM_read_bio_PrivateKey(b.bio,None,_cb,c_char_p(password))
48 self.key=libcrypto.d2i_PrivateKey_bio(b.bio,None)
50 raise PKeyError("error parsing private key")
51 elif not pubkey is None:
55 self.key=libcrypto.PEM_read_bio_PUBKEY(b.bio,None,_cb,None)
57 self.key=libcrypto.d2i_PUBKEY_bio(b.bio,None)
59 raise PKeyError("error parsing public key")
61 raise TypeError("Neither public, nor private key is specified")
65 libcrypto.EVP_PKEY_free(self.key)
66 def __eq__(self,other):
67 """ Compares two public keys. If one has private key and other
68 doesn't it doesn't affect result of comparation
70 return libcrypto.EVP_PKEY_cmp(self.key,other.key)==1
71 def __ne__(self,other):
72 return not self.__eq__(other)
74 """ printable representation of public key """
76 libcrypto.EVP_PKEY_print_public(b.bio,self.key,0,None)
79 def sign(self,digest,**kwargs):
81 Signs given digest and retirns signature
82 Keyword arguments allows to set various algorithm-specific
83 parameters. See pkeyutl(1) manual.
85 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
87 raise PKeyError("Initailizing sign context")
88 if libcrypto.EVP_PKEY_sign_init(ctx)<1:
89 raise PKeyError("sign_init")
90 self._configure_context(ctx,kwargs)
91 # Find out signature size
93 if libcrypto.EVP_PKEY_sign(ctx,None,byref(siglen),digest,len(digest))<1:
94 raise PKeyError("signing")
95 sig=create_string_buffer(siglen.value)
96 libcrypto.EVP_PKEY_sign(ctx,sig,byref(siglen),digest,len(digest))
97 libcrypto.EVP_PKEY_CTX_free(ctx)
98 return sig.raw[:siglen.value]
100 def verify(self,digest,signature,**kwargs):
102 Verifies given signature on given digest
103 Returns True if Ok, False if don't match
104 Keyword arguments allows to set algorithm-specific
107 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
109 raise PKeyError("Initailizing verify context")
110 if libcrypto.EVP_PKEY_verify_init(ctx)<1:
111 raise PKeyError("verify_init")
112 self._configure_context(ctx,kwargs)
113 rv=libcrypto.EVP_PKEY_verify(ctx,signature,len(signature),digest,len(digest))
115 raise PKeyError("Signature verification")
116 libcrypto.EVP_PKEY_CTX_free(ctx)
118 def derive(self,peerkey,**kwargs):
120 Derives shared key (DH,ECDH,VKO 34.10). Requires
121 private key available
123 @param peerkey - other key (may be public only)
125 Keyword parameters are algorithm-specific
127 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
129 raise PKeyError("Initailizing derive context")
130 if libcrypto.EVP_PKEY_derive_init(ctx)<1:
131 raise PKeyError("derive_init")
132 self._configure_context(self,ctx,kwargs)
133 if libcrypto.EVP_PKEY_derive_set_peer(ctx,peerkey.key)<=0:
134 raise PKeyError("Cannot set peer key")
136 if libcrypto.EVP_PKEY_derive(ctx,None,byref(keylen))<=0:
137 raise PKeyError("computing shared key length")
138 buf=create_string_buffer(keylen)
139 if libcrypto.EVP_PKEY_derive(ctx,buf,byref(keylen))<=0:
140 raise PKeyError("computing actual shared key")
141 libcrypto.EVP_PKEY_CTX_free(ctx)
142 return buf.raw[:keylen]
144 def generate(algorithm,**kwargs):
146 Generates new private-public key pair for given algorithm
147 (string like 'rsa','ec','gost2001') and algorithm-specific
150 Algorithm specific paramteers for RSA:
152 rsa_keygen_bits=number - size of key to be generated
153 rsa_keygen_pubexp - RSA public expontent(default 65537)
155 Algorithn specific parameters for DSA,DH and EC
157 paramsfrom=PKey object
159 copy parameters of newly generated key from existing key
161 Algorithm specific parameters for GOST2001
163 paramset= paramset name where name is one of
164 'A','B','C','XA','XB','test'
166 paramsfrom does work too
168 tmpeng=c_void_p(None)
169 ameth=libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng),algorithm,-1)
171 raise PKeyError("Algorithm %s not foind\n"%(algname))
174 libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id),None,None,None,None,ameth)
175 libcrypto.ENGINE_finish(tmpeng)
176 if "paramsfrom" in kwargs:
177 ctx=libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key,None)
179 ctx=libcrypto.EVP_PKEY_CTX_new_id(pkey_id,None)
180 # FIXME support EC curve as keyword param by invoking paramgen
183 raise PKeyError("Creating context for key type %d"%(pkey_id.value))
184 if libcrypto.EVP_PKEY_keygen_init(ctx) <=0 :
185 raise PKeyError("keygen_init")
186 PKey._configure_context(ctx,kwargs,["paramsfrom"])
188 if libcrypto.EVP_PKEY_keygen(ctx,byref(key))<=0:
189 raise PKeyError("Error generating key")
190 libcrypto.EVP_PKEY_CTX_free(ctx)
191 return PKey(ptr=key,cansign=True)
192 def exportpub(self,format="PEM"):
194 Returns public key as PEM or DER structure.
198 r=libcrypto.PEM_write_bio_PUBKEY(b.bio,self.key)
200 r=libcrypto.i2d_PUBKEY_bio(b.bio,self.key)
202 raise PKeyError("error serializing public key")
204 def exportpriv(self,format="PEM",password=None,cipher=None):
206 Returns private key as PEM or DER Structure.
207 If password and cipher are specified, encrypts key
208 on given password, using given algorithm. Cipher must be
209 an ctypescrypto.cipher.CipherType object
216 raise NotImplementedError("Interactive password entry is not supported")
217 evp_cipher=cipher.cipher
219 r=libcrypto.PEM_write_bio_PrivateKey(b.bio,self.key,evp_cipher,None,0,_cb,
222 if cipher is not None:
223 raise NotImplementedError("Der-formatted encrypted keys are not supported")
224 r=libcrypto.i2d_PrivateKey_bio(b.bio,self.key)
226 raise PKeyError("error serializing private key")
229 def _configure_context(ctx,opts,skip=[]):
231 Configures context of public key operations
232 @param ctx - context to configure
233 @param opts - dictionary of options (from kwargs of calling
235 @param skip - list of options which shouldn't be passed to
242 rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,str(opts[oper]))
244 raise PKeyError("Parameter %s is not supported by key"%(oper))
246 raise PKeyError("Error setting parameter %s"(oper))
247 # Declare function prototypes
248 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
249 libcrypto.PEM_read_bio_PrivateKey.restype=c_void_p
250 libcrypto.PEM_read_bio_PrivateKey.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p)
251 libcrypto.PEM_read_bio_PUBKEY.restype=c_void_p
252 libcrypto.PEM_read_bio_PUBKEY.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p)
253 libcrypto.d2i_PUBKEY_bio.restype=c_void_p
254 libcrypto.d2i_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
255 libcrypto.d2i_PrivateKey_bio.restype=c_void_p
256 libcrypto.d2i_PrivateKey_bio.argtypes=(c_void_p,c_void_p)
257 libcrypto.EVP_PKEY_print_public.argtypes=(c_void_p,c_void_p,c_int,c_void_p)
258 libcrypto.EVP_PKEY_asn1_find_str.restype=c_void_p
259 libcrypto.EVP_PKEY_asn1_find_str.argtypes=(c_void_p,c_char_p,c_int)
260 libcrypto.EVP_PKEY_asn1_get0_info.restype=c_int
261 libcrypto.EVP_PKEY_asn1_get0_info.argtypes=(POINTER(c_int),POINTER(c_int),POINTER(c_int),POINTER(c_char_p), POINTER(c_char_p),c_void_p)
262 libcrypto.EVP_PKEY_cmp.restype=c_int
263 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
264 libcrypto.EVP_PKEY_CTX_ctrl_str.restype=c_int
265 libcrypto.EVP_PKEY_CTX_ctrl_str.argtypes=(c_void_p,)
266 libcrypto.EVP_PKEY_CTX_free.argtypes=(c_void_p,)
267 libcrypto.EVP_PKEY_CTX_new.restype=c_void_p
268 libcrypto.EVP_PKEY_CTX_new.argtypes=(c_void_p,c_void_p)
269 libcrypto.EVP_PKEY_CTX_new_id.restype=c_void_p
270 libcrypto.EVP_PKEY_CTX_new_id.argtypes=(c_int,c_void_p)
271 libcrypto.EVP_PKEY_derive.restype=c_int
272 libcrypto.EVP_PKEY_derive.argtypes=(c_void_p,c_char_p,POINTER(c_long))
273 libcrypto.EVP_PKEY_derive_init.restype=c_int
274 libcrypto.EVP_PKEY_derive_init.argtypes=(c_void_p,)
275 libcrypto.EVP_PKEY_derive_set_peer.restype=c_int
276 libcrypto.EVP_PKEY_derive_set_peer.argtypes=(c_void_p,c_void_p)
277 libcrypto.EVP_PKEY_free.argtypes=(c_void_p,)
278 libcrypto.EVP_PKEY_keygen.restype=c_int
279 libcrypto.EVP_PKEY_keygen.argtypes=(c_void_p,c_void_p)
280 libcrypto.EVP_PKEY_keygen_init.restype=c_int
281 libcrypto.EVP_PKEY_keygen_init.argtypes=(c_void_p,)
282 libcrypto.EVP_PKEY_sign.restype=c_int
283 libcrypto.EVP_PKEY_sign.argtypes=(c_void_p,c_char_p,POINTER(c_long),c_char_p,c_long)
284 libcrypto.EVP_PKEY_sign_init.restype=c_int
285 libcrypto.EVP_PKEY_sign_init.argtypes=(c_void_p,)
286 libcrypto.EVP_PKEY_verify.restype=c_int
287 libcrypto.EVP_PKEY_verify.argtypes=(c_void_p,c_char_p,c_long,c_char_p,c_long)
288 libcrypto.EVP_PKEY_verify_init.restype=c_int
289 libcrypto.EVP_PKEY_verify_init.argtypes=(c_void_p,)
290 libcrypto.PEM_write_bio_PrivateKey.argtypes=(c_void_p,c_void_p,c_void_p,c_char_p,c_int,CALLBACK_FUNC,c_char_p)
291 libcrypto.PEM_write_bio_PUBKEY.argtypes=(c_void_p,c_void_p)
292 libcrypto.i2d_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
293 libcrypto.i2d_PrivateKey_bio.argtypes=(c_void_p,c_void_p)