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)
63 raise CMSError("Error parsing CMS data")
64 typeoid = Oid(libcrypto.OBJ_obj2nid(libcrypto.CMS_get0_type(ptr)))
65 if typeoid.shortname() == "pkcs7-signedData":
66 return SignedData(ptr)
67 elif typeoid.shortname() == "pkcs7-envelopedData":
68 return EnvelopedData(ptr)
69 elif typeoid.shortname() == "pkcs7-encryptedData":
70 return EncryptedData(ptr)
72 raise NotImplementedError("cannot handle "+typeoid.shortname())
74 class CMSBase(object):
76 Common ancessor for all CMS types.
77 Implements serializatio/deserialization
79 def __init__(self, ptr=None):
83 Serialize in DER format
86 if not libcrypto.i2d_CMS_bio(bio.bio, self.ptr):
87 raise CMSError("writing CMS to DER")
92 Serialize in PEM format
95 if not libcrypto.PEM_write_bio_CMS(bio.bio, self.ptr):
96 raise CMSError("writing CMS to PEM")
100 #pylint: disable=R0921
101 class SignedData(CMSBase):
103 Represents signed message (signeddata CMS type)
106 def create(data, cert, pkey, flags=Flags.BINARY, certs=None):
108 Creates SignedData message by signing data with pkey and
111 @param data - data to sign
112 @param cert - signer's certificate
113 @param pkey - pkey object with private key to sign
114 @param flags - OReed combination of Flags constants
115 @param certs - list of X509 objects to include into CMS
118 raise ValueError("Specified keypair has no private part")
119 if cert.pubkey != pkey:
120 raise ValueError("Certificate doesn't match public key")
122 if certs is not None and len(certs) > 0:
123 certstack = StackOfX509(certs).ptr
126 ptr = libcrypto.CMS_sign(cert.cert, pkey.key, certstack, bio.bio, flags)
128 raise CMSError("signing message")
129 return SignedData(ptr)
130 def sign(self, cert, pkey, digest_type=None, data=None, flags=Flags.BINARY):
132 Adds another signer to already signed message
133 @param cert - signer's certificate
134 @param pkey - signer's private key
135 @param digest_type - message digest to use as DigestType object
136 (if None - default for key would be used)
137 @param data - data to sign (if detached and
138 Flags.REUSE_DIGEST is not specified)
139 @param flags - ORed combination of Flags consants
142 raise ValueError("Specified keypair has no private part")
143 if cert.pubkey != pkey:
144 raise ValueError("Certificate doesn't match public key")
145 if libcrypto.CMS_add1_signer(self.ptr, cert.cert, pkey.key,
146 digest_type.digest, flags) is None:
147 raise CMSError("adding signer")
148 if flags & Flags.REUSE_DIGEST == 0:
154 res = libcrypto.CMS_final(self.ptr, biodata, None, flags)
156 raise CMSError("Cannot finalize CMS")
157 def verify(self, store, flags, data=None, certs=None):
159 Verifies signature under CMS message using trusted cert store
161 @param store - X509Store object with trusted certs
162 @param flags - OR-ed combination of flag consants
163 @param data - message data, if messge has detached signature
164 param certs - list of certificates to use during verification
165 If Flags.NOINTERN is specified, these are only
166 sertificates to search for signing certificates
167 @returns True if signature valid, False otherwise
171 bio_obj = Membio(data)
173 if certs is not None and len(certs) > 0:
174 certstack = StackOfX509(certs)
177 res = libcrypto.CMS_verify(self.ptr, certstack, store.store, bio,
184 Return list of signer's certificates
186 signerlist = libcrypto.CMS_get0_signers(self.ptr)
187 if signerlist is None:
188 raise CMSError("Cannot get signers")
189 return StackOfX509(ptr=signerlist, disposable=False)
194 Returns signed data if present in the message
197 if not libcrypto.CMS_verify(self.ptr, None, None, None, bio.bio,
199 raise CMSError("extract data")
202 def addcert(self, cert):
204 Adds a certificate (probably intermediate CA) to the SignedData
207 if libcrypto.CMS_add1_cert(self.ptr, cert.cert) <= 0:
208 raise CMSError("Cannot add cert")
209 def addcrl(self, crl):
211 Adds a CRL to the signed data structure
213 raise NotImplementedError
217 List of the certificates contained in the structure
219 certstack = libcrypto.CMS_get1_certs(self.ptr)
220 if certstack is None:
221 raise CMSError("getting certs")
222 return StackOfX509(ptr=certstack, disposable=True)
226 List of the CRLs contained in the structure
228 raise NotImplementedError
230 class EnvelopedData(CMSBase):
232 Represents EnvelopedData CMS, i.e. message encrypted with session
233 keys, encrypted with recipient's public keys
236 def create(recipients, data, cipher, flags=0):
238 Creates and encrypts message
239 @param recipients - list of X509 objects
240 @param data - contents of the message
241 @param cipher - CipherType object
244 recp = StackOfX509(recipients)
246 cms_ptr = libcrypto.CMS_encrypt(recp.ptr, bio.bio, cipher.cipher_type,
249 raise CMSError("encrypt EnvelopedData")
250 return EnvelopedData(cms_ptr)
252 def decrypt(self, pkey, cert, flags=0):
255 @param pkey - private key to decrypt
256 @param cert - certificate of this private key (to find
257 neccessary RecipientInfo
259 @returns - decrypted data
262 raise ValueError("Specified keypair has no private part")
263 if pkey != cert.pubkey:
264 raise ValueError("Certificate doesn't match private key")
266 res = libcrypto.CMS_decrypt(self.ptr, pkey.key, cert.ccert, None,
269 raise CMSError("decrypting CMS")
272 class EncryptedData(CMSBase):
274 Represents encrypted data CMS structure, i.e. encrypted
275 with symmetric key, shared by sender and recepient.
278 def create(data, cipher, key, flags=0):
280 Creates an EncryptedData message.
281 @param data data to encrypt
282 @param cipher cipher.CipherType object represening required
284 @param key - byte array used as simmetic key
285 @param flags - OR-ed combination of Flags constant
288 ptr = libcrypto.CMS_EncryptedData_encrypt(bio.bio, cipher.cipher_type,
289 key, len(key), flags)
291 raise CMSError("encrypt data")
292 return EncryptedData(ptr)
294 def decrypt(self, key, flags=0):
296 Decrypts encrypted data message
297 @param key - symmetic key to decrypt
298 @param flags - OR-ed combination of Flags constant
301 if libcrypto.CMS_EncryptedData_decrypt(self.ptr, key, len(key), None,
302 bio.bio, flags) <= 0:
303 raise CMSError("decrypt data")
306 __all__ = ['CMS', 'CMSError', 'Flags', 'SignedData', 'EnvelopedData',
309 libcrypto.CMS_get0_type.restype = c_void_p
310 libcrypto.CMS_get0_type.argtypes = (c_void_p,)
311 libcrypto.CMS_add1_cert.restype = c_int
312 libcrypto.CMS_add1_cert.argtypes = (c_void_p, c_void_p)
313 libcrypto.CMS_decrypt.restype = c_int
314 libcrypto.CMS_decrypt.argtypes = (c_void_p, c_void_p, c_void_p,
315 c_void_p, c_void_p, c_uint)
316 libcrypto.CMS_encrypt.restype = c_void_p
317 libcrypto.CMS_encrypt.argtypes = (c_void_p, c_void_p, c_void_p, c_uint)
318 libcrypto.CMS_EncryptedData_decrypt.restype = c_int
319 libcrypto.CMS_EncryptedData_decrypt.argtypes = (c_void_p, c_char_p, c_size_t,
320 c_void_p, c_void_p, c_uint)
321 libcrypto.CMS_EncryptedData_encrypt.restype = c_void_p
322 libcrypto.CMS_EncryptedData_encrypt.argtypes = (c_void_p, c_void_p, c_char_p,
324 libcrypto.CMS_final.restype = c_int
325 libcrypto.CMS_final.argtypes = (c_void_p, c_void_p, c_void_p, c_uint)
326 libcrypto.CMS_get0_signers.restype = c_void_p
327 libcrypto.CMS_get0_signers.argtypes = (c_void_p, )
328 libcrypto.CMS_get1_certs.restype = c_void_p
329 libcrypto.CMS_get1_certs.argtypes = (c_void_p, )
330 libcrypto.CMS_sign.restype = c_void_p
331 libcrypto.CMS_sign.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p, c_uint)
332 libcrypto.CMS_add1_signer.restype = c_void_p
333 libcrypto.CMS_add1_signer.argtypes = (c_void_p, c_void_p, c_void_p,
335 libcrypto.CMS_verify.restype = c_int
336 libcrypto.CMS_verify.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p,
338 libcrypto.d2i_CMS_bio.restype = c_void_p
339 libcrypto.d2i_CMS_bio.argtypes = (c_void_p, POINTER(c_void_p))
340 libcrypto.i2d_CMS_bio.restype = c_int
341 libcrypto.i2d_CMS_bio.argtypes = (c_void_p, c_void_p)
342 libcrypto.PEM_read_bio_CMS.restype = c_void_p
343 libcrypto.PEM_read_bio_CMS.argtypes = (c_void_p, POINTER(c_void_p),
345 libcrypto.PEM_write_bio_CMS.restype = c_int
346 libcrypto.PEM_write_bio_CMS.argtypes = (c_void_p, c_void_p)