ca.py 5.05 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from OpenSSL import crypto
import logging
import os
import subprocess
import time

from autoca import ca_storage
from autoca import certutil

log = logging.getLogger(__name__)


class CA(object):

    def __init__(self, root, subject, bits=1024, digest='sha1'):
        self.ca_subject = subject
        self.bits = bits
        self.digest = digest
        self.storage = ca_storage.FileStorage(root)
        self._init_ca()
ale's avatar
ale committed
21
        self._load_crl()
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

    def _init_ca(self):
        key_str, crt_str = self.storage.get_ca()
        if key_str:
            self.ca_key = crypto.load_privatekey(
                crypto.FILETYPE_PEM, key_str)
            self.ca_crt = crypto.load_certificate(
                crypto.FILETYPE_PEM, crt_str)
            self.public_ca_pem = crt_str
        else:
            log.info('initializing CA certificate and private key')
            self.ca_key = certutil.create_rsa_key_pair(self.bits)
            ca_req = certutil.create_cert_request(
                self.ca_key, **(self.ca_subject))
            self.ca_crt = certutil.sign_certificate(
                ca_req, self.ca_key, ca_req, 1, 3650,
                extensions=[
                    crypto.X509Extension('basicConstraints', True,
                                         'CA:TRUE, pathlen:0'),
                    crypto.X509Extension('keyUsage', True,
                                         'keyCertSign, cRLSign'),
                    #crypto.X509Extension('subjectKeyIdentifier', False,
                    #                     'hash', subject=ca_req),
                    ],
                digest=self.digest)

            crt_str = crypto.dump_certificate(
                crypto.FILETYPE_PEM, self.ca_crt)
            self.storage.set_ca(
                crypto.dump_privatekey(crypto.FILETYPE_PEM, self.ca_key),
                crt_str)
            self.public_ca_pem = crt_str

    def sign_certificate(self, req, days=365, server=False):
        cn = req.get_subject().CN
        log.info('sign request for cn=%s', cn)
        cert = self.get_certificate(cn)
        if cert:
            log.info('a valid certificate already exists for cn=%s, '
                     'revoking it', cn)
            self._revoke_certificate(cn, cert.get_serial_number())
        new_serial = self.storage.get_next_serial()
        extensions = [
            crypto.X509Extension('basicConstraints', True, 'CA:FALSE'),
            crypto.X509Extension('keyUsage', True,
                                 '%sdigitalSignature, keyEncipherment' % (
                    server and '' or 'nonRepudiation, ')),
            crypto.X509Extension('extendedKeyUsage', True,
                                 server and 'serverAuth' or 'clientAuth'),
            crypto.X509Extension('nsCertType', True,
                                 server and 'server' or 'client'),
            ]
        cert = certutil.sign_certificate(
            req, self.ca_key, self.ca_crt, new_serial, days,
            extensions=extensions, digest=self.digest)
        self.storage.store_certificate(
            cn, crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
        return cert

    def _update_crl(self):
ale's avatar
ale committed
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
        crl = crypto.CRL()
        for serial, stamp in self.storage.get_revoked():
            revoked = crypto.Revoked()
            revoked.set_serial(str(serial))
            revoked.set_rev_date(
                time.strftime('%Y%m%d%H%M%SZ', time.gmtime(stamp)))
            crl.add_revoked(revoked)
        self.storage.set_crl(
            crl.export(self.ca_crt, self.ca_key, crypto.FILETYPE_PEM, 30))
        self._load_crl()

    def _load_crl(self):
        self.crl_data_pem = self.storage.get_crl()
        if not self.crl_data_pem:
            # Create an empty CRL.
            crl = crypto.CRL()
            self.crl_data_pem = crl.export(self.ca_crt, self.ca_key,
                                           crypto.FILETYPE_PEM, 30)
            self.storage.set_crl(self.crl_data_pem)
        log.debug('CRL PEM data: %s', self.crl_data_pem)
        # Re-read the CRL data in DER and PEM formats.
        pipe = subprocess.Popen(
            ['openssl', 'crl', '-inform', 'PEM', '-outform', 'DER'],
            stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        self.crl_data_der = pipe.communicate(self.crl_data_pem)[0]
107
108
109
110

    def _revoke_certificate(self, cn, serial_num):
        log.debug('revoking certificate: cn=%s, serial=%s', cn, serial_num)

ale's avatar
ale committed
111
112
113
114
        #serial_hex = '%X' % int(serial_num)
        revoked = self.storage.get_revoked()
        if serial_num in revoked:
            return
115
116

        self.storage.delete_certificate(cn)
ale's avatar
ale committed
117
        self.storage.add_revoked(serial_num)
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
        self._update_crl()

    def revoke_certificate(self, cn):
        serial_num = self.get_serial(cn)
        if serial_num:
           self._revoke_certificate(cn, serial_num) 

    def get_certificate(self, cn):
        data = self.storage.get_certificate(cn)
        if data:
            return crypto.load_certificate(crypto.FILETYPE_PEM, data)

    def get_serial(self, cn):
        crt = self.get_certificate(cn)
        if crt:
            return crt.get_serial_number()