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, c_uint, c_size_t, POINTER
14 from ctypescrypto.exception import LibCryptoError
15 from ctypescrypto import libcrypto
16 from ctypescrypto.bio import Membio
17 from ctypescrypto.oid import Oid
18 from ctypescrypto.x509 import StackOfX509
20 class CMSError(LibCryptoError):
22 Exception which is raised when error occurs
28 Constants for flags passed to the CMS methods.
35 NO_SIGS = NO_CONTENT_VERIFY | NO_ATTR_VERIFY
37 NO_SIGNER_CERT_VERIFY = 0x20
50 DEBUG_DECRYPT = 0x20000
52 def CMS(data, format="PEM"):
54 Parses CMS data and returns either SignedData or EnvelopedData
59 ptr = libcrypto.PEM_read_bio_CMS(bio.bio, None, None, None)
61 ptr = libcrypto.d2i_CMS_bio(bio.bio, None)
62 typeoid = Oid(libcrypto.OBJ_obj2nid(libcrypto.CMS_get0_type(ptr)))
63 if typeoid.shortname() == "pkcs7-signedData":
64 return SignedData(ptr)
65 elif typeoid.shortname() == "pkcs7-envelopedData":
66 return EnvelopedData(ptr)
67 elif typeoid.shortname() == "pkcs7-encryptedData":
68 return EncryptedData(ptr)
70 raise NotImplementedError("cannot handle "+typeoid.shortname())
72 class CMSBase(object):
74 Common ancessor for all CMS types.
75 Implements serializatio/deserialization
77 def __init__(self, ptr=None):
81 Serialize in DER format
84 if not libcrypto.i2d_CMS_bio(bio.bio, self.ptr):
85 raise CMSError("writing CMS to PEM")
90 Serialize in PEM format
93 if not libcrypto.PEM_write_bio_CMS(bio.bio, self.ptr):
94 raise CMSError("writing CMS to PEM")
98 #pylint: disable=R0921
99 class SignedData(CMSBase):
101 Represents signed message (signeddata CMS type)
104 def create(data, cert, pkey, flags=Flags.BINARY, certs=None):
106 Creates SignedData message by signing data with pkey and
109 @param data - data to sign
110 @param cert - signer's certificate
111 @param pkey - pkey object with private key to sign
112 @param flags - OReed combination of Flags constants
113 @param certs - list of X509 objects to include into CMS
116 raise ValueError("Specified keypair has no private part")
117 if cert.pubkey != pkey:
118 raise ValueError("Certificate doesn't match public key")
120 if certs is not None and len(certs) > 0:
121 certstack = StackOfX509(certs)
124 ptr = libcrypto.CMS_sign(cert.cert, pkey.ptr, certstack, bio.bio, flags)
126 raise CMSError("signing message")
127 return SignedData(ptr)
128 def sign(self, cert, pkey, digest_type=None, data=None, flags=Flags.BINARY):
130 Adds another signer to already signed message
131 @param cert - signer's certificate
132 @param pkey - signer's private key
133 @param digest_type - message digest to use as DigestType object
134 (if None - default for key would be used)
135 @param data - data to sign (if detached and
136 Flags.REUSE_DIGEST is not specified)
137 @param flags - ORed combination of Flags consants
140 raise ValueError("Specified keypair has no private part")
141 if cert.pubkey != pkey:
142 raise ValueError("Certificate doesn't match public key")
143 if libcrypto.CMS_sign_add1_Signer(self.ptr, cert.cert, pkey.ptr,
144 digest_type.digest, flags) is None:
145 raise CMSError("adding signer")
146 if flags & Flags.REUSE_DIGEST == 0:
152 res = libcrypto.CMS_final(self.ptr, biodata, None, flags)
154 raise CMSError("Cannot finalize CMS")
155 def verify(self, store, flags, data=None, certs=None):
157 Verifies signature under CMS message using trusted cert store
159 @param store - X509Store object with trusted certs
160 @param flags - OR-ed combination of flag consants
161 @param data - message data, if messge has detached signature
162 param certs - list of certificates to use during verification
163 If Flags.NOINTERN is specified, these are only
164 sertificates to search for signing certificates
165 @returns True if signature valid, False otherwise
169 bio_obj = Membio(data)
171 if certs is not None and len(certs) > 0:
172 certstack = StackOfX509(certs)
175 res = libcrypto.CMS_verify(self.ptr, certstack, store.store, bio,
182 Return list of signer's certificates
184 signerlist = libcrypto.CMS_get0_signers(self.ptr)
185 if signerlist is None:
186 raise CMSError("Cannot get signers")
187 return StackOfX509(ptr=signerlist, disposable=False)
192 Returns signed data if present in the message
195 if not libcrypto.CMS_verify(self.ptr, None, None, None, bio.bio,
197 raise CMSError("extract data")
200 def addcert(self, cert):
202 Adds a certificate (probably intermediate CA) to the SignedData
205 if libcrypto.CMS_add1_cert(self.ptr, cert.cert) <= 0:
206 raise CMSError("Cannot add cert")
207 def addcrl(self, crl):
209 Adds a CRL to the signed data structure
211 raise NotImplementedError
215 List of the certificates contained in the structure
217 certstack = libcrypto.CMS_get1_certs(self.ptr)
218 if certstack is None:
219 raise CMSError("getting certs")
220 return StackOfX509(ptr=certstack, disposable=True)
224 List of the CRLs contained in the structure
226 raise NotImplementedError
228 class EnvelopedData(CMSBase):
230 Represents EnvelopedData CMS, i.e. message encrypted with session
231 keys, encrypted with recipient's public keys
234 def create(recipients, data, cipher, flags=0):
236 Creates and encrypts message
237 @param recipients - list of X509 objects
238 @param data - contents of the message
239 @param cipher - CipherType object
242 recp = StackOfX509(recipients)
244 cms_ptr = libcrypto.CMS_encrypt(recp.ptr, bio.bio, cipher.cipher_type,
247 raise CMSError("encrypt EnvelopedData")
248 return EnvelopedData(cms_ptr)
250 def decrypt(self, pkey, cert, flags=0):
253 @param pkey - private key to decrypt
254 @param cert - certificate of this private key (to find
255 neccessary RecipientInfo
257 @returns - decrypted data
260 raise ValueError("Specified keypair has no private part")
261 if pkey != cert.pubkey:
262 raise ValueError("Certificate doesn't match private key")
264 res = libcrypto.CMS_decrypt(self.ptr, pkey.ptr, cert.ccert, None,
267 raise CMSError("decrypting CMS")
270 class EncryptedData(CMSBase):
272 Represents encrypted data CMS structure, i.e. encrypted
273 with symmetric key, shared by sender and recepient.
276 def create(data, cipher, key, flags=0):
278 Creates an EncryptedData message.
279 @param data data to encrypt
280 @param cipher cipher.CipherType object represening required
282 @param key - byte array used as simmetic key
283 @param flags - OR-ed combination of Flags constant
286 ptr = libcrypto.CMS_EncryptedData_encrypt(bio.bio, cipher.cipher_type,
287 key, len(key), flags)
289 raise CMSError("encrypt data")
290 return EncryptedData(ptr)
292 def decrypt(self, key, flags=0):
294 Decrypts encrypted data message
295 @param key - symmetic key to decrypt
296 @param flags - OR-ed combination of Flags constant
299 if libcrypto.CMS_EncryptedData_decrypt(self.ptr, key, len(key), None,
300 bio.bio, flags) <= 0:
301 raise CMSError("decrypt data")
304 __all__ = ['CMS', 'CMSError', 'Flags', 'SignedData', 'EnvelopedData',
307 libcrypto.CMS_add1_cert.restype = c_int
308 libcrypto.CMS_add1_cert.argtypes = (c_void_p, c_void_p)
309 libcrypto.CMS_decrypt.restype = c_int
310 libcrypto.CMS_decrypt.argtypes = (c_void_p, c_void_p, c_void_p,
311 c_void_p, c_void_p, c_uint)
312 libcrypto.CMS_encrypt.restype = c_void_p
313 libcrypto.CMS_encrypt.argtypes = (c_void_p, c_void_p, c_void_p, c_uint)
314 libcrypto.CMS_EncryptedData_decrypt.restype = c_int
315 libcrypto.CMS_EncryptedData_decrypt.argtypes = (c_void_p, c_char_p, c_size_t,
316 c_void_p, c_void_p, c_uint)
317 libcrypto.CMS_EncryptedData_encrypt.restype = c_void_p
318 libcrypto.CMS_EncryptedData_encrypt.argtypes = (c_void_p, c_void_p, c_char_p,
320 libcrypto.CMS_final.restype = c_int
321 libcrypto.CMS_final.argtypes = (c_void_p, c_void_p, c_void_p, c_uint)
322 libcrypto.CMS_get0_signers.restype = c_void_p
323 libcrypto.CMS_get0_signers.argtypes = (c_void_p, )
324 libcrypto.CMS_get1_certs.restype = c_void_p
325 libcrypto.CMS_get1_certs.argtypes = (c_void_p, )
326 libcrypto.CMS_sign.restype = c_void_p
327 libcrypto.CMS_sign.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p, c_uint)
328 libcrypto.CMS_sign_add1_Signer.restype = c_void_p
329 libcrypto.CMS_sign_add1_Signer.argtypes = (c_void_p, c_void_p, c_void_p,
331 libcrypto.CMS_verify.restype = c_int
332 libcrypto.CMS_verify.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p,
334 libcrypto.d2i_CMS_bio.restype = c_void_p
335 libcrypto.d2i_CMS_bio.argtypes = (c_void_p, POINTER(c_void_p))
336 libcrypto.i2d_CMS_bio.restype = c_int
337 libcrypto.i2d_CMS_bio.argtypes = (c_void_p, c_void_p)
338 libcrypto.PEM_read_bio_CMS.restype = c_void_p
339 libcrypto.PEM_read_bio_CMS.argtypes = (c_void_p, POINTER(c_void_p),
341 libcrypto.PEM_write_bio_CMS.restype = c_int
342 libcrypto.PEM_write_bio_CMS.argtypes = (c_void_p, c_void_p)