Beispiel #1
0
    def sign(self, sigstr):
        assert isinstance(sigstr, str)  # for now, not unicode. Python 3?

        key_info = self._get_key_info()
        log.debug("sign %r with %s key (algo %s, fp %s)", sigstr,
                  key_info["type"], key_info["algorithm"],
                  key_info["fingerprint"])

        if key_info["type"] == "agent":
            response = key_info["agent_key"].sign_ssh_data(None, sigstr)
            signed_raw = signature_from_agent_sign_response(response)
            signed = base64.b64encode(signed_raw)
        elif key_info["type"] == "ssh_key":
            hash_algo = key_info["algorithm"].split('-')[1]
            hash_class = {
                "sha1": SHA,
                "sha256": SHA256,
                "sha512": SHA512
            }[hash_algo]
            hasher = hash_class.new()
            hasher.update(sigstr)
            signed_raw = key_info["signer"].sign(hasher)
            signed = base64.b64encode(signed_raw)
        else:
            raise MantaError("internal error: unknown key type: %r" %
                             key_info["type"])

        return (key_info["algorithm"], key_info["fingerprint"], signed)
Beispiel #2
0
    def sign(self, sigstr):
        if not isinstance(sigstr, bytes):
            assert isinstance(sigstr, str)
            sigstr = sigstr.encode("utf-8")

        key_info = self._get_key_info()
        log.debug("sign %r with %s key (algo %s, fp %s)", sigstr,
                  key_info["type"], key_info["algorithm"],
                  key_info["fingerprint"])

        if key_info["type"] == "agent":
            response = key_info["agent_key"].sign_ssh_data(sigstr)

            if re.search(r'^ecdsa-', key_info['algorithm']):
                signed_raw = ecdsa_sig_from_agent_signed_response(response)
            else:
                signed_raw = rsa_sig_from_agent_signed_response(response)

            signed = base64.b64encode(signed_raw)
        elif key_info["type"] == "ssh_key":
            signed = ssh_key_sign(key_info, sigstr)
        else:
            raise MantaError("internal error: unknown key type: %r" %
                             key_info["type"])

        return (key_info["algorithm"], key_info["fingerprint"], signed)
Beispiel #3
0
    def _get_key_info(self):
        """Get key info appropriate for signing: either from the ssh agent
        or from a private key.
        """
        if self._key_info_cache is not None:
            return self._key_info_cache

        errors = []

        # First try the agent.
        try:
            key_info = agent_key_info_from_key_id(self.key_id)
        except MantaError:
            _, ex, _ = sys.exc_info()
            errors.append(ex)
        else:
            self._key_info_cache = key_info
            return self._key_info_cache

        # Try loading from "~/.ssh/*".
        try:
            key_info = ssh_key_info_from_key_data(self.key_id)
        except MantaError:
            _, ex, _ = sys.exc_info()
            errors.append(ex)
        else:
            self._key_info_cache = key_info
            return self._key_info_cache

        raise MantaError("could not find key info for signing: %s" %
                         "; ".join(map(unicode, errors)))
Beispiel #4
0
def ssh_key_info_from_key_data(key_id, priv_key=None):
    """Get/load SSH key info necessary for signing.

    @param key_id {str} Either a private ssh key fingerprint, e.g.
        'b3:f0:a1:6c:18:3b:42:63:fd:6e:57:42:74:17:d4:bc', or the path to
        an ssh private key file (like ssh's IdentityFile config option).
    @param priv_key {str} Optional. SSH private key file data (PEM format).
    @return {dict} with these keys:
        - type: "agent"
        - signer: Crypto signer class (a PKCS#1 v1.5 signer for RSA keys)
        - fingerprint: key fingerprint
        - algorithm: 'rsa-sha256'  DSA not current supported. Hash algorithm
          selection is not exposed.
        - ... some others added by `load_ssh_key()`
    """
    if FINGERPRINT_RE.match(key_id) and priv_key:
        key_info = {
            "fingerprint": key_id,
            "priv_key": priv_key
        }
    else:
        # Otherwise, we attempt to load necessary details from ~/.ssh.
        key_info = load_ssh_key(key_id)

    # Load an RSA key signer.
    key = None
    try:
        key = RSA.importKey(key_info["priv_key"])
    except ValueError:
        if "priv_key_path" in key_info:
            prompt = "Passphrase [%s]: " % key_info["priv_key_path"]
        else:
            prompt = "Passphrase: "
        for i in range(3):
            passphrase = getpass(prompt)
            if not passphrase:
                break
            try:
                key = RSA.importKey(key_info["priv_key"], passphrase)
            except ValueError:
                continue
            else:
                break
        if not key:
            details = ""
            if "priv_key_path" in key_info:
                details = " (%s)" % key_info["priv_key_path"]
            raise MantaError("could not import key" + details)
    key_info["signer"] = PKCS1_v1_5.new(key)

    key_info["type"] = "ssh_key"
    key_info["algorithm"] = "rsa-sha256"
    return key_info
Beispiel #5
0
def ssh_key_info_from_key_data(key_id, priv_key=None):
    """Get/load SSH key info necessary for signing.

    @param key_id {str} Either a private ssh key fingerprint, e.g.
        'b3:f0:a1:6c:18:3b:42:63:fd:6e:57:42:74:17:d4:bc', or the path to
        an ssh private key file (like ssh's IdentityFile config option).
    @param priv_key {str} Optional. SSH private key file data (PEM format).
    @return {dict} with these keys:
        - type: "agent"
        - signer: Crypto signer class (a PKCS#1 v1.5 signer for RSA keys)
        - fingerprint: key md5 fingerprint
        - algorithm: See ALGO_FROM_SSH_KEY_TYPE for supported list.
        - ... some others added by `load_ssh_key()`
    """
    if FINGERPRINT_RE.match(key_id) and priv_key:
        key_info = {"fingerprint": key_id, "priv_key": priv_key}
    else:
        # Otherwise, we attempt to load necessary details from ~/.ssh.
        key_info = load_ssh_key(key_id)

    # Load a key signer.
    key = None
    try:
        key = serialization.load_pem_private_key(key_info["priv_key"],
                                                 password=None,
                                                 backend=default_backend())
    except TypeError, ex:
        log.debug(
            "could not import key without passphrase (will "
            "try with passphrase): %s", ex)
        if "priv_key_path" in key_info:
            prompt = "Passphrase [%s]: " % key_info["priv_key_path"]
        else:
            prompt = "Passphrase: "
        for i in range(3):
            passphrase = getpass(prompt)
            if not passphrase:
                break
            try:
                key = serialization.load_pem_private_key(
                    key_info["priv_key"],
                    password=passphrase,
                    backend=default_backend())
            except ValueError:
                continue
            else:
                break
        if not key:
            details = ""
            if "priv_key_path" in key_info:
                details = " (%s)" % key_info["priv_key_path"]
            raise MantaError("could not import key" + details)
Beispiel #6
0
def agent_key_info_from_key_id(key_id):
    """Find a matching key in the ssh-agent.

    @param key_id {str} Either a private ssh key fingerprint, e.g.
        'b3:f0:a1:6c:18:3b:42:63:fd:6e:57:42:74:17:d4:bc', or the path to
        an ssh private key file (like ssh's IdentityFile config option).
    @return {dict} with these keys:
        - type: "agent"
        - agent_key: paramiko AgentKey
        - fingerprint: key fingerprint
        - algorithm: "rsa-sha1"  Currently don't support DSA agent signing.
    """
    # Need the fingerprint of the key we're using for signing. If it
    # is a path to a priv key, then we need to load it.
    if not FINGERPRINT_RE.match(key_id):
        ssh_key = load_ssh_key(key_id, True)
        fingerprint = ssh_key["fingerprint"]
    else:
        fingerprint = key_id

    # Look for a matching fingerprint in the ssh-agent keys.
    keys = Agent().get_keys()

    for key in keys:
        raw_key = key.blob

        # The MD5 fingerprint functions return the hexdigest without the hash
        # algorithm prefix ("MD5:"), and the SHA256 functions return the
        # fingerprint with the prefix ("SHA256:").  Ideally we'd want to
        # normalize these, but more importantly we don't want to break backwards
        # compatibility for either the SHA or MD5 users.
        md5_fp = fingerprint_from_raw_ssh_pub_key(raw_key)
        sha_fp = sha256_fingerprint_from_raw_ssh_pub_key(raw_key)
        if (sha_fp == fingerprint or md5_fp == fingerprint
                or "MD5:" + md5_fp == fingerprint):

            # Canonicalize it to the md5 fingerprint.
            md5_fingerprint = md5_fp
            break
    else:
        raise MantaError('no ssh-agent key with fingerprint "%s"' %
                         fingerprint)

    return {
        "type": "agent",
        "agent_key": key,
        "fingerprint": md5_fingerprint,
        "algorithm": ALGO_FROM_SSH_KEY_TYPE[key.name]
    }
Beispiel #7
0
def agent_key_info_from_key_id(key_id):
    """Find a matching key in the ssh-agent.

    @param key_id {str} Either a private ssh key fingerprint, e.g.
        'b3:f0:a1:6c:18:3b:42:63:fd:6e:57:42:74:17:d4:bc', or the path to
        an ssh private key file (like ssh's IdentityFile config option).
    @return {dict} with these keys:
        - type: "agent"
        - agent_key: paramiko AgentKey
        - fingerprint: key fingerprint
        - algorithm: "rsa-sha1"  Currently don't support DSA agent signing.
    """
    # Need the fingerprint of the key we're using for signing. If it
    # is a path to a priv key, then we need to load it.
    if not FINGERPRINT_RE.match(key_id):
        ssh_key = load_ssh_key(key_id, True)
        fingerprint = ssh_key["fingerprint"]
    else:
        fingerprint = key_id

    # Look for a matching fingerprint in the ssh-agent keys.
    import paramiko
    keys = paramiko.Agent().get_keys()
    for key in keys:
        raw_key = str(key)
        if sha256_fingerprint_from_raw_ssh_pub_key(raw_key) == fingerprint:
            # Canonicalize it to the md5 fingerprint.
            md5_fingerprint = fingerprint_from_raw_ssh_pub_key(raw_key)
            break
        elif fingerprint_from_raw_ssh_pub_key(raw_key) == fingerprint:
            md5_fingerprint = fingerprint
            break
    else:
        raise MantaError('no ssh-agent key with fingerprint "%s"' %
                         fingerprint)

    # TODO:XXX DSA support possible with paramiko?
    algorithm = 'rsa-sha1'

    return {
        "type": "agent",
        "agent_key": key,
        "fingerprint": md5_fingerprint,
        "algorithm": algorithm
    }
Beispiel #8
0
def load_ssh_key(key_id, skip_priv_key=False):
    """
    Load a local ssh private key (in PEM format). PEM format is the OpenSSH
    default format for private keys.

    See similar code in imgapi.js#loadSSHKey.

    @param key_id {str} An ssh public key fingerprint or ssh private key path.
    @param skip_priv_key {boolean} Optional. Default false. If true, then this
        will skip loading the private key file and `priv_key` will be `None`
        in the retval.
    @returns {dict} with these keys:
        - pub_key_path
        - fingerprint
        - priv_key_path
        - priv_key
    """
    priv_key = None

    # If `key_id` is already a private key path, then easy.
    if not FINGERPRINT_RE.match(key_id):
        if not skip_priv_key:
            f = open(key_id)
            try:
                priv_key = f.read()
            finally:
                f.close()
        pub_key_path = key_id + '.pub'
        f = open(pub_key_path)
        try:
            pub_key = f.read()
        finally:
            f.close()
        fingerprint = fingerprint_from_ssh_pub_key(pub_key)
        return dict(pub_key_path=pub_key_path,
                    fingerprint=fingerprint,
                    priv_key_path=key_id,
                    priv_key=priv_key)

    # Else, look at all pub/priv keys in "~/.ssh" for a matching fingerprint.
    fingerprint = key_id
    pub_key_glob = expanduser('~/.ssh/*.pub')
    for pub_key_path in glob(pub_key_glob):
        f = open(pub_key_path)
        try:
            pub_key = f.read()
        finally:
            f.close()
        if fingerprint_from_ssh_pub_key(pub_key) == fingerprint:
            break
    else:
        raise MantaError("no '~/.ssh/*.pub' key found with fingerprint '%s'" %
                         fingerprint)
    priv_key_path = os.path.splitext(pub_key_path)[0]
    if not skip_priv_key:
        f = open(priv_key_path)
        try:
            priv_key = f.read()
        finally:
            f.close()
    return dict(pub_key_path=pub_key_path,
                fingerprint=fingerprint,
                priv_key_path=priv_key_path,
                priv_key=priv_key)
Beispiel #9
0
def load_ssh_key(key_id, skip_priv_key=False):
    """
    Load a local ssh private key (in PEM format). PEM format is the OpenSSH
    default format for private keys.

    See similar code in imgapi.js#loadSSHKey.

    @param key_id {str} An ssh public key fingerprint or ssh private key path.
    @param skip_priv_key {boolean} Optional. Default false. If true, then this
        will skip loading the private key file and `priv_key` will be `None`
        in the retval.
    @returns {dict} with these keys:
        - pub_key_path
        - fingerprint
        - priv_key_path
        - priv_key
        - algorithm
    """
    priv_key = None

    # If `key_id` is already a private key path, then easy.
    if not FINGERPRINT_RE.match(key_id):
        if not skip_priv_key:
            f = io.open(key_id, 'rb')
            try:
                priv_key = f.read()
            finally:
                f.close()
        pub_key_path = key_id + '.pub'
        f = io.open(pub_key_path, 'r')
        try:
            pub_key = f.read()
        finally:
            f.close()
        fingerprint = fingerprint_from_ssh_pub_key(pub_key)

        # XXX: pubkey should NOT be in PEM format.
        try:
            algo = ALGO_FROM_SSH_KEY_TYPE[pub_key.split()[0]]
        except KeyError:
            raise MantaError("Unsupported key type for: {}".format(key_id))

        return dict(pub_key_path=pub_key_path,
                    fingerprint=fingerprint,
                    priv_key_path=key_id,
                    priv_key=priv_key,
                    algorithm=algo)

    # Else, look at all pub/priv keys in "~/.ssh" for a matching fingerprint.
    fingerprint = key_id

    pub_key_glob = expanduser('~/.ssh/*.pub')
    pub_key = None
    for pub_key_path in glob(pub_key_glob):
        try:
            f = io.open(pub_key_path, 'r')
        except IOError:
            # This can happen if the .pub file is a broken symlink.
            log.debug("could not open '%s', skip it", pub_key_path)
            continue
        try:
            pub_key = f.read()
        finally:
            f.close()

        # The MD5 fingerprint functions return the hexdigest without the hash
        # algorithm prefix ("MD5:"), and the SHA256 functions return the
        # fingerprint with the prefix ("SHA256:").  Ideally we'd want to
        # normalize these, but more importantly we don't want to break backwards
        # compatibility for either the SHA or MD5 users.
        md5_fp = fingerprint_from_ssh_pub_key(pub_key)
        sha256_fp = sha256_fingerprint_from_ssh_pub_key(pub_key)
        if (sha256_fp == fingerprint or md5_fp == fingerprint
                or "MD5:" + md5_fp == fingerprint):

            # if the user has given us sha256 fingerprint, canonicalize
            # it to the md5 fingerprint
            fingerprint = md5_fp
            break
    else:
        raise MantaError("no '~/.ssh/*.pub' key found with fingerprint '%s'" %
                         fingerprint)

    # XXX: pubkey should NOT be in PEM format.
    try:
        algo = ALGO_FROM_SSH_KEY_TYPE[pub_key.split()[0]]
    except KeyError:
        raise MantaError("Unsupported key type for: {}".format(key_id))

    priv_key_path = os.path.splitext(pub_key_path)[0]
    if not skip_priv_key:
        f = io.open(priv_key_path, 'rb')
        try:
            priv_key = f.read()
        finally:
            f.close()
    return dict(pub_key_path=pub_key_path,
                fingerprint=fingerprint,
                priv_key_path=priv_key_path,
                priv_key=priv_key,
                algorithm=algo)
Beispiel #10
0
            # This can happen if the .pub file is a broken symlink.
            log.debug("could not open '%s', skip it", pub_key_path)
            continue
        try:
            pub_key = f.read()
        finally:
            f.close()
        if sha256_fingerprint_from_ssh_pub_key(pub_key) == fingerprint:
            # if the user has given us sha256 fingerprint, canonicalize
            # it to the md5 fingerprint
            fingerprint = fingerprint_from_ssh_pub_key(pub_key)
            break
        elif fingerprint_from_ssh_pub_key(pub_key) == fingerprint:
            break
    else:
        raise MantaError("no '~/.ssh/*.pub' key found with fingerprint '%s'" %
                         fingerprint)
    priv_key_path = os.path.splitext(pub_key_path)[0]
    if not skip_priv_key:
        f = open(priv_key_path)
        try:
            priv_key = f.read()
        finally:
            f.close()
    return dict(pub_key_path=pub_key_path,
                fingerprint=fingerprint,
                priv_key_path=priv_key_path,
                priv_key=priv_key)


def unpack_agent_response(d):
    parts = []
Beispiel #11
0
        # The MD5 fingerprint functions return the hexdigest without the hash
        # algorithm prefix ("MD5:"), and the SHA256 functions return the
        # fingerprint with the prefix ("SHA256:").  Ideally we'd want to
        # normalize these, but more importantly we don't want to break backwards
        # compatibility for either the SHA or MD5 users.
        md5_fp = fingerprint_from_ssh_pub_key(pub_key)
        sha256_fp = sha256_fingerprint_from_ssh_pub_key(pub_key)
        if (sha256_fp == fingerprint or md5_fp == fingerprint
                or "MD5:" + md5_fp == fingerprint):

            # if the user has given us sha256 fingerprint, canonicalize
            # it to the md5 fingerprint
            fingerprint = md5_fp
            break
    else:
        raise MantaError("no '~/.ssh/*.pub' key found with fingerprint '%s'" %
                         fingerprint)

    # XXX: pubkey should NOT be in PEM format.
    try:
        algo = ALGO_FROM_SSH_KEY_TYPE[pub_key.split()[0]]
    except KeyError:
        raise MantaError("Unsupported key type for: {}".format(key_id))

    priv_key_path = os.path.splitext(pub_key_path)[0]
    if not skip_priv_key:
        f = open(priv_key_path)
        try:
            priv_key = f.read()
        finally:
            f.close()
    return dict(pub_key_path=pub_key_path,