def test_KeyCertException(self): """ It provides a message. """ message = mk.string() error = KeyCertException(message) self.assertEqual(message, error.message)
def test_KeyCertException_str(self): """ The message is the string serialization. """ message = mk.string() error = KeyCertException(message) self.assertEqual(message.encode('utf-8'), str(error))
def generate_and_store_csr(options, encoding='utf-8'): """ Generate a key/csr and try to store it on disk. Raise KeyCertException when failing to create the key or csr. """ name, _ = os.path.splitext(options.key_file) csr_name = u'%s.csr' % name if os.path.exists(_path(options.key_file, encoding)): raise KeyCertException('Key file already exists.') result = generate_csr(options) try: with open(_path(options.key_file, encoding), 'wb') as store_file: store_file.write(result['key_pem']) with open(_path(csr_name, encoding), 'wb') as store_file: store_file.write(result['csr_pem']) except Exception as error: raise KeyCertException(str(error).decode('utf-8', errors='replace'))
def _sign_cert_or_csr(target, key, options): """ Sign the certificate or CSR. """ sign_algorithm = getattr(options, 'sign_algorithm', 'sha256').encode('ascii') if sign_algorithm not in _SUPPORTED_SIGN_ALGORITHMS: raise KeyCertException( 'Invalid signing algorithm. Supported values: %s.' % (', '.join(_SUPPORTED_SIGN_ALGORITHMS))) target.set_pubkey(key) target.sign(key, sign_algorithm)
def generate_csr(options): """ Generate a new SSL key and the associated SSL cert signing. Returns a tuple of (csr_pem, key_pem) Raise KeyCertException on failure. """ try: return _generate_csr(options) except crypto.Error as error: try: message = error[0][0][2].decode('utf-8', errors='replace') except IndexError: # pragma: no cover message = 'no error details.' raise KeyCertException(message)
def _generate_csr(options): """ Helper to catch all crypto errors and reduce indentation. """ key_size = getattr(options, 'key_size', 2048) if key_size < 512: raise KeyCertException('Key size must be greater or equal to 512.') key_type = crypto.TYPE_RSA csr = crypto.X509Req() _set_subject_and_extensions(csr, options) key_pem = None private_key = options.key if private_key: if os.path.exists(_path(private_key)): with open(_path(private_key), 'rb') as stream: private_key = stream.read() key_pem = private_key key = crypto.load_privatekey(crypto.FILETYPE_PEM, private_key) else: # Generate new Key. key = crypto.PKey() key.generate_key(key_type, key_size) _sign_cert_or_csr(csr, key, options) csr_pem = crypto.dump_certificate_request(crypto.FILETYPE_PEM, csr) if not key_pem: if options.key_password: key_pem = crypto.dump_privatekey( crypto.FILETYPE_PEM, key, _DEFAULT_SSL_KEY_CYPHER, options.key_password.encode('utf-8')) else: key_pem = crypto.dump_privatekey(crypto.FILETYPE_PEM, key) return { 'csr_pem': csr_pem, 'key_pem': key_pem, 'csr': csr, 'key': key, }
def _set_subject_and_extensions(target, options): """ Set the subject and option for `target` CRS or certificate. """ common_name = options.common_name constraints = getattr(options, 'constraints', '') key_usage = getattr(options, 'key_usage', '').lower() email = getattr(options, 'email', '') alternative_name = getattr(options, 'alternative_name', '') country = getattr(options, 'country', '') state = getattr(options, 'state', '') locality = getattr(options, 'locality', '') organization = getattr(options, 'organization', '') organization_unit = getattr(options, 'organization_unit', '') # RFC 2459 defines it as optional, and pyopenssl set it to `0` anyway. # But we got reports that Windows 2003 and Windows 2008 Servers # can not parse CSR generated using this tool, so here we are. target.set_version(2) subject = target.get_subject() subject.CN = common_name.encode('idna') if country: if len(country) != 2: raise KeyCertException('Invalid country code.') subject.C = country if state: subject.ST = state if locality: subject.L = locality if organization: subject.O = organization if organization_unit: subject.OU = organization_unit if email: try: address, domain = options.email.split('@', 1) except ValueError: raise KeyCertException('Invalid email address.') subject.emailAddress = u'%s@%s' % (address, domain.encode('idna')) critical_constraints = False critical_usage = False standard_usage = [] extended_usage = [] extensions = [] if constraints.lower().startswith('critical'): critical_constraints = True constraints = constraints[8:].strip(',').strip() if key_usage.startswith('critical'): critical_usage = True key_usage = key_usage[8:] for usage in key_usage.split(','): usage = usage.strip() if not usage: continue if usage in _KEY_USAGE_STANDARD: standard_usage.append(_KEY_USAGE_STANDARD[usage]) if usage in _KEY_USAGE_EXTENDED: extended_usage.append(_KEY_USAGE_EXTENDED[usage]) if constraints: extensions.append( crypto.X509Extension( b'basicConstraints', critical_constraints, constraints.encode('ascii'), )) if standard_usage: extensions.append( crypto.X509Extension( b'keyUsage', critical_usage, b','.join(standard_usage), )) if extended_usage: extensions.append( crypto.X509Extension( b'extendedKeyUsage', critical_usage, b','.join(extended_usage), )) # Alternate name is optional. if alternative_name: extensions.append( crypto.X509Extension(b'subjectAltName', False, alternative_name.encode('idna'))) target.add_extensions(extensions)