1
2
3
4
5
6
7 """
8 Core functions to verify, sign, encrypt and decrypt SMIME data, build just on
9 top of M2Crypto library wrapper to OpenSSL.
10 """
11
12 import os
13 import base64
14 import logging
15
16 from M2Crypto import SMIME, X509, Rand, m2
17 from util import BIO_from_buffer, set_keyring, set_certificate
18
19
21 """
22 Exception raised if requested PKCS#7 type is not valid.
23 Ammitted values are PEM and DER.
24 """
25 pass
26
27
29 """
30 Exception raised if the reference certstore for verification is not
31 available.
32 """
33 pass
34
35
37 """
38 Exception raised if the input PKCS#7 is not a signed PKCS#7.
39 """
40 pass
41
42
43 -def encrypt(input_bio, cert, keyring_source, cypher):
44 """
45 Encrypts the input data with the public key in the certificate from keyring
46 source with selected cypher.
47
48 @type input_bio: M2Crypto.BIO
49 @param input_bio: input data to encrypt.
50 @type cert: filepath or M2Crypto.BIO or M2Crypto.X509.X509
51 @param cert: the recipient certificate reference from filepath, could be
52 from file, from memory or from pkcs11 smartcard, based on
53 keyring_soruce input parameter.
54 @type keyring_source: str
55 @keyword keyring_source: the type of the source for input certificate, used
56 to recall the appropriate method for encrypter settings. Ammitted
57 values are: file, memory, pkcs11.
58 @type cypher: str
59 @keyword cypher: the cypher to use for encryption of the data, run
60 "openssl enc -help" for supported cyphers, you have to choose a public
61 key cypher from availables.
62 @rtype: M2Crypto.SMIME.PKCS7
63 @return: the PKCS#7 encrypted data in PEM format.
64 """
65 encrypter = SMIME.SMIME()
66 x509 = set_certificate(cert, keyring_source)
67 sk = X509.X509_Stack()
68 sk.push(x509)
69 encrypter.set_x509_stack(sk)
70 encrypter.set_cipher(SMIME.Cipher(cypher))
71 Rand.load_file('randpool.dat', -1)
72 try:
73 p7 = encrypter.encrypt(input_bio)
74 except SMIME.SMIME_Error, e:
75 logging.error('smime error: ' + str(e))
76 raise
77 except SMIME.PKCS7_Error, e:
78 logging.error('pkcs7 error: ' + str(e))
79 raise
80 Rand.save_file('randpool.dat')
81 return p7
82
83
84 -def decrypt(input_bio, private_key, cert, keyring_source, type):
85 """
86 Decrypts the input data with the private key and the certificate from
87 keyring source.
88
89 @type input_bio: M2Crypto.BIO
90 @param input_bio: input data to sign.
91 @type private_key: filepath or M2Crypto.BIO or M2Crypto.EVP.PKey
92 @param private_key: recipient private key reference, could be from file,
93 from memory or from pkcs11 smartcard, based on keyring_soruce input
94 parameter.
95 @type cert: filepath or M2Crypto.BIO or M2Crypto.X509.X509
96 @param cert: recipient certificate, could be from filepath, from memory or
97 from pkcs11 smartcard, based on keyring_soruce input parameter.
98 @type keyring_source: str
99 @keyword keyring_source: the type of the source for input certificate, used
100 to recall the appropriate method for decrypter settings. Ammitted
101 values are: file, memory, pkcs11.
102 @type type: str
103 @keyword type: specifies the type of input PKCS#7 data: PEM or DER
104 @rtype: str
105 @return: the decrypted data in plain form.
106 @raise BadPKCS7Type: The requested PKCS#7 type is not valid. Ammitted
107 values are PEM and DER.
108 """
109 decrypter = SMIME.SMIME()
110 set_keyring(decrypter, private_key, cert, keyring_source)
111 try:
112 if type == 'PEM':
113 p7, data_bio = SMIME.smime_load_pkcs7_bio(input_bio)
114 elif type == 'DER':
115 p7 = SMIME.PKCS7(m2.pkcs7_read_bio_der(input_bio._ptr()), 1)
116 else:
117 logging.error('pkcs7 type error: unknown type')
118 raise BadPKCS7Type('unknown type: ' + type +
119 '; possible values: PEM, DER')
120 except SMIME.SMIME_Error, e:
121 logging.error('load pkcs7 error: ' + str(e))
122 pass
123 try:
124 decrypted_data = decrypter.decrypt(p7)
125 except SMIME.SMIME_Error, e:
126 logging.error('smime error: ' + str(e))
127 raise
128 except SMIME.PKCS7_Error, e:
129 logging.error('pkcs7 error: ' + str(e))
130 raise
131 return decrypted_data.replace('\r', '')
132
133
134 -def sign(input_bio, private_key, cert, keyring_source, type):
135 """
136 Signs the input data with the private key and the certificate from keyring
137 source.
138
139 @type input_bio: M2Crypto.BIO
140 @param input_bio: input data to sign.
141 @type private_key: filepath or M2Crypto.BIO or M2Crypto.EVP.PKey
142 @param private_key: sender private key reference, could be from file,
143 from memory or from pkcs11 smartcard, based on keyring_soruce input
144 parameter.
145 @type cert: filepath or M2Crypto.BIO or M2Crypto.X509.X509
146 @param cert: sender certificate, could be from filepath, from memory or
147 from pkcs11 smartcard, based on keyring_soruce input parameter.
148 @type keyring_source: str
149 @keyword keyring_source: the type of the source for input certificate, used
150 to recall the appropriate method for signer settings. Ammitted
151 values are: file, memory, pkcs11.
152 @type type: str
153 @keyword type: specifies the type of output PKCS#7 data: PEM or DER
154 @rtype: M2Crypto.SMIME.PKCS7
155 @return: the PKCS#7 signed data in PEM or DER format.
156 """
157 signer = SMIME.SMIME()
158 set_keyring(signer, private_key, cert, keyring_source)
159 Rand.load_file('randpool.dat', -1)
160 try:
161 if type == 'PEM':
162 p7 = signer.sign(input_bio, flags=SMIME.PKCS7_DETACHED)
163 elif type == 'DER':
164 p7 = signer.sign(input_bio)
165 else:
166 logging.error('pkcs7 type error: unknown type')
167 raise BadPKCS7Type('unknown type: ' + type +
168 '; possible values: PEM, DER')
169 except SMIME.SMIME_Error, e:
170 logging.error('smime error: ' + str(e))
171 raise
172 except SMIME.PKCS7_Error, e:
173 logging.error('pkcs7 error: ' + str(e))
174 raise
175 Rand.save_file('randpool.dat')
176 return p7
177
178
179 -def verify(input_bio, certstore_path, AUTO_SIGNED_CERT, type):
180 """
181 Retrieves X.509 certificate from input data and verifies signed message
182 using as certificate store input certstore, inspired by:
183 U{http://code.activestate.com/recipes/285211/}.
184
185 @type input_bio: M2Crypto.BIO
186 @param input_bio: input data to verify
187 @type certstore_path: filepath
188 @param certstore_path: path to the file of the trusted certificates,
189 for example /etc/ssl/certs/ca-certificats.crt.
190 @type type: str
191 @keyword type: specifies the type of input PKCS#7 data: PEM or DER
192 @type AUTO_SIGNED_CERT: boolean
193 @keyword AUTOSIGNED_CERT: to accept or not auto signed certificates as
194 valid for verification.
195 @rtype: list or None
196 @return: a list of verified certificates retrieved from the original data
197 if verification success, else None.
198 @raise CertStoreNotAvailable: the reference certstore for verification is
199 not available.
200 @raise MissingSignerCertificate: the input PKCS#7 is not a signed PKCS#7.
201 """
202 signer = SMIME.SMIME()
203 cert_store = X509.X509_Store()
204 if not os.access(certstore_path, os.R_OK):
205 logging.error('certstore not available for verify')
206 raise CertStoreNotAvailable('certstore not available %' %
207 (certstore_path))
208 cert_store.load_info(certstore_path)
209 signer.set_x509_store(cert_store)
210 data_bio = None
211 try:
212 if type == 'PEM':
213 p7, data_bio = SMIME.smime_load_pkcs7_bio(input_bio)
214 elif type == 'DER':
215 p7 = SMIME.PKCS7(m2.pkcs7_read_bio_der(input_bio._ptr()), 1)
216 else:
217 logging.error('pkcs7 type error: unknown type')
218 raise BadPKCS7Type('unknown type: ' + type +
219 '; possible values: PEM, DER')
220 except SMIME.SMIME_Error, e:
221 logging.error('load pkcs7 error: ' + str(e))
222 raise
223 if data_bio is not None:
224 data = data_bio.read()
225 data_bio = BIO_from_buffer(data)
226 sk3 = p7.get0_signers(X509.X509_Stack())
227 if len(sk3) == 0:
228 logging.error('missing certificate')
229 raise MissingSignerCertificate('missing certificate')
230 signer_certs = []
231 for cert in sk3:
232 signer_certs.append(
233 "-----BEGIN CERTIFICATE-----\n%s-----END CERTIFICATE-----\n"
234 % base64.encodestring(cert.as_der()))
235 signer.set_x509_stack(sk3)
236 v = None
237 try:
238 if AUTO_SIGNED_CERT:
239 v = signer.verify(p7, data_bio, flags=SMIME.PKCS7_NOVERIFY)
240 else:
241 v = signer.verify(p7, data_bio)
242 except SMIME.SMIME_Error, e:
243 logging.error('smime error: ' + str(e))
244 raise
245 except SMIME.PKCS7_Error, e:
246 logging.error('pkcs7 error: ' + str(e))
247 raise
248 if data_bio is not None and data != v and v is not None:
249 return
250 return signer_certs
251