Example #1
0
    def execute(self, serial_number, **options):
        ca_enabled_check()
        hostname = None
        try:
            self.check_access()
        except errors.ACIError as acierr:
            self.debug(
                "Not granted by ACI to retrieve certificate, looking at principal"
            )
            bind_principal = getattr(context, 'principal')
            if not bind_principal.startswith('host/'):
                raise acierr
            hostname = get_host_from_principal(bind_principal)

        result = self.Backend.ra.get_certificate(serial_number)
        cert = x509.load_certificate(result['certificate'])
        result['subject'] = unicode(cert.subject)
        result['issuer'] = unicode(cert.issuer)
        result['valid_not_before'] = unicode(cert.valid_not_before_str)
        result['valid_not_after'] = unicode(cert.valid_not_after_str)
        result['md5_fingerprint'] = unicode(
            nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
        result['sha1_fingerprint'] = unicode(
            nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])
        if hostname:
            # If we have a hostname we want to verify that the subject
            # of the certificate matches it, otherwise raise an error
            if hostname != cert.subject.common_name:  #pylint: disable=E1101
                raise acierr

        return dict(result=result)
Example #2
0
def set_certificate_attrs(entry_attrs):
    """
    Set individual attributes from some values from a certificate.

    entry_attrs is a dict of an entry

    returns nothing
    """
    if not 'usercertificate' in entry_attrs:
        return
    if type(entry_attrs['usercertificate']) in (list, tuple):
        cert = entry_attrs['usercertificate'][0]
    else:
        cert = entry_attrs['usercertificate']
    cert = x509.normalize_certificate(cert)
    cert = x509.load_certificate(cert, datatype=x509.DER)
    entry_attrs['subject'] = unicode(cert.subject)
    entry_attrs['serial_number'] = unicode(cert.serial_number)
    entry_attrs['serial_number_hex'] = u'0x%X' % cert.serial_number
    entry_attrs['issuer'] = unicode(cert.issuer)
    entry_attrs['valid_not_before'] = unicode(cert.valid_not_before_str)
    entry_attrs['valid_not_after'] = unicode(cert.valid_not_after_str)
    entry_attrs['md5_fingerprint'] = unicode(
        nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
    entry_attrs['sha1_fingerprint'] = unicode(
        nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])
Example #3
0
File: cert.py Project: msrb/freeipa
    def execute(self, serial_number, **options):
        ca_enabled_check()
        hostname = None
        try:
            self.check_access()
        except errors.ACIError as acierr:
            self.debug("Not granted by ACI to retrieve certificate, looking at principal")
            bind_principal = getattr(context, 'principal')
            if not bind_principal.startswith('host/'):
                raise acierr
            hostname = get_host_from_principal(bind_principal)

        result=self.Backend.ra.get_certificate(serial_number)
        cert = x509.load_certificate(result['certificate'])
        result['subject'] = unicode(cert.subject)
        result['issuer'] = unicode(cert.issuer)
        result['valid_not_before'] = unicode(cert.valid_not_before_str)
        result['valid_not_after'] = unicode(cert.valid_not_after_str)
        result['md5_fingerprint'] = unicode(nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
        result['sha1_fingerprint'] = unicode(nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])
        if hostname:
            # If we have a hostname we want to verify that the subject
            # of the certificate matches it, otherwise raise an error
            if hostname != cert.subject.common_name:    #pylint: disable=E1101
                raise acierr

        return dict(result=result)
Example #4
0
    def test_string(self):
        if verbose:
            print("Plain Text:\n%s" % (plain_text))

        # Encode the plain text by feeding it to cipher_op getting cipher text back.
        # Append the final bit of cipher text by calling digest_final
        cipher_text = self.encoding_ctx.cipher_op(plain_text)
        cipher_text += self.encoding_ctx.digest_final()

        if verbose:
            print("Cipher Text:\n%s" %
                  (nss.data_to_hex(cipher_text, separator=":")))

        # Decode the cipher text by feeding it to cipher_op getting plain text back.
        # Append the final bit of plain text by calling digest_final
        decoded_text = self.decoding_ctx.cipher_op(cipher_text)
        decoded_text += self.decoding_ctx.digest_final()

        if verbose:
            print("Decoded Text:\n%s" % (decoded_text))

        # Validate the encryption/decryption by comparing the decoded text with
        # the original plain text, they should match.
        self.assertEqual(decoded_text, plain_text)

        self.assertNotEqual(cipher_text, plain_text)
Example #5
0
def generate_sym_key():
    """ This Function generate Symmetric key and nonce data """    
    ## Initialize NSS Database opens temporary database and the internal PKCS #112 module
    nss.nss_init_nodb()
    ## Mechanism to be used for symmetric key 
    mechanism = nss.CKM_DES_CBC_PAD
    # From the Soft token that we initialized get slot to generate symmetric key
    slot = nss.get_best_slot(mechanism)
    # Generate a symmetric key on the pk11 slot, The sym_key is of type PK11SymKey
    sym_key = slot.key_gen(mechanism, None, slot.get_best_key_length(mechanism))

    # Generate Nonce 
    iv_length = nss.get_iv_length(mechanism)

    if iv_length > 0:
        # Generate Nonce
        iv_data = nss.generate_random(iv_length)
        # Pass this random data to NSS SecItem
        iv_si = nss.SecItem(iv_data)
        # Use the Data passed to SecItem for initialization Vector 
        iv_param = nss.param_from_iv(mechanism, iv_si)
        # Random data is converted to hex
        pki_nonce = nss.data_to_hex(data=iv_data, separator=":")
        #print "generated %d bytes initialization vector: %s" % (iv_length, pki_nonce)
    
        # Create a Symmetric key Context using the Symmetric key, nonce  The context should be 
        # used for encrypt as well as decrypt
        encoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_ENCRYPT, sym_key, iv_param)
        decoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_DECRYPT, sym_key, iv_param)
    #return the symmetric key, nonce data, encoding context and decoding context to encrypt and 
    # decrypt
    return sym_key, pki_nonce, encoding_ctx, decoding_ctx
Example #6
0
    def getKeyFromPassphrase(self, keylen):
        pdialog = self.xml.get_widget("passphraseDialog")
        phraseEntry = self.xml.get_widget("passphraseEntry")

        phraseEntry.set_text('')
        pdialog.show()
        button = pdialog.run()
        pdialog.hide()
        if button != gtk.RESPONSE_OK and button != 0:
            return None

        phrase = phraseEntry.get_text()

        try:
            import nss.nss as nss
            nss.nss_init_nodb()
            shasum = nss.data_to_hex(nss.sha1_digest(phrase))
        except:
            import hashlib
            shasum = hashlib.sha1(phrase).hexdigest()

        if len(shasum) > keylen: 
            shasum = shasum[:keylen]

        return shasum
Example #7
0
    def test_string(self):
        if verbose:
            print "Plain Text:\n%s" % (plain_text)

        # Encode the plain text by feeding it to cipher_op getting cipher text back.
        # Append the final bit of cipher text by calling digest_final
        cipher_text = self.encoding_ctx.cipher_op(plain_text)
        cipher_text += self.encoding_ctx.digest_final()

        if verbose:
            print "Cipher Text:\n%s" % (nss.data_to_hex(cipher_text, separator=":"))

        # Decode the cipher text by feeding it to cipher_op getting plain text back.
        # Append the final bit of plain text by calling digest_final
        decoded_text = self.decoding_ctx.cipher_op(cipher_text)
        decoded_text += self.decoding_ctx.digest_final()

        if verbose:
            print "Decoded Text:\n%s" % (decoded_text)

        # Validate the encryption/decryption by comparing the decoded text with
        # the original plain text, they should match.
        self.assertEqual(decoded_text, plain_text)

        self.assertNotEqual(cipher_text, plain_text)
Example #8
0
    def _parse(self, obj, full=True):
        """Extract certificate-specific data into a result object.

        ``obj``
            Result object containing certificate, into which extracted
            data will be inserted.
        ``full``
            Whether to include all fields, or only the ones we guess
            people want to see most of the time.  Also add
            recognised otherNames to the generic ``san_other``
            attribute when ``True`` in addition to the specialised
            attribute.

        """
        cert = obj.get('certificate')
        if cert is not None:
            cert = x509.load_certificate(cert)
            obj['subject'] = DN(unicode(cert.subject))
            obj['issuer'] = DN(unicode(cert.issuer))
            obj['serial_number'] = cert.serial_number
            if full:
                obj['valid_not_before'] = unicode(cert.valid_not_before_str)
                obj['valid_not_after'] = unicode(cert.valid_not_after_str)
                obj['md5_fingerprint'] = unicode(
                    nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
                obj['sha1_fingerprint'] = unicode(
                    nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])

            try:
                ext_san = cert.get_extension(nss.SEC_OID_X509_SUBJECT_ALT_NAME)
                general_names = x509.decode_generalnames(ext_san.value)
            except KeyError:
                general_names = []

            for name_type, _desc, name, der_name in general_names:
                try:
                    self._add_san_attribute(obj, full, name_type, name,
                                            der_name)
                except Exception:
                    # Invalid GeneralName (i.e. not a valid X.509 cert);
                    # don't fail but log something about it
                    root_logger.warning(
                        "Encountered bad GeneralName; skipping", exc_info=True)

        serial_number = obj.get('serial_number')
        if serial_number is not None:
            obj['serial_number_hex'] = u'0x%X' % serial_number
Example #9
0
def verify_sym_key(archived_key, archived_iv, algorithm, plain_text):
    """ This function verifies whether archived key is usable, Actually
    verifying this is senseless, reason any random data can be used for encryption, 
    but still just for the heck of it. 
    """
    
    # Initialize NSS 
    nss.nss_init_nodb()

    # Decode the base64 string to binary
    key = base64.decodestring(archived_data)

    # Currently we are assuming the mechanism to AES
    # Will need to add other mechanisms later, but 
    # this is just an example.
    mechanism = nss.CKM_AES_CBC_PAD

    # Get the best pkcs11 slot
    slot = nss.get_best_slot(mechanism)

    # convert the binary to hex with separtor as :
    pki_key = nss.data_to_hex(data=key,separator=":")

    # create a nssSecItem object out of it.
    key_si = nss.SecItem(nss.read_hex(pki_key))

    # Import the key to the slot
    sym_key = nss.import_sym_key(slot, mechanism, nss.PK11_OriginUnwrap, nss.CKA_ENCRYPT, key_si)

    # Same for the nonce data
    iv = base64.decodestring(archived_iv)
    iv_data = nss.data_to_hex(data=iv,separator=":")
    iv_si = nss.SecItem(nss.read_hex(iv_data))
    iv_param = nss.param_from_iv(mechanism, iv_si)


    encoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_ENCRYPT,sym_key, iv_param)
    decoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_DECRYPT,sym_key, iv_param)

    cipher_text = encoding_ctx.cipher_op(plain_text)
    cipher_text += encoding_ctx.digest_final()
    print cipher_text

    decoded_text = decoding_ctx.cipher_op(cipher_text)
    decoded_text += decoding_ctx.digest_final()

    print decoded_text
Example #10
0
    def _parse(self, obj, full=True):
        """Extract certificate-specific data into a result object.

        ``obj``
            Result object containing certificate, into which extracted
            data will be inserted.
        ``full``
            Whether to include all fields, or only the ones we guess
            people want to see most of the time.  Also add
            recognised otherNames to the generic ``san_other``
            attribute when ``True`` in addition to the specialised
            attribute.

        """
        cert = obj.get('certificate')
        if cert is not None:
            cert = x509.load_certificate(cert)
            obj['subject'] = DN(unicode(cert.subject))
            obj['issuer'] = DN(unicode(cert.issuer))
            obj['serial_number'] = cert.serial_number
            if full:
                obj['valid_not_before'] = unicode(cert.valid_not_before_str)
                obj['valid_not_after'] = unicode(cert.valid_not_after_str)
                obj['md5_fingerprint'] = unicode(
                    nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
                obj['sha1_fingerprint'] = unicode(
                    nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])

            try:
                ext_san = cert.get_extension(nss.SEC_OID_X509_SUBJECT_ALT_NAME)
                general_names = x509.decode_generalnames(ext_san.value)
            except KeyError:
                general_names = []

            for name_type, desc, name, der_name in general_names:
                try:
                    self._add_san_attribute(
                        obj, full, name_type, name, der_name)
                except Exception as e:
                    # Invalid GeneralName (i.e. not a valid X.509 cert);
                    # don't fail but log something about it
                    root_logger.warning(
                        "Encountered bad GeneralName; skipping", exc_info=True)

        serial_number = obj.get('serial_number')
        if serial_number is not None:
            obj['serial_number_hex'] = u'0x%X' % serial_number
Example #11
0
def fmt_info(label, item, level=0, hex_data=False):
    fmt_tuples = nss.make_line_fmt_tuples(level, label + ':')
    if hex_data:
        fmt_tuples.extend(
            nss.make_line_fmt_tuples(level + 1, nss.data_to_hex(item, 16)))
    elif isinstance(item, six.string_types):
        fmt_tuples.extend(nss.make_line_fmt_tuples(level + 1, str(item)))
    else:
        fmt_tuples.extend(item.format_lines(level=level + 1))
    return nss.indented_format(fmt_tuples)
Example #12
0
def fmt_info(label, item, level=0, hex_data=False):
    fmt_tuples = nss.make_line_fmt_tuples(level, label+':')
    if hex_data:
        fmt_tuples.extend(nss.make_line_fmt_tuples(level+1,
                                                   nss.data_to_hex(item, 16)))
    elif isinstance(item, six.string_types):
        fmt_tuples.extend(nss.make_line_fmt_tuples(level+1, str(item)))
    else:
        fmt_tuples.extend(item.format_lines(level=level+1))
    return nss.indented_format(fmt_tuples)
Example #13
0
def set_certificate_attrs(entry_attrs):
    """
    Set individual attributes from some values from a certificate.

    entry_attrs is a dict of an entry

    returns nothing
    """
    if not 'usercertificate' in entry_attrs:
        return
    if type(entry_attrs['usercertificate']) in (list, tuple):
        cert = entry_attrs['usercertificate'][0]
    else:
        cert = entry_attrs['usercertificate']
    cert = x509.normalize_certificate(cert)
    cert = x509.load_certificate(cert, datatype=x509.DER)
    entry_attrs['subject'] = unicode(cert.subject)
    entry_attrs['serial_number'] = unicode(cert.serial_number)
    entry_attrs['serial_number_hex'] = u'0x%X' % cert.serial_number
    entry_attrs['issuer'] = unicode(cert.issuer)
    entry_attrs['valid_not_before'] = unicode(cert.valid_not_before_str)
    entry_attrs['valid_not_after'] = unicode(cert.valid_not_after_str)
    entry_attrs['md5_fingerprint'] = unicode(nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
    entry_attrs['sha1_fingerprint'] = unicode(nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])
Example #14
0
def setup_contexts(mechanism, key, iv):
    # Get a PK11 slot based on the cipher
    slot = nss.get_best_slot(mechanism)

    # If key was supplied use it, otherwise generate one
    if key:
        if verbose:
            print("using supplied key data")
            print("key:\n%s" % (key))
        key_si = nss.SecItem(nss.read_hex(key))
        sym_key = nss.import_sym_key(slot, mechanism, nss.PK11_OriginUnwrap,
                                     nss.CKA_ENCRYPT, key_si)
    else:
        if verbose:
            print("generating key data")
        sym_key = slot.key_gen(mechanism, None,
                               slot.get_best_key_length(mechanism))

    # If initialization vector was supplied use it, otherwise set it to None
    if iv:
        if verbose:
            print("supplied iv:\n%s" % (iv))
        iv_data = nss.read_hex(iv)
        iv_si = nss.SecItem(iv_data)
        iv_param = nss.param_from_iv(mechanism, iv_si)
    else:
        iv_length = nss.get_iv_length(mechanism)
        if iv_length > 0:
            iv_data = nss.generate_random(iv_length)
            iv_si = nss.SecItem(iv_data)
            iv_param = nss.param_from_iv(mechanism, iv_si)
            if verbose:
                print("generated %d byte initialization vector: %s" %
                      (iv_length, nss.data_to_hex(iv_data, separator=":")))
        else:
            iv_param = None

    # Create an encoding context
    encoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_ENCRYPT,
                                                 sym_key, iv_param)

    # Create a decoding context
    decoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_DECRYPT,
                                                 sym_key, iv_param)

    return encoding_ctx, decoding_ctx
Example #15
0
def setup_contexts(mechanism, key, iv):
    # Get a PK11 slot based on the cipher
    slot = nss.get_best_slot(mechanism)

    # If key was supplied use it, otherwise generate one
    if key:
        if verbose:
            print "using supplied key data"
            print "key:\n%s" % (key)
        key_si = nss.SecItem(nss.read_hex(key))
        sym_key = nss.import_sym_key(slot, mechanism, nss.PK11_OriginUnwrap,
                                     nss.CKA_ENCRYPT, key_si)
    else:
        if verbose:
            print "generating key data"
        sym_key = slot.key_gen(mechanism, None, slot.get_best_key_length(mechanism))

    # If initialization vector was supplied use it, otherwise set it to None
    if iv:
        if verbose:
            print "supplied iv:\n%s" % (iv)
        iv_data = nss.read_hex(iv)
        iv_si = nss.SecItem(iv_data)
        iv_param = nss.param_from_iv(mechanism, iv_si)
    else:
        iv_length = nss.get_iv_length(mechanism)
        if iv_length > 0:
            iv_data = nss.generate_random(iv_length)
            iv_si = nss.SecItem(iv_data)
            iv_param = nss.param_from_iv(mechanism, iv_si)
            if verbose:
                print "generated %d byte initialization vector: %s" % \
                    (iv_length, nss.data_to_hex(iv_data, separator=":"))
        else:
            iv_param = None

    # Create an encoding context
    encoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_ENCRYPT,
                                                 sym_key, iv_param)

    # Create a decoding context
    decoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_DECRYPT,
                                                 sym_key, iv_param)

    return encoding_ctx, decoding_ctx
def generate_sym_key():
    """ This Function generate Symmetric key and nonce data """
    ## Initialize NSS Database opens temporary database and the internal PKCS #112 module
    nss.nss_init_nodb()
    ## Mechanism to be used for symmetric key
    mechanism = nss.CKM_DES_CBC_PAD
    # From the Soft token that we initialized get slot to generate symmetric key
    slot = nss.get_best_slot(mechanism)
    # Generate a symmetric key on the pk11 slot, The sym_key is of type PK11SymKey
    sym_key = slot.key_gen(mechanism, None,
                           slot.get_best_key_length(mechanism))

    # Generate Nonce
    iv_length = nss.get_iv_length(mechanism)

    if iv_length > 0:
        # Generate Nonce
        iv_data = nss.generate_random(iv_length)
        # Pass this random data to NSS SecItem
        iv_si = nss.SecItem(iv_data)
        # Use the Data passed to SecItem for initialization Vector
        iv_param = nss.param_from_iv(mechanism, iv_si)
        # Random data is converted to hex
        pki_nonce = nss.data_to_hex(data=iv_data, separator=":")
        #print "generated %d bytes initialization vector: %s" % (iv_length, pki_nonce)

        # Create a Symmetric key Context using the Symmetric key, nonce  The context should be
        # used for encrypt as well as decrypt
        encoding_ctx = nss.create_context_by_sym_key(mechanism,
                                                     nss.CKA_ENCRYPT, sym_key,
                                                     iv_param)
        decoding_ctx = nss.create_context_by_sym_key(mechanism,
                                                     nss.CKA_DECRYPT, sym_key,
                                                     iv_param)
    #return the symmetric key, nonce data, encoding context and decoding context to encrypt and
    # decrypt
    return sym_key, pki_nonce, encoding_ctx, decoding_ctx
Example #17
0
    def retrieve_security_data(self, recovery_request_id, passphrase=None):
        """
        :param recovery_request_id:  identifier of key recovery request
        :param passphrase:           passphrase to be used to wrap the data

        Recover the passphrase or symmetric key.  We require an approved
        recovery request.

        If a passphrase is provided, the DRM will return a blob that can be decrypted
        with the passphrase.  If not, then a symmetric key will be created to wrap the
        data for transport to this server.  Upon receipt, the data will be unwrapped
        and returned unencrypted.

        The command returns a dict with the values described in parse_key_data_xml(),
        as well as the following field

        +-----------------+---------------+-------------------------------------- +
        |result name      |result type    |comments                               |
        +=================+===============+=======================================+
        |data             |String         | Key data (either wrapped using        |
        |                 |               | passphrase or unwrapped)              |
        +-----------------+---------------+---------------------------------------+
        """
        self.debug('%s.retrieve_security_data()', self.fullname)

        if recovery_request_id is None:
            raise CertificateOperationError(
                error=_('Bad arguments to retrieve_security_data'))

        # generate symmetric key
        slot = nss.get_best_slot(self.mechanism)
        session_key = slot.key_gen(
            self.mechanism,
            None,
            slot.get_best_key_length(
                self.mechanism))

        # wrap this key with the transport cert
        public_key = self.transport_cert.subject_public_key_info.public_key
        wrapped_session_key = b64encode(
            nss.pub_wrap_sym_key(
                self.mechanism,
                public_key,
                session_key))
        wrapped_passphrase = None
        if passphrase is not None:
            # wrap passphrase with session key
            wrapped_session_key = b64encode(
                self.symmetric_wrap(
                    passphrase,
                    session_key))

        request = self.create_recovery_request(None, recovery_request_id,
                                               wrapped_session_key,
                                               wrapped_passphrase)

        # Call CMS
        http_status, http_reason_phrase, _http_headers, http_body = \
            self._request('/kra/rest/agent/keys/retrieve',
                          self.kra_agent_port,
                          self.POST,
                          etree.tostring(request.getroot(), encoding='UTF-8'))

        # Parse and handle errors
        if (http_status != 200):
            raise CertificateOperationError(error=_('Error in retrieving security data (%s)') %
                                            http_reason_phrase)

        parse_result = self.get_parse_result_xml(http_body, parse_key_data_xml)

        if passphrase is None:
            iv = nss.data_to_hex(
                b64decode(
                    parse_result['nonce_data']))
            parse_result['data'] = self.symmetric_unwrap(
                b64decode(parse_result['wrapped_data']),
                session_key, iv)

        return parse_result
Example #18
0
    def retrieve_security_data(self, recovery_request_id, passphrase=None):
        """
        :param recovery_request_id:  identifier of key recovery request
        :param passphrase:           passphrase to be used to wrap the data

        Recover the passphrase or symmetric key.  We require an approved
        recovery request.

        If a passphrase is provided, the DRM will return a blob that can be decrypted
        with the passphrase.  If not, then a symmetric key will be created to wrap the
        data for transport to this server.  Upon receipt, the data will be unwrapped
        and returned unencrypted.

        The command returns a dict with the values described in parse_key_data_xml(),
        as well as the following field

        +-----------------+---------------+-------------------------------------- +
        |result name      |result type    |comments                               |
        +=================+===============+=======================================+
        |data             |String         | Key data (either wrapped using        |
        |                 |               | passphrase or unwrapped)              |
        +-----------------+---------------+---------------------------------------+
        """
        self.debug('%s.retrieve_security_data()', self.fullname)

        if recovery_request_id is None:
            raise CertificateOperationError(
                error=_('Bad arguments to retrieve_security_data'))

        # generate symmetric key
        slot = nss.get_best_slot(self.mechanism)
        session_key = slot.key_gen(self.mechanism, None,
                                   slot.get_best_key_length(self.mechanism))

        # wrap this key with the transport cert
        public_key = self.transport_cert.subject_public_key_info.public_key
        wrapped_session_key = b64encode(
            nss.pub_wrap_sym_key(self.mechanism, public_key, session_key))
        wrapped_passphrase = None
        if passphrase is not None:
            # wrap passphrase with session key
            wrapped_session_key = b64encode(
                self.symmetric_wrap(passphrase, session_key))

        request = self.create_recovery_request(None, recovery_request_id,
                                               wrapped_session_key,
                                               wrapped_passphrase)

        # Call CMS
        http_status, http_reason_phrase, _http_headers, http_body = \
            self._request('/kra/rest/agent/keys/retrieve',
                          self.kra_agent_port,
                          self.POST,
                          etree.tostring(request.getroot(), encoding='UTF-8'))

        # Parse and handle errors
        if (http_status != 200):
            raise CertificateOperationError(
                error=_('Error in retrieving security data (%s)') %
                http_reason_phrase)

        parse_result = self.get_parse_result_xml(http_body, parse_key_data_xml)

        if passphrase is None:
            iv = nss.data_to_hex(b64decode(parse_result['nonce_data']))
            parse_result['data'] = self.symmetric_unwrap(
                b64decode(parse_result['wrapped_data']), session_key, iv)

        return parse_result
Example #19
0
                pass
            if not principal.startswith('host/'):
                api.Command['service_mod'](principal, usercertificate=None)
            else:
                hostname = get_host_from_principal(principal)
                api.Command['host_mod'](hostname, usercertificate=None)

        # Request the certificate
        result = self.Backend.ra.request_certificate(csr,
                                                     request_type=request_type)
        cert = x509.load_certificate(result['certificate'])
        result['issuer'] = unicode(cert.issuer)
        result['valid_not_before'] = unicode(cert.valid_not_before_str)
        result['valid_not_after'] = unicode(cert.valid_not_after_str)
        result['md5_fingerprint'] = unicode(
            nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
        result['sha1_fingerprint'] = unicode(
            nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])

        # Success? Then add it to the service entry.
        if 'certificate' in result:
            if not principal.startswith('host/'):
                skw = {"usercertificate": str(result.get('certificate'))}
                api.Command['service_mod'](principal, **skw)
            else:
                hostname = get_host_from_principal(principal)
                skw = {"usercertificate": str(result.get('certificate'))}
                api.Command['host_mod'](hostname, **skw)

        return dict(result=result)
Example #20
0
File: cert.py Project: msrb/freeipa
    def execute(self, csr, **kw):
        ca_enabled_check()

        ldap = self.api.Backend.ldap2
        add = kw.get('add')
        request_type = kw.get('request_type')
        profile_id = kw.get('profile_id', self.Backend.ra.DEFAULT_PROFILE)
        ca = '.'  # top-level CA hardcoded until subca plugin implemented

        """
        Access control is partially handled by the ACI titled
        'Hosts can modify service userCertificate'. This is for the case
        where a machine binds using a host/ prinicpal. It can only do the
        request if the target hostname is in the managedBy attribute which
        is managed using the add/del member commands.

        Binding with a user principal one needs to be in the request_certs
        taskgroup (directly or indirectly via role membership).
        """

        principal_string = kw.get('principal')
        principal = split_any_principal(principal_string)
        servicename, principal_name, realm = principal
        if servicename is None:
            principal_type = USER
        elif servicename == 'host':
            principal_type = HOST
        else:
            principal_type = SERVICE

        bind_principal = split_any_principal(getattr(context, 'principal'))
        bind_service, bind_name, bind_realm = bind_principal

        if bind_service is None:
            bind_principal_type = USER
        elif bind_service == 'host':
            bind_principal_type = HOST
        else:
            bind_principal_type = SERVICE

        if bind_principal != principal and bind_principal_type != HOST:
            # Can the bound principal request certs for another principal?
            self.check_access()

        try:
            self.check_access("request certificate ignore caacl")
            bypass_caacl = True
        except errors.ACIError:
            bypass_caacl = False

        if not bypass_caacl:
            caacl_check(principal_type, principal_string, ca, profile_id)

        try:
            subject = pkcs10.get_subject(csr)
            extensions = pkcs10.get_extensions(csr)
            subjectaltname = pkcs10.get_subjectaltname(csr) or ()
        except (NSPRError, PyAsn1Error) as e:
            raise errors.CertificateOperationError(
                error=_("Failure decoding Certificate Signing Request: %s") % e)

        # self-service and host principals may bypass SAN permission check
        if bind_principal != principal and bind_principal_type != HOST:
            if '2.5.29.17' in extensions:
                self.check_access('request certificate with subjectaltname')

        dn = None
        principal_obj = None
        # See if the service exists and punt if it doesn't and we aren't
        # going to add it
        try:
            if principal_type == SERVICE:
                principal_obj = api.Command['service_show'](principal_string, all=True)
            elif principal_type == HOST:
                principal_obj = api.Command['host_show'](principal_name, all=True)
            elif principal_type == USER:
                principal_obj = api.Command['user_show'](principal_name, all=True)
        except errors.NotFound as e:
            if principal_type == SERVICE and add:
                principal_obj = api.Command['service_add'](principal_string, force=True)
            else:
                raise errors.NotFound(
                    reason=_("The principal for this request doesn't exist."))
        principal_obj = principal_obj['result']
        dn = principal_obj['dn']

        # Ensure that the DN in the CSR matches the principal
        cn = subject.common_name  #pylint: disable=E1101
        if not cn:
            raise errors.ValidationError(name='csr',
                error=_("No Common Name was found in subject of request."))

        if principal_type in (SERVICE, HOST):
            if cn.lower() != principal_name.lower():
                raise errors.ACIError(
                    info=_("hostname in subject of request '%(cn)s' "
                        "does not match principal hostname '%(hostname)s'")
                        % dict(cn=cn, hostname=principal_name))
        elif principal_type == USER:
            # check user name
            if cn != principal_name:
                raise errors.ValidationError(
                    name='csr',
                    error=_("DN commonName does not match user's login")
                )

            # check email address
            mail = subject.email_address  #pylint: disable=E1101
            if mail is not None and mail not in principal_obj.get('mail', []):
                raise errors.ValidationError(
                    name='csr',
                    error=_(
                        "DN emailAddress does not match "
                        "any of user's email addresses")
                )

        # We got this far so the principal entry exists, can we write it?
        if not ldap.can_write(dn, "usercertificate"):
            raise errors.ACIError(info=_("Insufficient 'write' privilege "
                "to the 'userCertificate' attribute of entry '%s'.") % dn)

        # Validate the subject alt name, if any
        for name_type, name in subjectaltname:
            if name_type == pkcs10.SAN_DNSNAME:
                name = unicode(name)
                alt_principal_obj = None
                alt_principal_string = None
                try:
                    if principal_type == HOST:
                        alt_principal_string = 'host/%s@%s' % (name, realm)
                        alt_principal_obj = api.Command['host_show'](name, all=True)
                    elif principal_type == SERVICE:
                        alt_principal_string = '%s/%s@%s' % (servicename, name, realm)
                        alt_principal_obj = api.Command['service_show'](
                            alt_principal_string, all=True)
                    elif principal_type == USER:
                        raise errors.ValidationError(
                            name='csr',
                            error=_("subject alt name type %s is forbidden "
                                "for user principals") % name_type
                        )
                except errors.NotFound:
                    # We don't want to issue any certificates referencing
                    # machines we don't know about. Nothing is stored in this
                    # host record related to this certificate.
                    raise errors.NotFound(reason=_('The service principal for '
                        'subject alt name %s in certificate request does not '
                        'exist') % name)
                if alt_principal_obj is not None:
                    altdn = alt_principal_obj['result']['dn']
                    if not ldap.can_write(altdn, "usercertificate"):
                        raise errors.ACIError(info=_(
                            "Insufficient privilege to create a certificate "
                            "with subject alt name '%s'.") % name)
                if alt_principal_string is not None and not bypass_caacl:
                    caacl_check(
                        principal_type, alt_principal_string, ca, profile_id)
            elif name_type in (pkcs10.SAN_OTHERNAME_KRB5PRINCIPALNAME,
                               pkcs10.SAN_OTHERNAME_UPN):
                if split_any_principal(name) != principal:
                    raise errors.ACIError(
                        info=_("Principal '%s' in subject alt name does not "
                               "match requested principal") % name)
            elif name_type == pkcs10.SAN_RFC822NAME:
                if principal_type == USER:
                    if name not in principal_obj.get('mail', []):
                        raise errors.ValidationError(
                            name='csr',
                            error=_(
                                "RFC822Name does not match "
                                "any of user's email addresses")
                        )
                else:
                    raise errors.ValidationError(
                        name='csr',
                        error=_("subject alt name type %s is forbidden "
                            "for non-user principals") % name_type
                    )
            else:
                raise errors.ACIError(
                    info=_("Subject alt name type %s is forbidden") %
                         name_type)

        # Request the certificate
        result = self.Backend.ra.request_certificate(
            csr, profile_id, request_type=request_type)
        cert = x509.load_certificate(result['certificate'])
        result['issuer'] = unicode(cert.issuer)
        result['valid_not_before'] = unicode(cert.valid_not_before_str)
        result['valid_not_after'] = unicode(cert.valid_not_after_str)
        result['md5_fingerprint'] = unicode(nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
        result['sha1_fingerprint'] = unicode(nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])

        # Success? Then add it to the principal's entry
        # (unless the profile tells us not to)
        profile = api.Command['certprofile_show'](profile_id)
        store = profile['result']['ipacertprofilestoreissued'][0] == 'TRUE'
        if store and 'certificate' in result:
            cert = str(result.get('certificate'))
            kwargs = dict(addattr=u'usercertificate={}'.format(cert))
            if principal_type == SERVICE:
                api.Command['service_mod'](principal_string, **kwargs)
            elif principal_type == HOST:
                api.Command['host_mod'](principal_name, **kwargs)
            elif principal_type == USER:
                api.Command['user_mod'](principal_name, **kwargs)

        return dict(
            result=result
        )
    if tblast and len(tblast) > 3:
        ll = []
        ll.extend(tblast[:3])
        ll[0] = os.path.basename(tblast[0])
        tblast = ll

    ntext = ""
    try:
        import nss.nss as nss
        nss.nss_init_nodb()
        context = nss.create_digest_context(nss.SEC_OID_MD5)
        context.digest_begin()
        for t in tblast:
            ntext += str(t) + ":"
            context.digest_op(str(t))
        hexdigest = nss.data_to_hex(context.digest_final())
    except:
        import hashlib
        m = hashlib.md5()
        for t in tblast:
            ntext += str(t) + ":"
            m.update(str(t))
        hexdigest = m.hexdigest()

    text += str(hexdigest)[:8] + " " + ntext

    text += extxt[0]
    text += "\n"
    text += "".join(elist)

    trace = tb
Example #22
0
class cert_request(VirtualCommand):
    __doc__ = _('Submit a certificate signing request.')

    takes_args = (File(
        'csr',
        validate_csr,
        label=_('CSR'),
        cli_name='csr_file',
        normalizer=normalize_csr,
    ), )
    operation = "request certificate"

    takes_options = (
        Str(
            'principal',
            label=_('Principal'),
            doc=
            _('Service principal for this certificate (e.g. HTTP/test.example.com)'
              ),
        ),
        Str(
            'request_type',
            default=u'pkcs10',
            autofill=True,
        ),
        Flag('add',
             doc=_("automatically add the principal if it doesn't exist"),
             default=False,
             autofill=True),
    )

    has_output_params = (
        Str(
            'certificate',
            label=_('Certificate'),
        ),
        Str(
            'subject',
            label=_('Subject'),
        ),
        Str(
            'issuer',
            label=_('Issuer'),
        ),
        Str(
            'valid_not_before',
            label=_('Not Before'),
        ),
        Str(
            'valid_not_after',
            label=_('Not After'),
        ),
        Str(
            'md5_fingerprint',
            label=_('Fingerprint (MD5)'),
        ),
        Str(
            'sha1_fingerprint',
            label=_('Fingerprint (SHA1)'),
        ),
        Str(
            'serial_number',
            label=_('Serial number'),
        ),
        Str(
            'serial_number_hex',
            label=_('Serial number (hex)'),
        ),
    )

    has_output = (Output(
        'result',
        type=dict,
        doc=_('Dictionary mapping variable name to value'),
    ), )

    def execute(self, csr, **kw):
        ldap = self.api.Backend.ldap2
        principal = kw.get('principal')
        add = kw.get('add')
        del kw['principal']
        del kw['add']
        service = None
        """
        Access control is partially handled by the ACI titled
        'Hosts can modify service userCertificate'. This is for the case
        where a machine binds using a host/ prinicpal. It can only do the
        request if the target hostname is in the managedBy attribute which
        is managed using the add/del member commands.

        Binding with a user principal one needs to be in the request_certs
        taskgroup (directly or indirectly via role membership).
        """

        bind_principal = getattr(context, 'principal')
        # Can this user request certs?
        if not bind_principal.startswith('host/'):
            self.check_access()

        # FIXME: add support for subject alt name

        # Ensure that the hostname in the CSR matches the principal
        subject_host = get_csr_hostname(csr)
        (servicename, hostname, realm) = split_principal(principal)
        if subject_host.lower() != hostname.lower():
            raise errors.ACIError(
                info=_("hostname in subject of request '%(subject_host)s' "
                       "does not match principal hostname '%(hostname)s'") %
                dict(subject_host=subject_host, hostname=hostname))

        dn = None
        service = None
        # See if the service exists and punt if it doesn't and we aren't
        # going to add it
        try:
            if not principal.startswith('host/'):
                service = api.Command['service_show'](principal,
                                                      all=True,
                                                      raw=True)['result']
                dn = service['dn']
            else:
                hostname = get_host_from_principal(principal)
                service = api.Command['host_show'](hostname,
                                                   all=True,
                                                   raw=True)['result']
                dn = service['dn']
        except errors.NotFound, e:
            if not add:
                raise errors.NotFound(reason=_("The service principal for "
                                               "this request doesn't exist."))
            try:
                service = api.Command['service_add'](principal, **{
                    'force': True
                })['result']
                dn = service['dn']
            except errors.ACIError:
                raise errors.ACIError(
                    info=_('You need to be a member of '
                           'the serviceadmin role to add services'))

        # We got this far so the service entry exists, can we write it?
        if not ldap.can_write(dn, "usercertificate"):
            raise errors.ACIError(
                info=_("Insufficient 'write' privilege "
                       "to the 'userCertificate' attribute of entry '%s'.") %
                dn)

        # Validate the subject alt name, if any
        request = pkcs10.load_certificate_request(csr)
        subjectaltname = pkcs10.get_subjectaltname(request)
        if subjectaltname is not None:
            for name in subjectaltname:
                name = unicode(name)
                try:
                    hostentry = api.Command['host_show'](name,
                                                         all=True,
                                                         raw=True)['result']
                    hostdn = hostentry['dn']
                except errors.NotFound:
                    # We don't want to issue any certificates referencing
                    # machines we don't know about. Nothing is stored in this
                    # host record related to this certificate.
                    raise errors.NotFound(reason=_(
                        'no host record for '
                        'subject alt name %s in certificate request') % name)
                authprincipal = getattr(context, 'principal')
                if authprincipal.startswith("host/"):
                    if not hostdn in service.get('managedby', []):
                        raise errors.ACIError(info=_(
                            "Insufficient privilege to create a certificate "
                            "with subject alt name '%s'.") % name)

        if 'usercertificate' in service:
            serial = x509.get_serial_number(service['usercertificate'][0],
                                            datatype=x509.DER)
            # revoke the certificate and remove it from the service
            # entry before proceeding. First we retrieve the certificate to
            # see if it is already revoked, if not then we revoke it.
            try:
                result = api.Command['cert_show'](unicode(serial))['result']
                if 'revocation_reason' not in result:
                    try:
                        api.Command['cert_revoke'](unicode(serial),
                                                   revocation_reason=4)
                    except errors.NotImplementedError:
                        # some CA's might not implement revoke
                        pass
            except errors.NotImplementedError:
                # some CA's might not implement get
                pass
            if not principal.startswith('host/'):
                api.Command['service_mod'](principal, usercertificate=None)
            else:
                hostname = get_host_from_principal(principal)
                api.Command['host_mod'](hostname, usercertificate=None)

        # Request the certificate
        result = self.Backend.ra.request_certificate(csr, **kw)
        cert = x509.load_certificate(result['certificate'])
        result['issuer'] = unicode(cert.issuer)
        result['valid_not_before'] = unicode(cert.valid_not_before_str)
        result['valid_not_after'] = unicode(cert.valid_not_after_str)
        result['md5_fingerprint'] = unicode(
            nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
        result['sha1_fingerprint'] = unicode(
            nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])

        # Success? Then add it to the service entry.
        if 'certificate' in result:
            if not principal.startswith('host/'):
                skw = {"usercertificate": str(result.get('certificate'))}
                api.Command['service_mod'](principal, **skw)
            else:
                hostname = get_host_from_principal(principal)
                skw = {"usercertificate": str(result.get('certificate'))}
                api.Command['host_mod'](hostname, **skw)

        return dict(result=result)
Example #23
0
                           (1, 'Data:')])
print nss.indented_format([(2, 'Version: %d (%#x)' % (cert.version+1, cert.version))])
print nss.indented_format([(2, 'Serial Number: %d (%#x)' % (cert.serial_number, cert.serial_number))])
print nss.indented_format([(2, 'Signature Algorithm:')])
print nss.indented_format(cert.signature_algorithm.format_lines(3))
print nss.indented_format([(2, 'Issuer: "%s"' % cert.issuer)])
print nss.indented_format([(2, 'Validity:'),
                           (3, 'Not Before: %s' % cert.valid_not_before_str),
                           (3, 'Not After:  %s' % cert.valid_not_after_str)])
print nss.indented_format([(2, 'Subject: "%s"' % cert.subject)])
print nss.indented_format([(2, 'Subject Public Key Info:')])
print nss.indented_format(cert.subject_public_key_info.format_lines(3))

if len(extensions) > 0:
    print nss.indented_format([(1, 'Signed Extensions: (%d)' % len(extensions))])
    for extension in extensions:
        print_extension(2, extension)

print nss.indented_format(cert.signed_data.format_lines(1))

print nss.indented_format([(1, 'Fingerprint (MD5):')])
print nss.indented_format(nss.make_line_fmt_tuples(2,
                                                   nss.data_to_hex(nss.md5_digest(cert.der_data),
                                                                   nss.OCTETS_PER_LINE_DEFAULT)))

print nss.indented_format([(1, 'Fingerprint (SHA1):')])
print nss.indented_format(nss.make_line_fmt_tuples(2,
                                                   nss.data_to_hex(nss.sha1_digest(cert.der_data),
                                                                   nss.OCTETS_PER_LINE_DEFAULT)))

Example #24
0
    def do_test(self, name, ref_cmd, nss_digest_func, hash_oid):
        hash_oid_name = nss.oid_str(hash_oid)

        if verbose:
            print(
                'running test %s: nss_digest_func=%s hash_oid=%s in_filename=%s'
                % (name, nss_digest_func.__name__, hash_oid_name, in_filename))

        # read binary data in from the file
        with open(in_filename, "rb") as f:
            ref_data = f.read()

        # Run the system hash function to get a reference result.
        # Since we're testing the python-nss binding we assume
        # the system command is entirely independent and correct.
        #
        # Because our digest routines return binary data (e.g. a buffer
        # of octets) and the system hash command returns a hex string
        # which we need to compare against, and because we sometimes
        # want to print the result of our digest functions always
        # convert our results to a hex string via nss.data_to_hex()
        #
        # We want to read the reference result from the subprocess as text
        # not binary, thus universal_newlines must be True.
        proc = subprocess.Popen([ref_cmd, in_filename],
                                stdout=subprocess.PIPE,
                                universal_newlines=True)
        stdout, stderr = proc.communicate()
        reference_digest = stdout.split()[0]
        if verbose:
            print('reference_digest\n%s' % (reference_digest))

        # Run the test with convenience digest function (e.g. nss.sha256_digest, etc.).
        test_digest = nss.data_to_hex(nss_digest_func(ref_data),
                                      separator=None)
        if verbose:
            print('nss %s\n%s' % (nss_digest_func.__name__, test_digest))

        self.assertEqual(
            test_digest,
            reference_digest,
            msg='nss %s test failed reference=%s test=%s' %
            (nss_digest_func.__name__, reference_digest, test_digest),
        )

        # Run the test using the generic hash_buf function specifying the hash algorithm.
        test_digest = nss.data_to_hex(nss.hash_buf(hash_oid, ref_data),
                                      separator=None)
        if verbose:
            print('nss.hash_buf %s\n%s' % (hash_oid_name, test_digest))

        self.assertEqual(
            test_digest,
            reference_digest,
            msg='nss.hash_buf %s test failed reference=%s test=%s' %
            (hash_oid_name, reference_digest, test_digest),
        )

        # Run the test using the lowest level hashing functions by specifying the hash algorithm.
        # The entire input data is supplied all at once in a single call.
        context = nss.create_digest_context(hash_oid)
        context.digest_begin()
        context.digest_op(ref_data)
        test_digest = nss.data_to_hex(context.digest_final(), separator=None)
        if verbose:
            print('nss.digest_context %s\n%s' % (hash_oid_name, test_digest))

        self.assertEqual(
            test_digest,
            reference_digest,
            msg='nss.digest_context %s test failed reference=%s test=%s' %
            (hash_oid_name, reference_digest, test_digest),
        )

        # Run the test using the lowest level hashing functions by specifying the hash algorithm
        # and feeding 'chunks' of data one at a time to be consumed.
        with open(in_filename, 'rb') as in_file:
            context = nss.create_digest_context(hash_oid)
            context.digest_begin()
            while True:
                in_data = in_file.read(chunk_size)
                if len(in_data) == 0:
                    break
                context.digest_op(in_data)

        test_digest = nss.data_to_hex(context.digest_final(), separator=None)
        if verbose:
            print('chunked nss.digest_context %s\n%s' %
                  (hash_oid_name, test_digest))

        self.assertEqual(
            test_digest,
            reference_digest,
            msg='chunked nss.digest_context %s test failed reference=%s test=%s'
            % (hash_oid_name, reference_digest, test_digest),
        )
Example #25
0
                # some CA's might not implement get
                pass
            if not principal.startswith('host/'):
                api.Command['service_mod'](principal, usercertificate=None)
            else:
                hostname = get_host_from_principal(principal)
                api.Command['host_mod'](hostname, usercertificate=None)

        # Request the certificate
        result = self.Backend.ra.request_certificate(
            csr, request_type=request_type)
        cert = x509.load_certificate(result['certificate'])
        result['issuer'] = unicode(cert.issuer)
        result['valid_not_before'] = unicode(cert.valid_not_before_str)
        result['valid_not_after'] = unicode(cert.valid_not_after_str)
        result['md5_fingerprint'] = unicode(nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
        result['sha1_fingerprint'] = unicode(nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])

        # Success? Then add it to the service entry.
        if 'certificate' in result:
            if not principal.startswith('host/'):
                skw = {"usercertificate": str(result.get('certificate'))}
                api.Command['service_mod'](principal, **skw)
            else:
                hostname = get_host_from_principal(principal)
                skw = {"usercertificate": str(result.get('certificate'))}
                api.Command['host_mod'](hostname, **skw)

        return dict(
            result=result
        )
Example #26
0
class cert_show(VirtualCommand):
    __doc__ = _('Retrieve an existing certificate.')

    takes_args = _serial_number

    has_output_params = (
        Str(
            'certificate',
            label=_('Certificate'),
        ),
        Str(
            'subject',
            label=_('Subject'),
        ),
        Str(
            'issuer',
            label=_('Issuer'),
        ),
        Str(
            'valid_not_before',
            label=_('Not Before'),
        ),
        Str(
            'valid_not_after',
            label=_('Not After'),
        ),
        Str(
            'md5_fingerprint',
            label=_('Fingerprint (MD5)'),
        ),
        Str(
            'sha1_fingerprint',
            label=_('Fingerprint (SHA1)'),
        ),
        Str(
            'revocation_reason',
            label=_('Revocation reason'),
        ),
        Str(
            'serial_number_hex',
            label=_('Serial number (hex)'),
        ),
    )

    takes_options = (Str(
        'out?',
        label=_('Output filename'),
        doc=_('File to store the certificate in.'),
        exclude='webui',
    ), )

    operation = "retrieve certificate"

    def execute(self, serial_number, **options):
        hostname = None
        try:
            self.check_access()
        except errors.ACIError, acierr:
            self.debug(
                "Not granted by ACI to retrieve certificate, looking at principal"
            )
            bind_principal = getattr(context, 'principal')
            if not bind_principal.startswith('host/'):
                raise acierr
            hostname = get_host_from_principal(bind_principal)

        result = self.Backend.ra.get_certificate(serial_number)
        cert = x509.load_certificate(result['certificate'])
        result['subject'] = unicode(cert.subject)
        result['issuer'] = unicode(cert.issuer)
        result['valid_not_before'] = unicode(cert.valid_not_before_str)
        result['valid_not_after'] = unicode(cert.valid_not_after_str)
        result['md5_fingerprint'] = unicode(
            nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
        result['sha1_fingerprint'] = unicode(
            nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])
        if hostname:
            # If we have a hostname we want to verify that the subject
            # of the certificate matches it, otherwise raise an error
            if hostname != cert.subject.common_name:  #pylint: disable=E1101
                raise acierr

        return dict(result=result)
Example #27
0
    def do_test(self, name, ref_cmd, nss_digest_func, hash_oid):
        hash_oid_name = nss.oid_str(hash_oid)

        if verbose:
            print 'running test %s: nss_digest_func=%s hash_oid=%s in_filename=%s' % \
                (name, nss_digest_func.__name__, hash_oid_name, in_filename)

        # read the data in from the file
        ref_data = open(in_filename).read()

        # Run the system hash function to get a reference result.
        # Since we're testing the python-nss binding we assume
        # the system command is entirely independent and correct.
        #
        # Because our digest routines return raw data (e.g. a buffer of octets)
        # and the system hash command returns a hex string which we need to compare agains,
        # and because we sometimes want to print the result of our digest functions
        # always convert our results to a hex string via nss.data_to_hex()
        proc = subprocess.Popen([ref_cmd, in_filename], stdout=subprocess.PIPE)
        status = proc.wait()
        reference_digest = proc.stdout.read().split()[0]
        if verbose:
            print 'reference_digest\n%s' % (reference_digest)

        # Run the test with convenience digest function (e.g. nss.sha256_digest, etc.).
        test_digest = nss.data_to_hex(nss_digest_func(ref_data),
                                      separator=None)
        if verbose:
            print 'nss %s\n%s' % (nss_digest_func.__name__, test_digest)

        self.assertEqual(test_digest, reference_digest,
                         msg='nss %s test failed reference=%s test=%s' % \
                             (nss_digest_func.__name__, reference_digest, test_digest))

        # Run the test using the generic hash_buf function specifying the hash algorithm.
        test_digest = nss.data_to_hex(nss.hash_buf(hash_oid, ref_data),
                                      separator=None)
        if verbose: print 'nss.hash_buf %s\n%s' % (hash_oid_name, test_digest)

        self.assertEqual(test_digest, reference_digest,
                         msg='nss.hash_buf %s test failed reference=%s test=%s' % \
                             (hash_oid_name, reference_digest, test_digest))

        # Run the test using the lowest level hashing functions by specifying the hash algorithm.
        # The entire input data is supplied all at once in a single call.
        context = nss.create_digest_context(hash_oid)
        context.digest_begin()
        context.digest_op(ref_data)
        test_digest = nss.data_to_hex(context.digest_final(), separator=None)
        if verbose:
            print 'nss.digest_context %s\n%s' % (hash_oid_name, test_digest)

        self.assertEqual(test_digest, reference_digest,
                         msg='nss.digest_context %s test failed reference=%s test=%s' % \
                             (hash_oid_name, reference_digest, test_digest))

        # Run the test using the lowest level hashing functions by specifying the hash algorithm
        # and feeding 'chunks' of data one at a time to be consumed.
        in_file = open(in_filename, 'r')
        context = nss.create_digest_context(hash_oid)
        context.digest_begin()
        while True:
            in_data = in_file.read(chunk_size)
            if len(in_data) == 0:
                break
            context.digest_op(in_data)

        test_digest = nss.data_to_hex(context.digest_final(), separator=None)
        if verbose:
            print 'chunked nss.digest_context %s\n%s' % (hash_oid_name,
                                                         test_digest)

        self.assertEqual(test_digest, reference_digest,
                         msg='chunked nss.digest_context %s test failed reference=%s test=%s' % \
                             (hash_oid_name, reference_digest, test_digest))
Example #28
0
    def execute(self, csr, **kw):
        ca_enabled_check()

        ldap = self.api.Backend.ldap2
        add = kw.get('add')
        request_type = kw.get('request_type')
        profile_id = kw.get('profile_id', self.Backend.ra.DEFAULT_PROFILE)
        ca = '.'  # top-level CA hardcoded until subca plugin implemented
        """
        Access control is partially handled by the ACI titled
        'Hosts can modify service userCertificate'. This is for the case
        where a machine binds using a host/ prinicpal. It can only do the
        request if the target hostname is in the managedBy attribute which
        is managed using the add/del member commands.

        Binding with a user principal one needs to be in the request_certs
        taskgroup (directly or indirectly via role membership).
        """

        principal_string = kw.get('principal')
        principal = split_any_principal(principal_string)
        servicename, principal_name, realm = principal
        if servicename is None:
            principal_type = USER
        elif servicename == 'host':
            principal_type = HOST
        else:
            principal_type = SERVICE

        bind_principal = split_any_principal(getattr(context, 'principal'))
        bind_service, bind_name, bind_realm = bind_principal

        if bind_service is None:
            bind_principal_type = USER
        elif bind_service == 'host':
            bind_principal_type = HOST
        else:
            bind_principal_type = SERVICE

        if bind_principal != principal and bind_principal_type != HOST:
            # Can the bound principal request certs for another principal?
            self.check_access()

        try:
            self.check_access("request certificate ignore caacl")
            bypass_caacl = True
        except errors.ACIError:
            bypass_caacl = False

        if not bypass_caacl:
            caacl_check(principal_type, principal_string, ca, profile_id)

        try:
            subject = pkcs10.get_subject(csr)
            extensions = pkcs10.get_extensions(csr)
            subjectaltname = pkcs10.get_subjectaltname(csr) or ()
        except (NSPRError, PyAsn1Error, ValueError) as e:
            raise errors.CertificateOperationError(
                error=_("Failure decoding Certificate Signing Request: %s") %
                e)

        # self-service and host principals may bypass SAN permission check
        if bind_principal != principal and bind_principal_type != HOST:
            if '2.5.29.17' in extensions:
                self.check_access('request certificate with subjectaltname')

        dn = None
        principal_obj = None
        # See if the service exists and punt if it doesn't and we aren't
        # going to add it
        try:
            if principal_type == SERVICE:
                principal_obj = api.Command['service_show'](principal_string,
                                                            all=True)
            elif principal_type == HOST:
                principal_obj = api.Command['host_show'](principal_name,
                                                         all=True)
            elif principal_type == USER:
                principal_obj = api.Command['user_show'](principal_name,
                                                         all=True)
        except errors.NotFound as e:
            if principal_type == SERVICE and add:
                principal_obj = api.Command['service_add'](principal_string,
                                                           force=True)
            else:
                raise errors.NotFound(
                    reason=_("The principal for this request doesn't exist."))
        principal_obj = principal_obj['result']
        dn = principal_obj['dn']

        # Ensure that the DN in the CSR matches the principal
        cn = subject.common_name  #pylint: disable=E1101
        if not cn:
            raise errors.ValidationError(
                name='csr',
                error=_("No Common Name was found in subject of request."))

        if principal_type in (SERVICE, HOST):
            if cn.lower() != principal_name.lower():
                raise errors.ACIError(info=_(
                    "hostname in subject of request '%(cn)s' "
                    "does not match principal hostname '%(hostname)s'") %
                                      dict(cn=cn, hostname=principal_name))
        elif principal_type == USER:
            # check user name
            if cn != principal_name:
                raise errors.ValidationError(
                    name='csr',
                    error=_("DN commonName does not match user's login"))

            # check email address
            mail = subject.email_address  #pylint: disable=E1101
            if mail is not None and mail not in principal_obj.get('mail', []):
                raise errors.ValidationError(
                    name='csr',
                    error=_("DN emailAddress does not match "
                            "any of user's email addresses"))

        # We got this far so the principal entry exists, can we write it?
        if not ldap.can_write(dn, "usercertificate"):
            raise errors.ACIError(
                info=_("Insufficient 'write' privilege "
                       "to the 'userCertificate' attribute of entry '%s'.") %
                dn)

        # Validate the subject alt name, if any
        for name_type, name in subjectaltname:
            if name_type == pkcs10.SAN_DNSNAME:
                name = unicode(name)
                alt_principal_obj = None
                alt_principal_string = None
                try:
                    if principal_type == HOST:
                        alt_principal_string = 'host/%s@%s' % (name, realm)
                        alt_principal_obj = api.Command['host_show'](name,
                                                                     all=True)
                    elif principal_type == SERVICE:
                        alt_principal_string = '%s/%s@%s' % (servicename, name,
                                                             realm)
                        alt_principal_obj = api.Command['service_show'](
                            alt_principal_string, all=True)
                    elif principal_type == USER:
                        raise errors.ValidationError(
                            name='csr',
                            error=_("subject alt name type %s is forbidden "
                                    "for user principals") % name_type)
                except errors.NotFound:
                    # We don't want to issue any certificates referencing
                    # machines we don't know about. Nothing is stored in this
                    # host record related to this certificate.
                    raise errors.NotFound(reason=_(
                        'The service principal for '
                        'subject alt name %s in certificate request does not '
                        'exist') % name)
                if alt_principal_obj is not None:
                    altdn = alt_principal_obj['result']['dn']
                    if not ldap.can_write(altdn, "usercertificate"):
                        raise errors.ACIError(info=_(
                            "Insufficient privilege to create a certificate "
                            "with subject alt name '%s'.") % name)
                if alt_principal_string is not None and not bypass_caacl:
                    caacl_check(principal_type, alt_principal_string, ca,
                                profile_id)
            elif name_type in (pkcs10.SAN_OTHERNAME_KRB5PRINCIPALNAME,
                               pkcs10.SAN_OTHERNAME_UPN):
                if split_any_principal(name) != principal:
                    raise errors.ACIError(
                        info=_("Principal '%s' in subject alt name does not "
                               "match requested principal") % name)
            elif name_type == pkcs10.SAN_RFC822NAME:
                if principal_type == USER:
                    if name not in principal_obj.get('mail', []):
                        raise errors.ValidationError(
                            name='csr',
                            error=_("RFC822Name does not match "
                                    "any of user's email addresses"))
                else:
                    raise errors.ValidationError(
                        name='csr',
                        error=_("subject alt name type %s is forbidden "
                                "for non-user principals") % name_type)
            else:
                raise errors.ACIError(
                    info=_("Subject alt name type %s is forbidden") %
                    name_type)

        # Request the certificate
        result = self.Backend.ra.request_certificate(csr,
                                                     profile_id,
                                                     request_type=request_type)
        cert = x509.load_certificate(result['certificate'])
        result['issuer'] = unicode(cert.issuer)
        result['valid_not_before'] = unicode(cert.valid_not_before_str)
        result['valid_not_after'] = unicode(cert.valid_not_after_str)
        result['md5_fingerprint'] = unicode(
            nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
        result['sha1_fingerprint'] = unicode(
            nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])

        # Success? Then add it to the principal's entry
        # (unless the profile tells us not to)
        profile = api.Command['certprofile_show'](profile_id)
        store = profile['result']['ipacertprofilestoreissued'][0] == 'TRUE'
        if store and 'certificate' in result:
            cert = str(result.get('certificate'))
            kwargs = dict(addattr=u'usercertificate={}'.format(cert))
            if principal_type == SERVICE:
                api.Command['service_mod'](principal_string, **kwargs)
            elif principal_type == HOST:
                api.Command['host_mod'](principal_name, **kwargs)
            elif principal_type == USER:
                api.Command['user_mod'](principal_name, **kwargs)

        return dict(result=result)
Example #29
0
    def do_test(self, name, ref_cmd, nss_digest_func, hash_oid):
        hash_oid_name = nss.oid_str(hash_oid)

        if verbose:
            print 'running test %s: nss_digest_func=%s hash_oid=%s in_filename=%s' % \
                (name, nss_digest_func.__name__, hash_oid_name, in_filename)

        # read the data in from the file
        ref_data = open(in_filename).read()

        # Run the system hash function to get a reference result.
        # Since we're testing the python-nss binding we assume
        # the system command is entirely independent and correct.
        #
        # Because our digest routines return raw data (e.g. a buffer of octets)
        # and the system hash command returns a hex string which we need to compare agains,
        # and because we sometimes want to print the result of our digest functions
        # always convert our results to a hex string via nss.data_to_hex()
        proc = subprocess.Popen([ref_cmd, in_filename], stdout=subprocess.PIPE)
        status = proc.wait();
        reference_digest = proc.stdout.read().split()[0]
        if verbose:
            print 'reference_digest\n%s' % (reference_digest)

        # Run the test with convenience digest function (e.g. nss.sha256_digest, etc.).
        test_digest =  nss.data_to_hex(nss_digest_func(ref_data), separator=None)
        if verbose: print 'nss %s\n%s' % (nss_digest_func.__name__, test_digest)

        self.assertEqual(test_digest, reference_digest,
                         msg='nss %s test failed reference=%s test=%s' % \
                             (nss_digest_func.__name__, reference_digest, test_digest))

        # Run the test using the generic hash_buf function specifying the hash algorithm.
        test_digest =  nss.data_to_hex(nss.hash_buf(hash_oid, ref_data), separator=None)
        if verbose: print 'nss.hash_buf %s\n%s' % (hash_oid_name, test_digest)

        self.assertEqual(test_digest, reference_digest,
                         msg='nss.hash_buf %s test failed reference=%s test=%s' % \
                             (hash_oid_name, reference_digest, test_digest))

        # Run the test using the lowest level hashing functions by specifying the hash algorithm.
        # The entire input data is supplied all at once in a single call.
        context = nss.create_digest_context(hash_oid)
        context.digest_begin()
        context.digest_op(ref_data)
        test_digest = nss.data_to_hex(context.digest_final(), separator=None)
        if verbose: print 'nss.digest_context %s\n%s' % (hash_oid_name, test_digest)

        self.assertEqual(test_digest, reference_digest,
                         msg='nss.digest_context %s test failed reference=%s test=%s' % \
                             (hash_oid_name, reference_digest, test_digest))

        # Run the test using the lowest level hashing functions by specifying the hash algorithm
        # and feeding 'chunks' of data one at a time to be consumed.
        in_file = open(in_filename, 'r')
        context = nss.create_digest_context(hash_oid)
        context.digest_begin()
        while True:
            in_data = in_file.read(chunk_size)
            if len(in_data) == 0:
                break
            context.digest_op(in_data)

        test_digest = nss.data_to_hex(context.digest_final(), separator=None)
        if verbose: print 'chunked nss.digest_context %s\n%s' % (hash_oid_name, test_digest)

        self.assertEqual(test_digest, reference_digest,
                         msg='chunked nss.digest_context %s test failed reference=%s test=%s' % \
                             (hash_oid_name, reference_digest, test_digest))
Example #30
0
                         (3, 'Not Before: %s' % cert.valid_not_before_str),
                         (3, 'Not After:  %s' % cert.valid_not_after_str)]))
print(nss.indented_format([(2, 'Subject: "%s"' % cert.subject)]))
print(nss.indented_format([(2, 'Subject Public Key Info:')]))
print(nss.indented_format(cert.subject_public_key_info.format_lines(3)))

if len(extensions) > 0:
    print(
        nss.indented_format([(1, 'Signed Extensions: (%d)' % len(extensions))
                             ]))
    for extension in extensions:
        print_extension(2, extension)

print(nss.indented_format(cert.signed_data.format_lines(1)))

print(nss.indented_format([(1, 'Fingerprint (MD5):')]))
print(
    nss.indented_format(
        nss.make_line_fmt_tuples(
            2,
            nss.data_to_hex(nss.md5_digest(cert.der_data),
                            nss.OCTETS_PER_LINE_DEFAULT))))

print(nss.indented_format([(1, 'Fingerprint (SHA1):')]))
print(
    nss.indented_format(
        nss.make_line_fmt_tuples(
            2,
            nss.data_to_hex(nss.sha1_digest(cert.der_data),
                            nss.OCTETS_PER_LINE_DEFAULT))))