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))
Beispiel #3
0
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'))
Beispiel #4
0
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)
Beispiel #5
0
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)
Beispiel #6
0
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,
    }
Beispiel #7
0
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)