- def __init__(self,ptr=None,privkey=None,pubkey=None,format="PEM",cansign=False,password=None):
- if not ptr is None:
- self.key=ptr
- self.cansign=cansign
- if not privkey is None or not pubkey is None:
- raise TypeError("Just one of pubkey or privkey can be specified")
- elif not privkey is None:
- if not pubkey is None:
- raise TypeError("Just one of pubkey or privkey can be specified")
- b=Membio(privkey)
- self.cansign=True
- if format == "PEM":
- self.key=libcrypto.PEM_read_bio_PrivateKey(b.bio,None,_cb,c_char_p(password))
- else:
- self.key=libcrypto.d2i_PrivateKey_bio(b.bio,None)
- if self.key is None:
- raise PKeyError("error parsing private key")
- elif not pubkey is None:
- b=Membio(pubkey)
- self.cansign=False
- if format == "PEM":
- self.key=libcrypto.PEM_read_bio_PUBKEY(b.bio,None,_cb,None)
- else:
- self.key=libcrypto.d2i_PUBKEY_bio(b.bio,None)
- if self.key is None:
- raise PKeyError("error parsing public key")
- else:
- raise TypeError("Neither public, nor private key is specified")
-
-
- def __del__(self):
- libcrypto.EVP_PKEY_free(self.key)
- def __eq__(self,other):
- """ Compares two public keys. If one has private key and other
- doesn't it doesn't affect result of comparation
- """
- return libcrypto.EVP_PKEY_cmp(self.key,other.key)==1
- def __ne__(self,other):
- return not self.__eq__(other)
- def __str__(self):
- """ printable representation of public key """
- b=Membio()
- libcrypto.EVP_PKEY_print_public(b.bio,self.key,0,None)
- return str(b)
-
- def sign(self,digest,**kwargs):
- """
- Signs given digest and retirns signature
- Keyword arguments allows to set various algorithm-specific
- parameters. See pkeyutl(1) manual.
- """
- ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
- if ctx is None:
- raise PKeyError("Initailizing sign context")
- if libcrypto.EVP_PKEY_sign_init(ctx)<1:
- raise PKeyError("sign_init")
- self._configure_context(ctx,kwargs)
- # Find out signature size
- siglen=c_long(0)
- if libcrypto.EVP_PKEY_sign(ctx,None,byref(siglen),digest,len(digest))<1:
- raise PKeyError("signing")
- sig=create_string_buffer(siglen.value)
- libcrypto.EVP_PKEY_sign(ctx,sig,byref(siglen),digest,len(digest))
- libcrypto.EVP_PKEY_CTX_free(ctx)
- return sig.raw[:siglen.value]
-
- def verify(self,digest,signature,**kwargs):
- """
- Verifies given signature on given digest
- Returns True if Ok, False if don't match
- Keyword arguments allows to set algorithm-specific
- parameters
- """
- ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
- if ctx is None:
- raise PKeyError("Initailizing verify context")
- if libcrypto.EVP_PKEY_verify_init(ctx)<1:
- raise PKeyError("verify_init")
- self._configure_context(ctx,kwargs)
- rv=libcrypto.EVP_PKEY_verify(ctx,signature,len(signature),digest,len(digest))
- if rv<0:
- raise PKeyError("Signature verification")
- libcrypto.EVP_PKEY_CTX_free(ctx)
- return rv>0
- def derive(self,peerkey,**kwargs):
- """
- Derives shared key (DH,ECDH,VKO 34.10). Requires
- private key available
-
- @param peerkey - other key (may be public only)
-
- Keyword parameters are algorithm-specific
- """
- ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
- if ctx is None:
- raise PKeyError("Initailizing derive context")
- if libcrypto.EVP_PKEY_derive_init(ctx)<1:
- raise PKeyError("derive_init")
- self._configure_context(self,ctx,kwargs)
- if libcrypto.EVP_PKEY_derive_set_peer(ctx,peerkey.key)<=0:
- raise PKeyError("Cannot set peer key")
- keylen=c_long(0)
- if libcrypto.EVP_PKEY_derive(ctx,None,byref(keylen))<=0:
- raise PKeyError("computing shared key length")
- buf=create_string_buffer(keylen)
- if libcrypto.EVP_PKEY_derive(ctx,buf,byref(keylen))<=0:
- raise PKeyError("computing actual shared key")
- libcrypto.EVP_PKEY_CTX_free(ctx)
- return buf.raw[:keylen]
- @staticmethod
- def generate(algorithm,**kwargs):
- """
- Generates new private-public key pair for given algorithm
- (string like 'rsa','ec','gost2001') and algorithm-specific
- parameters.
-
- Algorithm specific paramteers for RSA:
-
- rsa_keygen_bits=number - size of key to be generated
- rsa_keygen_pubexp - RSA public expontent(default 65537)
-
- Algorithn specific parameters for DSA,DH and EC
-
- paramsfrom=PKey object
-
- copy parameters of newly generated key from existing key
-
- Algorithm specific parameters for GOST2001
-
- paramset= paramset name where name is one of
- 'A','B','C','XA','XB','test'
-
- paramsfrom does work too
- """
- tmpeng=c_void_p(None)
- ameth=libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng),algorithm,-1)
- if ameth is None:
- raise PKeyError("Algorithm %s not foind\n"%(algname))
- clear_err_stack()
- pkey_id=c_int(0)
- libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id),None,None,None,None,ameth)
- libcrypto.ENGINE_finish(tmpeng)
- if "paramsfrom" in kwargs:
- ctx=libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key,None)
- else:
- ctx=libcrypto.EVP_PKEY_CTX_new_id(pkey_id,None)
- # FIXME support EC curve as keyword param by invoking paramgen
- # operation
- if ctx is None:
- raise PKeyError("Creating context for key type %d"%(pkey_id.value))
- if libcrypto.EVP_PKEY_keygen_init(ctx) <=0 :
- raise PKeyError("keygen_init")
- PKey._configure_context(ctx,kwargs,["paramsfrom"])
- key=c_void_p(None)
- if libcrypto.EVP_PKEY_keygen(ctx,byref(key))<=0:
- raise PKeyError("Error generating key")
- libcrypto.EVP_PKEY_CTX_free(ctx)
- return PKey(ptr=key,cansign=True)
- def exportpub(self,format="PEM"):
- """
- Returns public key as PEM or DER structure.
- """
- b=Membio()
- if format == "PEM":
- r=libcrypto.PEM_write_bio_PUBKEY(b.bio,self.key)
- else:
- r=libcrypto.i2d_PUBKEY_bio(b.bio,self.key)
- if r==0:
- raise PKeyError("error serializing public key")
- return str(b)
- def exportpriv(self,format="PEM",password=None,cipher=None):
- """
- Returns private key as PEM or DER Structure.
- If password and cipher are specified, encrypts key
- on given password, using given algorithm. Cipher must be
- an ctypescrypto.cipher.CipherType object
- """
- b=Membio()
- if cipher is None:
- evp_cipher=None
- else:
- if password is None:
- raise NotImplementedError("Interactive password entry is not supported")
- evp_cipher=cipher.cipher
- if format == "PEM":
- r=libcrypto.PEM_write_bio_PrivateKey(b.bio,self.key,evp_cipher,None,0,_cb,
- password)
- else:
- if cipher is not None:
- raise NotImplementedError("Der-formatted encrypted keys are not supported")
- r=libcrypto.i2d_PrivateKey_bio(b.bio,self.key)
- if r==0:
- raise PKeyError("error serializing private key")
- return str(b)
- @staticmethod
- def _configure_context(ctx,opts,skip=[]):
- """
- Configures context of public key operations
- @param ctx - context to configure
- @param opts - dictionary of options (from kwargs of calling
- function)
- @param skip - list of options which shouldn't be passed to
- context
- """
-
- for oper in opts:
- if oper in skip:
- continue
- rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,str(opts[oper]))
- if rv==-2:
- raise PKeyError("Parameter %s is not supported by key"%(oper))
- if rv<1:
- raise PKeyError("Error setting parameter %s"(oper))
+ """
+ Represents public/private key pair. Wrapper around EVP_PKEY
+ libcrypto object.
+
+ May contain either both private and public key (such objects can be
+ used for signing, deriving shared key as well as verifying or public
+ key only, which can be used for verifying or as peer key when
+ deriving.
+
+ @var cansign is true key has private part.
+ @var key contain pointer to EVP_PKEY and should be passed to various
+ libcrypto routines
+ """
+ def __init__(self, ptr=None, privkey=None, pubkey=None, format="PEM",
+ cansign=False, password=None, callback=_cb):
+ if not ptr is None:
+ self.key = ptr
+ self.cansign = cansign
+ if not privkey is None or not pubkey is None:
+ raise TypeError("Just one of ptr, pubkey or privkey can " +
+ "be specified")
+ elif not privkey is None:
+ if not pubkey is None:
+ raise TypeError("Just one of ptr, pubkey or privkey can " +
+ "be specified")
+ bio = Membio(privkey)
+ self.cansign = True
+ if format == "PEM":
+ self.key = libcrypto.PEM_read_bio_PrivateKey(bio.bio, None,
+ callback,
+ c_char_p(password))
+ else:
+ self.key = libcrypto.d2i_PrivateKey_bio(bio.bio, None)
+ if self.key is None:
+ raise PKeyError("error parsing private key")
+ elif not pubkey is None:
+ bio = Membio(pubkey)
+ self.cansign = False
+ if format == "PEM":
+ self.key = libcrypto.PEM_read_bio_PUBKEY(bio.bio, None,
+ callback,
+ c_char_p(password))
+ else:
+ self.key = libcrypto.d2i_PUBKEY_bio(bio.bio, None)
+ if self.key is None:
+ raise PKeyError("error parsing public key")
+ else:
+ raise TypeError("Neither public, nor private key is specified")
+
+
+ def __del__(self):
+ """ Frees EVP_PKEY object (note, it is reference counted) """
+ libcrypto.EVP_PKEY_free(self.key)
+
+ def __eq__(self, other):
+ """ Compares two public keys. If one has private key and other
+ doesn't it doesn't affect result of comparation
+ """
+ return libcrypto.EVP_PKEY_cmp(self.key, other.key) == 1
+
+ def __ne__(self, other):
+ """ Compares two public key for not-equality """
+ return not self.__eq__(other)
+
+ def __str__(self):
+ """ printable representation of public key """
+ bio = Membio()
+ libcrypto.EVP_PKEY_print_public(bio.bio, self.key, 0, None)
+ return str(bio)
+
+ def sign(self, digest, **kwargs):
+ """
+ Signs given digest and retirns signature
+ Keyword arguments allows to set various algorithm-specific
+ parameters. See pkeyutl(1) manual.
+ """
+ ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
+ if ctx is None:
+ raise PKeyError("Initailizing sign context")
+ if libcrypto.EVP_PKEY_sign_init(ctx) < 1:
+ raise PKeyError("sign_init")
+ self._configure_context(ctx, kwargs)
+ # Find out signature size
+ siglen = c_long(0)
+ if libcrypto.EVP_PKEY_sign(ctx, None, byref(siglen), digest,
+ len(digest)) < 1:
+ raise PKeyError("computing signature length")
+ sig = create_string_buffer(siglen.value)
+ if libcrypto.EVP_PKEY_sign(ctx, sig, byref(siglen), digest,
+ len(digest)) < 1:
+ raise PKeyError("signing")
+ libcrypto.EVP_PKEY_CTX_free(ctx)
+ return sig.raw[:int(siglen.value)]
+
+ def verify(self, digest, signature, **kwargs):
+ """
+ Verifies given signature on given digest
+ Returns True if Ok, False if don't match
+ Keyword arguments allows to set algorithm-specific
+ parameters
+ """
+ ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
+ if ctx is None:
+ raise PKeyError("Initailizing verify context")
+ if libcrypto.EVP_PKEY_verify_init(ctx) < 1:
+ raise PKeyError("verify_init")
+ self._configure_context(ctx, kwargs)
+ ret = libcrypto.EVP_PKEY_verify(ctx, signature, len(signature), digest,
+ len(digest))
+ if ret < 0:
+ raise PKeyError("Signature verification")
+ libcrypto.EVP_PKEY_CTX_free(ctx)
+ return ret > 0
+
+ def derive(self, peerkey, **kwargs):
+ """
+ Derives shared key (DH,ECDH,VKO 34.10). Requires
+ private key available
+
+ @param peerkey - other key (may be public only)
+
+ Keyword parameters are algorithm-specific
+ """
+ if not self.cansign:
+ raise ValueError("No private key available")
+ ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
+ if ctx is None:
+ raise PKeyError("Initailizing derive context")
+ if libcrypto.EVP_PKEY_derive_init(ctx) < 1:
+ raise PKeyError("derive_init")
+
+ # This is workaround around missing functionality in GOST engine
+ # it provides only numeric control command to set UKM, not
+ # string one.
+ self._configure_context(ctx, kwargs, ["ukm"])
+ if libcrypto.EVP_PKEY_derive_set_peer(ctx, peerkey.key) <= 0:
+ raise PKeyError("Cannot set peer key")
+ if "ukm" in kwargs:
+ # We just hardcode numeric command to set UKM here
+ if libcrypto.EVP_PKEY_CTX_ctrl(ctx, -1, 1 << 10, 8, 8,
+ kwargs["ukm"]) <= 0:
+ raise PKeyError("Cannot set UKM")
+ keylen = c_long(0)
+ if libcrypto.EVP_PKEY_derive(ctx, None, byref(keylen)) <= 0:
+ raise PKeyError("computing shared key length")
+ buf = create_string_buffer(keylen.value)
+ if libcrypto.EVP_PKEY_derive(ctx, buf, byref(keylen)) <= 0:
+ raise PKeyError("computing actual shared key")
+ libcrypto.EVP_PKEY_CTX_free(ctx)
+ return buf.raw[:int(keylen.value)]
+
+ @staticmethod
+ def generate(algorithm, **kwargs):
+ """
+ Generates new private-public key pair for given algorithm
+ (string like 'rsa','ec','gost2001') and algorithm-specific
+ parameters.
+
+ Algorithm specific paramteers for RSA:
+
+ rsa_keygen_bits=number - size of key to be generated
+ rsa_keygen_pubexp - RSA public expontent(default 65537)
+
+ Algorithm specific parameters for DSA,DH and EC
+
+ paramsfrom=PKey object
+
+ copy parameters of newly generated key from existing key
+
+ Algorithm specific parameters for GOST2001
+
+ paramset= paramset name where name is one of
+ 'A','B','C','XA','XB','test'
+
+ paramsfrom does work too
+ """
+ tmpeng = c_void_p(None)
+ ameth = libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng), algorithm, -1)
+ if ameth is None:
+ raise PKeyError("Algorithm %s not foind\n"%(algorithm))
+ clear_err_stack()
+ pkey_id = c_int(0)
+ libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id), None, None, None,
+ None, ameth)
+ #libcrypto.ENGINE_finish(tmpeng)
+ if "paramsfrom" in kwargs:
+ ctx = libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key, None)
+ else:
+ ctx = libcrypto.EVP_PKEY_CTX_new_id(pkey_id, None)
+ # FIXME support EC curve as keyword param by invoking paramgen
+ # operation
+ if ctx is None:
+ raise PKeyError("Creating context for key type %d"%(pkey_id.value))
+ if libcrypto.EVP_PKEY_keygen_init(ctx) <= 0:
+ raise PKeyError("keygen_init")
+ PKey._configure_context(ctx, kwargs, ["paramsfrom"])
+ key = c_void_p(None)
+ if libcrypto.EVP_PKEY_keygen(ctx, byref(key)) <= 0:
+ raise PKeyError("Error generating key")
+ libcrypto.EVP_PKEY_CTX_free(ctx)
+ return PKey(ptr=key, cansign=True)
+
+ def exportpub(self, format="PEM"):
+ """
+ Returns public key as PEM or DER structure.
+ """
+ bio = Membio()
+ if format == "PEM":
+ retcode = libcrypto.PEM_write_bio_PUBKEY(bio.bio, self.key)
+ else:
+ retcode = libcrypto.i2d_PUBKEY_bio(bio.bio, self.key)
+ if retcode == 0:
+ raise PKeyError("error serializing public key")
+ return str(bio)
+
+ def exportpriv(self, format="PEM", password=None, cipher=None,
+ callback=_cb):
+ """
+ Returns private key as PEM or DER Structure.
+ If password and cipher are specified, encrypts key
+ on given password, using given algorithm. Cipher must be
+ an ctypescrypto.cipher.CipherType object
+ """
+ bio = Membio()
+ if cipher is None:
+ evp_cipher = None
+ else:
+ evp_cipher = cipher.cipher
+ if format == "PEM":
+ ret = libcrypto.PEM_write_bio_PrivateKey(bio.bio, self.key,
+ evp_cipher, None, 0,
+ callback,
+ c_char_p(password))
+ else:
+ ret = libcrypto.i2d_PKCS8PrivateKey_bio(bio.bio, self.key,
+ evp_cipher, None, 0,
+ callback,
+ c_char_p(password))
+ if ret == 0:
+ raise PKeyError("error serializing private key")
+ return str(bio)
+
+ @staticmethod
+ def _configure_context(ctx, opts, skip=()):
+ """
+ Configures context of public key operations
+ @param ctx - context to configure
+ @param opts - dictionary of options (from kwargs of calling
+ function)
+ @param skip - list of options which shouldn't be passed to
+ context
+ """
+
+ for oper in opts:
+ if oper in skip:
+ continue
+ ret = libcrypto.EVP_PKEY_CTX_ctrl_str(ctx, oper, str(opts[oper]))
+ if ret == -2:
+ raise PKeyError("Parameter %s is not supported by key" % oper)
+ if ret < 1:
+ raise PKeyError("Error setting parameter %s" % oper)