2 Implements operations with CMS EnvelopedData and SignedData messages
4 Contains function CMS() which parses CMS message and creates either
5 EnvelopedData or SignedData objects (EncryptedData and CompressedData
6 can be easily added, because OpenSSL contain nessesary function)
8 Each of these objects contains create() static method which is used to
9 create it from raw data and neccessary certificates.
13 from ctypes import c_int, c_void_p, c_char_p, c_int
14 from ctypescrypto.exception import LibCryptoError
15 from ctypescrypto import libcrypto
16 from ctypescrypto.bio import Membio
17 from ctypescrypto.oid import Oid
19 class CMSError(LibCryptoError):
21 Exception which is raised when error occurs
27 Constants for flags passed to the CMS methods.
34 NO_SIGS=NO_CONTENT_VERIFY|NO_ATTR_VERIFY
36 NO_SIGNER_CERT_VERIFY=0x20
51 def CMS(data,format="PEM"):
53 Parses CMS data and returns either SignedData or EnvelopedData
58 ptr=libcrypto.PEM_read_bio_CMS(b.bio,None,None,None)
60 ptr=libcrypto.d2i_CMS_bio(b.bio,None)
61 typeoid = Oid(libcrypto.OBJ_obj2nid(libcrypto.CMS_get0_type(ptr)))
62 if typeoid.shortname()=="pkcs7-signedData":
63 return SignedData(ptr)
64 elif typeoid.shortname()=="pkcs7-envelopedData":
65 return EnvelopedData(ptr)
66 elif typeoid.shortname()=="pkcs7-encryptedData":
67 return EncryptedData(ptr)
69 raise NotImplementedError("cannot handle "+typeoid.shortname())
73 Common ancessor for all CMS types.
74 Implements serializatio/deserialization
76 def __init__(self,ptr=None):
80 Serialize in DER format
83 if not libcrypto.i2d_CMS_bio(b.bio,self.ptr):
84 raise CMSError("writing CMS to PEM")
89 Serialize in PEM format
92 if not libcrypto.PEM_write_bio_CMS(b.bio,self.ptr):
93 raise CMSError("writing CMS to PEM")
98 class SignedData(CMSBase):
100 def create(data,cert,pkey,flags=Flags.BINARY):
102 Creates SignedData message by signing data with pkey and
105 @param data - data to sign
106 @param pkey - pkey object with private key to sign
109 raise ValueError("Specified keypair has no private part")
110 if cert.pubkey!=pkey:
111 raise ValueError("Certificate doesn't match public key")
113 ptr=libcrypto.CMS_sign(cert.cert,pkey.ptr,None,b.bio,flags)
115 raise CMSError("signing message")
116 return SignedData(ptr)
117 def sign(self,cert,pkey,md=None,data=None,flags=Flags.BINARY):
119 Adds another signer to already signed message
120 @param cert - signer's certificate
121 @param pkey - signer's private key
122 @param md - message digest to use as DigestType object
123 (if None - default for key would be used)
124 @param data - data to sign (if detached and
125 Flags.REUSE_DIGEST is not specified)
126 @param flags - ORed combination of Flags consants
129 raise ValueError("Specified keypair has no private part")
130 if cert.pubkey!=pkey:
131 raise ValueError("Certificate doesn't match public key")
132 p1=libcrypto.CMS_sign_add1_Signer(self.ptr,cert.cert,pkey.ptr,
135 raise CMSError("adding signer")
136 if flags & Flags.REUSE_DIGEST==0:
142 res= libcrypto.CMS_final(self.ptr,biodata,None,flags)
145 def verify(self,store,flags,data=None):
147 Verifies signature under CMS message using trusted cert store
149 @param store - X509Store object with trusted certs
150 @param flags - OR-ed combination of flag consants
151 @param data - message data, if messge has detached signature
152 @returns True if signature valid, False otherwise
158 res=libcrypto.CMS_verify(self.ptr,None,store.store,bio,None,flags)
161 def signers(self,store=None):
163 Return list of signer's certificates
165 raise NotImplementedError
169 Returns signed data if present in the message
172 if not libcrypto.CMS_verify(self.ptr,None,None,None,b.bio,Flags.NO_VERIFY):
173 raise CMSError("extract data")
175 def addcert(self,cert):
177 Adds a certificate (probably intermediate CA) to the SignedData
180 raise NotImplementedError
181 def addcrl(self,crl):
183 Adds a CRL to the signed data structure
185 raise NotImplementedError
189 List of the certificates contained in the structure
191 raise NotImplementedError
195 List of the CRLs contained in the structure
197 raise NotImplementedError
199 class EnvelopedData(CMSBase):
201 def create(recipients,data,cipher,flags=0):
203 Creates and encrypts message
204 @param recipients - list of X509 objects
205 @param data - contents of the message
206 @param cipher - CipherType object
209 # Need to be able to handle OPENSSL stacks
210 raise NotImplementedError
211 def decrypt(self,pkey,cert,flags=0):
214 @param pkey - private key to decrypt
215 @param cert - certificate of this private key (to find
216 neccessary RecipientInfo
218 @returns - decrypted data
221 raise ValueError("Specified keypair has no private part")
222 if pkey != cert.pubkey:
223 raise ValueError("Certificate doesn't match private key")
225 res=libcrypto.CMS_decrypt(self.ptr,pkey.ptr,cert.ccert,None,b.bio,flags)
227 raise CMSError("decrypting CMS")
230 class EncryptedData(CMSBase):
232 def create(data,cipher,key,flags=0):
234 Creates an EncryptedData message.
235 @param data data to encrypt
236 @param cipher cipher.CipherType object represening required
238 @param key - byte array used as simmetic key
239 @param flags - OR-ed combination of Flags constant
242 ptr=libcrypto.CMS_EncryptedData_encrypt(b.bio,cipher.cipher_type,key,len(key),flags)
244 raise CMSError("encrypt data")
245 return EncryptedData(ptr)
246 def decrypt(self,key,flags=0):
248 Decrypts encrypted data message
249 @param key - symmetic key to decrypt
250 @param flags - OR-ed combination of Flags constant
253 if libcrypto.CMS_EncryptedData_decrypt(self.ptr,key,len(key),None,
255 raise CMSError("decrypt data")
260 libcrypto.CMS_verify.restype=c_int
261 libcrypto.CMS_verify.argtypes=(c_void_p,c_void_p,c_void_p,c_void_p,c_void_p,c_int)