allow to set custom subject when signing certificate
... | @@ -42,6 +42,22 @@ MIN_CA_RENEW_PERIOD = 2 | ... | @@ -42,6 +42,22 @@ MIN_CA_RENEW_PERIOD = 2 |
DEFAULT_DIGEST_LIST = ['sha256', 'sha384', 'sha512'] | DEFAULT_DIGEST_LIST = ['sha256', 'sha384', 'sha512'] | ||
SUBJECT_KEY_LIST = ['C', 'ST', 'L', 'OU', 'O', 'CN', 'emailAddress'] | SUBJECT_KEY_LIST = ['C', 'ST', 'L', 'OU', 'O', 'CN', 'emailAddress'] | ||
def x509_name(**attrs): | |||
""" | |||
Return a new X509Name with the given attributes. | |||
""" | |||
# XXX There's no other way to get a new X509Name. | |||
name = crypto.X509().get_subject() | |||
attrs = list(attrs.items()) | |||
|
|||
# Make the order stable - order matters! | |||
|
|||
def key(attr): | |||
return attr[1] | |||
attrs.sort(key=key) | |||
for k, v in attrs: | |||
setattr(name, k, v) | |||
return name | |||
class CertificateAuthority(object): | class CertificateAuthority(object): | ||
def __init__(self, storage, ca_life_period, ca_renew_period, | def __init__(self, storage, ca_life_period, ca_renew_period, | ||
crt_life_time, crl_renew_period, digest_list=None, | crt_life_time, crl_renew_period, digest_list=None, | ||
... | @@ -210,7 +226,7 @@ class CertificateAuthority(object): | ... | @@ -210,7 +226,7 @@ class CertificateAuthority(object): |
""" | """ | ||
return self._storage.getPendingCertificateRequestList(limit, with_data) | return self._storage.getPendingCertificateRequestList(limit, with_data) | ||
def createCertificate(self, csr_id, ca_key_pair=None): | def createCertificate(self, csr_id, ca_key_pair=None, subject_dict=None): | ||
""" | """ | ||
Generate new signed certificate. `ca_key_pair` is the CA key_pair to use | Generate new signed certificate. `ca_key_pair` is the CA key_pair to use | ||
if None, use the latest CA key_pair | if None, use the latest CA key_pair | ||
... | @@ -219,6 +235,8 @@ class CertificateAuthority(object): | ... | @@ -219,6 +235,8 @@ class CertificateAuthority(object): |
new certificate (string). | new certificate (string). | ||
@param ca_key_pair: The CA key_pair to used for signature. If None, the | @param ca_key_pair: The CA key_pair to used for signature. If None, the | ||
latest key_pair is used. | latest key_pair is used. | ||
@param subject_dict: dict of subject attributes to use in x509 subject, | |||
if None, csr subject is used (dict). | |||
""" | """ | ||
# Apply extensions (ex: "not a certificate", ...) | # Apply extensions (ex: "not a certificate", ...) | ||
# Generate a certificate from the CSR | # Generate a certificate from the CSR | ||
... | @@ -229,9 +247,22 @@ class CertificateAuthority(object): | ... | @@ -229,9 +247,22 @@ class CertificateAuthority(object): |
# Certificate serial is the csr_id without extension .csr.pem | # Certificate serial is the csr_id without extension .csr.pem | ||
serial = int(csr_id[:-8], 16) | serial = int(csr_id[:-8], 16) | ||
subject = None | |||
if ca_key_pair is None: | if ca_key_pair is None: | ||
ca_key_pair = self._ca_key_pairs_list[-1] | ca_key_pair = self._ca_key_pairs_list[-1] | ||
cert_pem = self._generateCertificateObjects(ca_key_pair, csr_pem, serial) | if subject_dict: | ||
for attr in subject_dict.keys(): | |||
if not attr in SUBJECT_KEY_LIST: | |||
|
|||
raise ValueError("Subject key %r is not allowed. Certificate subject " \ | |||
"key should be one of %r" % (attr, SUBJECT_KEY_LIST)) | |||
if subject_dict.has_key('C') and len(subject_dict['C']) != 2: | |||
# Country code size is 2 | |||
raise ValueError("Country Code size in subject should be equal to 2.") | |||
subject = x509_name(**subject_dict) | |||
cert_pem = self._generateCertificateObjects(ca_key_pair, | |||
csr_pem, | |||
serial, | |||
subject=subject) | |||
crt_id = self._storage.storeCertificate(csr_id, cert_pem) | crt_id = self._storage.storeCertificate(csr_id, cert_pem) | ||
return crt_id | return crt_id | ||
... | @@ -442,12 +473,21 @@ class CertificateAuthority(object): | ... | @@ -442,12 +473,21 @@ class CertificateAuthority(object): |
""" | """ | ||
return crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey_object) | return crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey_object) | ||
def _generateCertificateObjects(self, ca_key_pair, req, serial): | def _generateCertificateObjects(self, ca_key_pair, req, serial, subject=None): | ||
""" | """ | ||
Generate certificate from CSR PEM Object. | Generate certificate from CSR PEM Object. | ||
This method set default certificate extensions, later will allow to set custom extensions | This method set default certificate extensions, later will allow to set custom extensions | ||
ca_key_pair: ca_key_pair which should be used to sign certificate | |||
req: csr object to sign | |||
serial: serial to apply to the new signed certificate | |||
subject: give a dict containing new subject to apply on signed certificate | |||
if subject is None, req.get_subject() is used. | |||
""" | """ | ||
if subject is None: | |||
subject = req.get_subject() | |||
# Here comes the actual certificate | # Here comes the actual certificate | ||
cert = crypto.X509() | cert = crypto.X509() | ||
# version v3 | # version v3 | ||
... | @@ -456,7 +496,7 @@ class CertificateAuthority(object): | ... | @@ -456,7 +496,7 @@ class CertificateAuthority(object): |
cert.gmtime_adj_notBefore(0) | cert.gmtime_adj_notBefore(0) | ||
cert.gmtime_adj_notAfter(self.crt_life_time) | cert.gmtime_adj_notAfter(self.crt_life_time) | ||
cert.set_issuer(ca_key_pair['crt'].get_subject()) | cert.set_issuer(ca_key_pair['crt'].get_subject()) | ||
cert.set_subject(req.get_subject()) | cert.set_subject(subject) | ||
cert.set_pubkey(req.get_pubkey()) | cert.set_pubkey(req.get_pubkey()) | ||
self.extension_manager.setDefaultExtensions( | self.extension_manager.setDefaultExtensions( | ||
cert, | cert, | ||
... | ... |
-
Owner
Other than the minor points noted, looks good to me.