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,certs=[]):
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
107 @param flags - OReed combination of Flags constants
108 @param certs - list of X509 objects to include into CMS
111 raise ValueError("Specified keypair has no private part")
112 if cert.pubkey!=pkey:
113 raise ValueError("Certificate doesn't match public key")
115 if certs is not None and len(certs)>0:
116 certstack=StackOfX509(certs)
119 ptr=libcrypto.CMS_sign(cert.cert,pkey.ptr,certstack,b.bio,flags)
121 raise CMSError("signing message")
122 return SignedData(ptr)
123 def sign(self,cert,pkey,md=None,data=None,flags=Flags.BINARY):
125 Adds another signer to already signed message
126 @param cert - signer's certificate
127 @param pkey - signer's private key
128 @param md - message digest to use as DigestType object
129 (if None - default for key would be used)
130 @param data - data to sign (if detached and
131 Flags.REUSE_DIGEST is not specified)
132 @param flags - ORed combination of Flags consants
135 raise ValueError("Specified keypair has no private part")
136 if cert.pubkey!=pkey:
137 raise ValueError("Certificate doesn't match public key")
138 p1=libcrypto.CMS_sign_add1_Signer(self.ptr,cert.cert,pkey.ptr,
141 raise CMSError("adding signer")
142 if flags & Flags.REUSE_DIGEST==0:
148 res= libcrypto.CMS_final(self.ptr,biodata,None,flags)
151 def verify(self,store,flags,data=None,certs=[]):
153 Verifies signature under CMS message using trusted cert store
155 @param store - X509Store object with trusted certs
156 @param flags - OR-ed combination of flag consants
157 @param data - message data, if messge has detached signature
158 param certs - list of certificates to use during verification
159 If Flags.NOINTERN is specified, these are only
160 sertificates to search for signing certificates
161 @returns True if signature valid, False otherwise
167 if certs is not None and len(certs)>0:
168 certstack=StackOfX509(certs)
171 res=libcrypto.CMS_verify(self.ptr,certstack,store.store,bio,None,flags)
174 def signers(self,store=None):
176 Return list of signer's certificates
178 p=libcrypto.CMS_get0_signers(self.ptr)
181 return StackOfX509(ptr=p,disposable=False)
185 Returns signed data if present in the message
188 if not libcrypto.CMS_verify(self.ptr,None,None,None,b.bio,Flags.NO_VERIFY):
189 raise CMSError("extract data")
191 def addcert(self,cert):
193 Adds a certificate (probably intermediate CA) to the SignedData
196 if libcrypto.CMS_add1_cert(self.ptr,cert.cert)<=0:
197 raise CMSError("adding cert")
198 def addcrl(self,crl):
200 Adds a CRL to the signed data structure
202 raise NotImplementedError
206 List of the certificates contained in the structure
208 p=CMS_get1_certs(self.ptr)
210 raise CMSError("getting certs")
211 return StackOfX509(ptr=p,disposable=True)
215 List of the CRLs contained in the structure
217 raise NotImplementedError
219 class EnvelopedData(CMSBase):
221 def create(recipients,data,cipher,flags=0):
223 Creates and encrypts message
224 @param recipients - list of X509 objects
225 @param data - contents of the message
226 @param cipher - CipherType object
229 recp=StackOfX509(recipients)
231 p=libcrypto.CMS_encrypt(recp.ptr,b.bio,cipher.cipher_type,flags)
233 raise CMSError("encrypt EnvelopedData")
234 return EnvelopedData(p)
235 def decrypt(self,pkey,cert,flags=0):
238 @param pkey - private key to decrypt
239 @param cert - certificate of this private key (to find
240 neccessary RecipientInfo
242 @returns - decrypted data
245 raise ValueError("Specified keypair has no private part")
246 if pkey != cert.pubkey:
247 raise ValueError("Certificate doesn't match private key")
249 res=libcrypto.CMS_decrypt(self.ptr,pkey.ptr,cert.ccert,None,b.bio,flags)
251 raise CMSError("decrypting CMS")
254 class EncryptedData(CMSBase):
256 def create(data,cipher,key,flags=0):
258 Creates an EncryptedData message.
259 @param data data to encrypt
260 @param cipher cipher.CipherType object represening required
262 @param key - byte array used as simmetic key
263 @param flags - OR-ed combination of Flags constant
266 ptr=libcrypto.CMS_EncryptedData_encrypt(b.bio,cipher.cipher_type,key,len(key),flags)
268 raise CMSError("encrypt data")
269 return EncryptedData(ptr)
270 def decrypt(self,key,flags=0):
272 Decrypts encrypted data message
273 @param key - symmetic key to decrypt
274 @param flags - OR-ed combination of Flags constant
277 if libcrypto.CMS_EncryptedData_decrypt(self.ptr,key,len(key),None,
279 raise CMSError("decrypt data")
284 libcrypto.CMS_verify.restype=c_int
285 libcrypto.CMS_verify.argtypes=(c_void_p,c_void_p,c_void_p,c_void_p,c_void_p,c_int)