allow to set custom subject when signing certificate
... | ... | @@ -42,6 +42,22 @@ MIN_CA_RENEW_PERIOD = 2 |
DEFAULT_DIGEST_LIST = ['sha256', 'sha384', 'sha512'] | ||
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): | ||
def __init__(self, storage, ca_life_period, ca_renew_period, | ||
crt_life_time, crl_renew_period, digest_list=None, | ||
... | ... | @@ -210,7 +226,7 @@ class CertificateAuthority(object): |
""" | ||
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 | ||
if None, use the latest CA key_pair | ||
... | ... | @@ -219,6 +235,8 @@ class CertificateAuthority(object): |
new certificate (string). | ||
@param ca_key_pair: The CA key_pair to used for signature. If None, the | ||
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", ...) | ||
# Generate a certificate from the CSR | ||
... | ... | @@ -229,9 +247,22 @@ class CertificateAuthority(object): |
# Certificate serial is the csr_id without extension .csr.pem | ||
serial = int(csr_id[:-8], 16) | ||
subject = None | ||
if ca_key_pair is None: | ||
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) | ||
return crt_id | ||
... | ... | @@ -442,12 +473,21 @@ class CertificateAuthority(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. | ||
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 | ||
cert = crypto.X509() | ||
# version v3 | ||
... | ... | @@ -456,7 +496,7 @@ class CertificateAuthority(object): |
cert.gmtime_adj_notBefore(0) | ||
cert.gmtime_adj_notAfter(self.crt_life_time) | ||
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()) | ||
self.extension_manager.setDefaultExtensions( | ||
cert, | ||
... | ... |
-
Owner
Other than the minor points noted, looks good to me.