/
m2crypto_ca.py
192 lines (161 loc) · 5.03 KB
/
m2crypto_ca.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
"""
Tools for creating a CA cert and signed server certs.
Divined from http://svn.osafoundation.org/m2crypto/trunk/tests/test_x509.py
The mk_temporary_xxx calls return a NamedTemporaryFile with certs.
Got it from: https://gist.github.com/eskil/2338529
"""
import time
from tempfile import NamedTemporaryFile as namedtmp
from M2Crypto import X509, EVP, RSA, ASN1
__author__ = 'eskil@yelp.com'
__all__ = ['mk_temporary_cacert', 'mk_temporary_cert']
def mk_ca_issuer():
"""
Our default CA issuer name.
"""
issuer = X509.X509_Name()
issuer.C = "ES"
issuer.CN = "ca_testing_server"
issuer.ST = 'PO'
issuer.L = 'Vigo'
issuer.O = 'ca_yelp'
issuer.OU = 'ca_testing'
return issuer
def mk_cert_valid(cert, days=3650):
"""
Make a cert valid from now and til 'days' from now.
Args:
cert -- cert to make valid
days -- number of days cert is valid for from now.
"""
t = int(time.time())
now = ASN1.ASN1_UTCTIME()
now.set_time(t)
expire = ASN1.ASN1_UTCTIME()
expire.set_time(t + days * 24 * 60 * 60)
cert.set_not_before(now)
cert.set_not_after(expire)
def mk_request(bits, cn='localhost'):
"""
Create a X509 request with the given number of bits in they key.
Args:
bits -- number of RSA key bits
cn -- common name in the request
Returns a X509 request and the private key (EVP)
"""
pk = EVP.PKey()
x = X509.Request()
rsa = RSA.gen_key(bits, 65537, lambda: None)
pk.assign_rsa(rsa)
x.set_pubkey(pk)
name = x.get_subject()
name.C = "ES"
name.CN = cn
name.ST = 'PO'
name.O = 'yelp'
name.OU = 'testing'
x.sign(pk, 'sha1')
return x, pk
def mk_cacert():
"""
Make a CA certificate.
Returns the certificate, private key and public key.
"""
req, pk = mk_request(1024)
pkey = req.get_pubkey()
cert = X509.X509()
cert.set_serial_number(1)
cert.set_version(2)
mk_cert_valid(cert)
cert.set_issuer(mk_ca_issuer())
cert.set_subject(cert.get_issuer())
cert.set_pubkey(pkey)
cert.add_ext(X509.new_extension('basicConstraints', 'CA:TRUE'))
cert.add_ext(X509.new_extension('subjectKeyIdentifier', cert.get_fingerprint()))
cert.sign(pk, 'sha1')
return cert, pk, pkey
def mk_cert():
"""
Make a certificate.
Returns a new cert.
"""
cert = X509.X509()
cert.set_serial_number(2)
cert.set_version(2)
mk_cert_valid(cert)
cert.add_ext(X509.new_extension('nsComment', 'SSL sever'))
return cert
def mk_casigned_cert():
"""
Create a CA cert + server cert + server private key.
"""
# unused, left for history.
cacert, pk1, _ = mk_cacert()
cert_req, pk2 = mk_request(1024, cn='testing_server')
cert = mk_cert(cacert)
cert.set_subject(cert_req.get_subject())
cert.set_pubkey(cert_req.get_pubkey())
cert.sign(pk1, 'sha1')
return cacert, cert, pk2
def mk_temporary_cacert():
"""
Create a temporary CA cert.
Returns a tuple of NamedTemporaryFiles holding the CA cert and private key.
"""
cacert, pk1, pkey = mk_cacert()
cacertf = namedtmp()
cacertf.write(cacert.as_pem())
cacertf.flush()
pk1f = namedtmp()
pk1f.write(pk1.as_pem(None))
pk1f.flush()
return cacertf, pk1f
def mk_temporary_cert(cacert_file, ca_key_file, cn):
"""
Create a temporary certificate signed by the given CA, and with the given common name.
If cacert_file and ca_key_file is None, the certificate will be self-signed.
Args:
cacert_file -- file containing the CA certificate
ca_key_file -- file containing the CA private key
cn -- desired common name
Returns a namedtemporary file with the certificate and private key
"""
cert_req, pk2 = mk_request(1024, cn=cn)
if cacert_file and ca_key_file:
cacert = X509.load_cert(cacert_file)
pk1 = EVP.load_key(ca_key_file)
else:
cacert = None
pk1 = None
certificate = mk_cert()
certificate.set_subject(cert_req.get_subject())
certificate.set_pubkey(cert_req.get_pubkey())
if cacert and pk1:
certificate.set_issuer(cacert.get_issuer())
certificate.sign(pk1, 'sha1')
else:
certificate.set_issuer(certificate.get_subject())
certificate.sign(pk2, 'sha1')
cert_file = namedtmp()
key_file = namedtmp()
cert_file.write(certificate.as_pem())
key_file.write(pk2.as_pem(None))
cert_file.flush()
key_file.flush()
return cert_file, key_file
if __name__ == '__main__':
ca_cert, cert, pk = mk_casigned_cert()
with open('cacert.crt', 'wb') as f:
f.write(ca_cert.as_pem())
with open('cert.crt', 'wb') as f:
f.write(cert.as_pem())
f.write(pk.as_pem(None))
# Sanity checks...
cac = X509.load_cert('cacert.crt')
print(cac.verify(), cac.check_ca())
cc = X509.load_cert('cert.crt')
print(cc.verify(cac.get_pubkey()))
# protips
# openssl verify -CAfile cacert.crt cacert.crt cert.crt
# openssl x509 -in cert.crt -noout -text
# openssl x509 -in cacert.crt -noout -text