Exemple #1
0
    def gen_putty(self):

        # set filenames
        file_key = self.filenames["private"].format(self.tmpfile)
        file_putty = self.filenames["private_putty"].format(self.tmpfile)

        # merge arguments and command
        cmd = [
            self.opts.get('sshkey_bin_puttygen'), "{}".format(file_key), "-O",
            "private", "--old-passphrase", "{}".format(self.pwdfile),
            "--new-passphrase", "{}".format(self.pwdfile), "-o",
            "{}".format(file_putty), "-C",
            "{}".format(self.opts.get('sshkey_comment'))
        ]
        proc = Process("puttygen", cmd).run()

        # set result
        result = {}
        result['private_putty'] = hlp.get_file_contents(file_putty)

        # generate putty fingerprint
        cmd = [
            self.opts.get('sshkey_bin_puttygen'), "{}".format(file_putty),
            "-O", "fingerprint"
        ]
        proc = Process("puttygen", cmd).run()
        stdout = proc.getstdout()
        result['fingerprint_putty'] = "\n".join(stdout)

        # return result
        return result
Exemple #2
0
    def gen_fingerprints(self, fptype, pubfile):

        # generate fingerprints - output sent to stdout
        cmd = [
            self.opts.get('sshhostkey_bin_keygen'),  # full path to binary
            "-l",  # list fingerprint
            "-v",  # list visual fingerprint
            "-E{}".format(fptype),  # fingerprint hash algorithm
            "-f{}".format(pubfile)
        ]  # full path to public key

        # run process and catch stdout
        proc = Process("ssh-keygen", cmd).run()
        stdout = proc.getstdout()
        fpline = stdout.pop(0)

        # set results
        result = {}
        result['fingerprint_{}'.format(fptype)] = fpline
        result['fingerprint_{}_clean'.format(
            fptype)] = hlp.extract_fingerprint(fpline)
        result['fingerprint_{}_art'.format(fptype)] = "\n".join(stdout)

        # return
        return result
Exemple #3
0
    def check_versions(self):

        msg.vvvv("checking gnupg and libgcrypt versions")

        # set command
        cmd = [self.opts.get('gpgkey_bin')]
        args = ["--version"]
        cmd += args

        # run subprocess
        proc = Process("gpg", cmd).run()
        stdout = proc.getstdout()

        # find gpg version
        regex_gpg = r"gpg\s+\(GnuPG\)\s+(\d+\.\d+\.?\d*)$"
        match_gpg = re.match(regex_gpg, stdout[0])

        # sanity check
        if re.compile(regex_gpg).groups < 1:
            msg.fail(
                "could not find a valid gpg version number in string [{}]".
                format(stdout[0]))

        # find libgcrypt version
        regex_libgcrypt = r"libgcrypt\s+(\d+\.\d+\.?\d*)$"
        match_libgcrypt = re.match(regex_libgcrypt, stdout[1])

        # sanity check
        if re.compile(regex_libgcrypt).groups < 1:
            msg.fail(
                "could not find a valid libgcrypt version number in string [{}]"
                .format(stdout[1]))

        # check versions
        versions = {
            'gpg': match_gpg.group(1),
            'libgcrypt': match_libgcrypt.group(1),
        }
        req_gpg = '2.1.17'
        req_libgcrypt = '1.8.1'

        # sanity check
        if version.parse(
                versions['gpg']) < version.parse(req_gpg) or version.parse(
                    versions['libgcrypt']) < version.parse(req_libgcrypt):
            msg.fail(
                "gpg version [{}] and libgcrypt version [{}] are required; [{}] and [{}] given"
                .format(req_gpg, req_libgcrypt, versions['gpg'],
                        versions['libgcrypt']))
        else:
            msg.vvvv("gnupg version [{}] and libgcrypt version [{}] detected".
                     format(versions['gpg'], versions['libgcrypt']))

        return True
Exemple #4
0
    def gen_pkcs8(self):

        # set filenames
        file_key = self.filenames["private"].format(self.tmpfile)
        file_pub = self.filenames["public"].format(self.tmpfile)
        file_pkcs8 = self.filenames["private_pkcs8"].format(self.tmpfile)

        # create password file
        # BEWARE: when supplying the SAME password file for both a -passin file:filename and -passout file:filename parameter,
        # openssl will use the first line as the passin password and the second line as the passout parameter
        # if the password file does not contain a second line, openssl will fail with the following message:
        #  Error reading password from BIO
        #  Error getting passwords
        # to avoid this, we write the same password to the password file twice. Since we're only using openssl to convert a
        # single key into multiple formats, this does not pose a security risk
        # to avoid confusing and prevent potential error with other processes requiring the password file, we generate a seperate one
        file_pwd = hlp.create_pwd_file(
            self.opts.get_tmp_filename(),
            "{}\n{}".format(self.passphrase, self.passphrase))

        # merge arguments and command
        cmd = [self.opts.get('sshkey_bin_openssl')]
        args = [
            "pkcs8", "-topk8", "-v2", "des3", "-in", "{}".format(file_key),
            "-passin", "file:{}".format(file_pwd), "-out",
            "{}".format(file_pkcs8), "-passout", "file:{}".format(file_pwd)
        ]
        cmd += args

        # run subprocess
        proc = Process("openssl", cmd).run()

        # read file contents
        result = {}
        result['private_pkcs8'] = hlp.get_file_contents(file_pkcs8)

        # generate public key
        cmd = [
            self.opts.get('sshkey_bin_keygen'), "-e", "-mPKCS8",
            "-f{}".format(file_pub)
        ]
        proc = Process("ssh-keygen", cmd).run()
        stdout = proc.getstdout()
        result['public_pkcs8'] = "\n".join(stdout)

        # return result
        return result
Exemple #5
0
    def gen_pem(self):

        # set filenames
        file_pub = self.filenames["public"].format(self.tmpfile)

        # merge arguments and command
        cmd = [
            self.opts.get('sshkey_bin_keygen'), "-e", "-m", "PEM",
            "-f{}".format(file_pub)
        ]
        proc = Process("ssh-keygen", cmd).run()

        # set result
        result = {}
        stdout = proc.getstdout()
        result['public_pem'] = "\n".join(stdout)

        # return result
        return result
Exemple #6
0
    def gen_rfc4716(self):

        # set filenames
        file_pub = self.filenames["public"].format(self.tmpfile)

        # merge arguments and command
        # setting a custom comment for this keytype is not supported
        cmd = [
            self.opts.get('sshkey_bin_keygen'), "-e", "-m", "RFC4716",
            "-f{}".format(file_pub)
        ]
        proc = Process("ssh-keygen", cmd).run()

        # set result
        result = {}
        stdout = proc.getstdout()
        result['public_rfc4716'] = "\n".join(stdout)

        # return result
        return result
Exemple #7
0
    def check_versions(self):

        # only check puttygen version if puttygen is enabled
        if self.opts.get('sshkey_putty_enabled'):

            msg.vvv("checking puttygen version")

            # merge arguments and command
            cmd = [
                self.opts.get('sshkey_bin_puttygen'),
                "--version",
            ]
            proc = Process("puttygen", cmd).run()

            # regex version from first line
            stdout = proc.getstdout()
            regex_putty = r"^.+(\d\.\d+)"
            match_putty = re.match(regex_putty, stdout[0])

            # sanity check
            if re.compile(regex_putty).groups < 1:
                msg.fail(
                    "could not find a valid puttygen version number in string [{}]"
                    .format(stdout[0]))

            # check versions
            versions = {'puttygen': match_putty.group(1)}
            req_puttygen = '0.72'

            # sanity check
            if version.parse(
                    versions['puttygen']) < version.parse(req_puttygen):
                msg.fail(
                    "puttygen version [{}] is required; [{}] given".format(
                        req_puttygen, versions['puttygen']))
            else:
                msg.vvv("puttygen version [{}] detected".format(
                    versions['puttygen']))

        return True
Exemple #8
0
    def gen_bubblebabble(self, pubfile):

        # generate fingerprints - output sent to stdout
        cmd = [
            self.opts.get('sshhostkey_bin_keygen'),  # full path to binary
            "-B",  # list bubble babble fingerprint
            "-f{}".format(pubfile)
        ]  # full path to public key

        # run process and catch stdout
        proc = Process("ssh-keygen", cmd).run()
        stdout = proc.getstdout()
        bbline = stdout.pop(0)

        # set results
        result = {}
        result['fingerprint_bubblebabble'] = bbline
        result['fingerprint_bubblebabble_clean'] = hlp.extract_bubblebabble(
            bbline)

        # return
        return result
Exemple #9
0
    def gen_dns(self, pubfile):

        # generate dns records - output sent to stdout
        cmd = [
            self.opts.get('sshhostkey_bin_keygen'),  # full path to binary
            "-r{}".format(
                self.opts.get('hostname')),  # hostname of dns records
            "-f{}".format(pubfile)
        ]  # full path to public key

        #
        # SSHFP records consist of three things:
        #
        # Algorithm
        #   1 - RSA
        #   2 - DSA
        #   3 - ECDSA
        #   4 - Ed25519
        # Fingerprint type
        #   1 - SHA-1
        #   2 - SHA-256
        # Fingerprint (in hex)
        #
        # EXAMPLE:
        # [root@localhost ~]# ssh-keygen -r my.domain.com -f ./mykey.pub
        # example.com IN SSHFP 4 1 de3dec1fb5eadf130396a60607f5baa6ace831e8
        # example.com IN SSHFP 4 2 a0a4d61227b08addd0d685ded8e475c396831e6d91ab3ac1adf536425fab431f
        #

        # run process and catch stdout
        proc = Process("ssh-keygen", cmd).run()
        stdout = proc.getstdout()

        # set known algorithms and fingerprint types
        algorithms = {
            "1": "rsa",
            "2": "dsa",
            "3": "ecdsa",
            "4": "ed25519",
        }
        fptypes = {
            "1": "sha1",
            "2": "sha256",
        }

        # find algorithm and fptype for each output line
        result = {}
        for l in stdout:

            # split items on space and extract info
            items = l.split(" ")
            alg = algorithms[
                items[3]]  # not added to key, as that is done elsewhere
            fpt = fptypes[items[4]]
            clean = items[3:]
            dnsname = "dns_{}".format(fpt)
            dnsclean = "dns_{}_clean".format(fpt)

            # set dns records
            result[dnsname] = " ".join(items)
            result[dnsclean] = " ".join(clean)

        # return
        return result
Exemple #10
0
    def get_key_info(self, keytype, usage, mapping, fpr=None):

        msg.vvvv(
            "attempt to extract key info from generated keys [{}] with usage [{}]"
            .format(keytype, usage))

        # create the command to generate a new master key
        cmd = [self.opts.get('gpgkey_bin')]
        listargs = self.get_seclist_args(
        ) if keytype == 'secret' else self.get_publist_args()
        cmd += listargs

        # run subprocess; catch the output but don't fail on stderr as gnupg outputs key creation details to stderr instead of stdout
        proc = Process("gpg", cmd, failonstderr=False).run()

        #
        # SAMPLE DATA
        #
        # sec:u:256:22:41343326127FD34F:1566067845:::u:::cC:::+::ed25519:::0:
        # fpr:::::::::0D18E4B6B2698560729D00CE41343326127FD34F:
        # grp:::::::::54AA357FD85BA4D4B7CE86016A3734F00B1BDD07:
        # uid:u::::1566067845::00B9F0DC33EE293CC1E687FFA54A5EA805FD78F8::testing145 (TESTINGCOMM) <*****@*****.**>::::::::::0:
        #

        #
        # line types
        #
        # *** Field 1 - Type of record
        #
        #     - pub :: Public key
        #     - crt :: X.509 certificate
        #     - crs :: X.509 certificate and private key available
        #     - sub :: Subkey (secondary key)
        #     - sec :: Secret key
        #     - ssb :: Secret subkey (secondary key)
        #     - uid :: User id
        #     - uat :: User attribute (same as user id except for field 10).
        #     - sig :: Signature
        #     - rev :: Revocation signature
        #     - rvs :: Revocation signature (standalone) [since 2.2.9]
        #     - fpr :: Fingerprint (fingerprint is in field 10)
        #     - pkd :: Public key data [*]
        #     - grp :: Keygrip
        #     - rvk :: Revocation key
        #     - tfs :: TOFU statistics [*]
        #     - tru :: Trust database information [*]
        #     - spk :: Signature subpacket [*]
        #     - cfg :: Configuration data [*]
        #
        #     Records marked with an asterisk are described at [[*Special%20field%20formats][*Special fields]].        #
        #

        #
        # *** Field 12 - Key capabilities
        #
        #     The defined capabilities are:
        #
        #     - e :: Encrypt
        #     - s :: Sign
        #     - c :: Certify
        #     - a :: Authentication
        #     - ? :: Unknown capability
        #
        #     A key may have any combination of them in any order.  In addition
        #     to these letters, the primary key has uppercase versions of the
        #     letters to denote the _usable_ capabilities of the entire key, and
        #     a potential letter 'D' to indicate a disabled key.
        #

        # determine which codes to look for
        if keytype == 'secret' and usage == 'cert':
            ltype = 'sec'  # secret master key
        elif keytype == 'secret':
            ltype = 'ssb'
        elif keytype == 'public' and usage == 'cert':
            ltype = 'pub'
        else:
            ltype = 'sub'

        if usage == 'cert':
            lcapb = 'c'
        elif usage == 'sign':
            lcapb = 's'
        elif usage == 'auth':
            lcapb = 'a'
        else:
            lcapb = 'e'

        #
        # FIELD TYPES:
        #
        # - Field 1 - Type of record
        # - Field 2 - Validity
        # - Field 3 - Key length
        # - Field 4 - Public key algorithm
        # - Field 5 - KeyID
        # - Field 6 - Creation date
        # - Field 7 - Expiration date
        # - Field 8 - Certificate S/N, UID hash, trust signature info
        # - Field 9 -  Ownertrust
        # - Field 10 - User-ID
        # - Field 11 - Signature class
        # - Field 12 - Key capabilities
        # - Field 13 - Issuer certificate fingerprint or other info
        # - Field 14 - Flag field
        # - Field 15 - S/N of a token
        # - Field 16 - Hash algorithm
        # - Field 17 - Curve name
        # - Field 18 - Compliance flags
        # - Field 19 - Last update
        # - Field 20 - Origin
        # - Field 21 - Comment
        #

        # set empty result
        tmpresult = {}

        # determine the correct line
        correct_line = False
        main_lines = ['sec', 'ssb', 'pub', 'sub']
        follow_lines = ['fpr', 'grp', 'uid']

        # indexes start at 0
        # main parts are for main_lines only
        mainparts = {
            'type': 0,
            'key_length': 2,
            'pubkey_algorithm': 3,
            'keyid': 4,
            'creationdate': 5,
            'expirationdate': 6,
            'key_capabilities': 11,
            'hash_algorithm': 15,
            'curve_name': 16,
        }

        # indexes start at 0
        # follow parts for follow_lines only
        followparts = {
            'type': 0,
            'userid':
            9,  # this is the fingerprint for fpr records and the keygrip for grp records
        }

        #
        # 9.1.  Public-Key Algorithms
        #
        #       ID           Algorithm
        #       --           ---------
        #       1          - RSA (Encrypt or Sign) [HAC]
        #       2          - RSA Encrypt-Only [HAC]
        #       3          - RSA Sign-Only [HAC]
        #       16         - Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
        #       17         - DSA (Digital Signature Algorithm) [FIPS186] [HAC]
        #       18         - Reserved for Elliptic Curve
        #       19         - Reserved for ECDSA
        #       20         - Reserved (formerly Elgamal Encrypt or Sign)
        #       21         - Reserved for Diffie-Hellman (X9.42,
        #                    as defined for IETF-S/MIME)
        #       22         - Ed25519
        #       100 to 110 - Private/Experimental algorithm
        #
        pubkeys = {
            '1': 'RSA (Encrypt or Sign)',
            '2': 'RSA Encrypt-Only',
            '3': 'RSA Sign-Only',
            '16': 'Elgamal (Encrypt-Only)',
            '17': 'DSA [FIPS186]',
            '18': 'Cv25519',
            '22': 'Ed25519',
        }

        msg.vvvv("looping through key details")

        # loop through lines
        for l in proc.getstdout():

            #print("line: {}".format(l))
            # split line into pieces
            pieces = l.split(":")

            # get current type
            ctype = pieces[mainparts.get('type')]

            # check for usage/capabilities
            if ctype in main_lines:
                ccapb = pieces[mainparts.get('key_capabilities')]
            else:
                ccapb = ""

            # check if capabilities OK
            if lcapb in ccapb:
                capbok = True
            else:
                capbok = False

            #print("usage: {} | ctype: {} | ccapb: {} | lcapb: {} | capbok: {}".format(usage,ctype,ccapb,lcapb,capbok))

            # check for main lines
            if ctype in main_lines and capbok and not correct_line:

                # we must be on the correct line
                #print("we are now on the correct line")
                correct_line = True
                current_line = ctype

                for x in mainparts.keys():

                    # skip the type
                    if x == 'type':
                        continue

                    # add the other info to the tmpresult array
                    y = '{}_{}_{}'.format(usage, ctype, x)
                    if x == 'pubkey_algorithm':
                        p = pieces[mainparts.get(x)]
                        z = pubkeys.get(p) if p is not None else ''
                    else:
                        z = pieces[mainparts.get(x)]

                    tmpresult[y] = z

            # check for follow lines
            elif correct_line and ctype in follow_lines:

                for x in followparts.keys():

                    # skip the type
                    if x == 'type':
                        continue

                    # add the other info to the tmpresult array

                    y = '{}_{}_{}_{}'.format(usage, current_line, ctype, x)
                    if x == 'pubkey_algorithm':
                        p = pieces[followparts.get(x)]
                        z = pubkeys.get(p) if p is not None else ''
                    else:
                        z = pieces[followparts.get(x)]

                    tmpresult[y] = z

            # if not, we have reached a new key or the end
            else:
                #print("we are no longer on the correct line")
                correct_line = False

        msg.vvvv("renaming tmpresult keys; usage [{}]".format(usage))

        #
        # MAPPING
        #ssb_fpr_userid  sec_fpr_userid master_cert_sec_fingerprint
        keymap = {
            'regular': {
                'cert_sec_fpr_userid': 'master_cert_sec_fingerprint',
                'cert_sec_curve_name': 'master_cert_sec_keycurve',
                'cert_sec_grp_userid': 'master_cert_sec_keygrip',
                'cert_sec_key_length': 'master_cert_sec_keybits',
                'cert_sec_creationdate': 'master_cert_sec_creationdate',
                'cert_sec_keyid': 'master_cert_sec_keyid',
                'cert_sec_expirationdate': 'master_cert_sec_expirationdate',
                'sign_ssb_fpr_userid': 'subkey_sign_sec_fingerprint',
                'sign_ssb_curve_name': 'subkey_sign_sec_keycurve',
                'sign_ssb_grp_userid': 'subkey_sign_sec_keygrip',
                'sign_ssb_key_length': 'subkey_sign_sec_keybits',
                'sign_ssb_creationdate': 'subkey_sign_sec_creationdate',
                'sign_ssb_keyid': 'subkey_sign_sec_keyid',
                'sign_ssb_expirationdate': 'subkey_sign_sec_expirationdate',
                'encr_ssb_fpr_userid': 'subkey_encr_sec_fingerprint',
                'encr_ssb_curve_name': 'subkey_encr_sec_keycurve',
                'encr_ssb_grp_userid': 'subkey_encr_sec_keygrip',
                'encr_ssb_key_length': 'subkey_encr_sec_keybits',
                'encr_ssb_creationdate': 'subkey_encr_sec_creationdate',
                'encr_ssb_keyid': 'subkey_encr_sec_keyid',
                'encr_ssb_expirationdate': 'subkey_encr_sec_expirationdate',
                'auth_ssb_fpr_userid': 'subkey_auth_sec_fingerprint',
                'auth_ssb_curve_name': 'subkey_auth_sec_keycurve',
                'auth_ssb_grp_userid': 'subkey_auth_sec_keygrip',
                'auth_ssb_key_length': 'subkey_auth_sec_keybits',
                'auth_ssb_creationdate': 'subkey_auth_sec_creationdate',
                'auth_ssb_keyid': 'subkey_auth_sec_keyid',
                'auth_ssb_expirationdate': 'subkey_auth_sec_expirationdate',
            },
            'backup_sign': {
                'cert_sec_fpr_userid': 'sign_master_cert_sec_fingerprint',
                'cert_sec_curve_name': 'sign_master_cert_sec_keycurve',
                'cert_sec_grp_userid': 'sign_master_cert_sec_keygrip',
                'cert_sec_key_length': 'sign_master_cert_sec_keybits',
                'cert_sec_creationdate': 'sign_master_cert_sec_creationdate',
                'cert_sec_keyid': 'sign_master_cert_sec_keyid',
                'cert_sec_expirationdate':
                'sign_master_cert_sec_expirationdate',
                'sign_ssb_fpr_userid': 'sign_subkey_sign_sec_fingerprint',
                'sign_ssb_curve_name': 'sign_subkey_sign_sec_keycurve',
                'sign_ssb_grp_userid': 'sign_subkey_sign_sec_keygrip',
                'sign_ssb_key_length': 'sign_subkey_sign_sec_keybits',
                'sign_ssb_creationdate': 'sign_subkey_sign_sec_creationdate',
                'sign_ssb_keyid': 'sign_subkey_sign_sec_keyid',
                'sign_ssb_expirationdate':
                'sign_subkey_sign_sec_expirationdate',
            },
            'backup_encr': {
                'cert_sec_fpr_userid': 'encr_master_cert_sec_fingerprint',
                'cert_sec_curve_name': 'encr_master_cert_sec_keycurve',
                'cert_sec_grp_userid': 'encr_master_cert_sec_keygrip',
                'cert_sec_key_length': 'encr_master_cert_sec_keybits',
                'cert_sec_creationdate': 'encr_master_cert_sec_creationdate',
                'cert_sec_keyid': 'encr_master_cert_sec_keyid',
                'cert_sec_expirationdate':
                'encr_master_cert_sec_expirationdate',
                'encr_ssb_fpr_userid': 'encr_subkey_encr_sec_fingerprint',
                'encr_ssb_curve_name': 'encr_subkey_encr_sec_keycurve',
                'encr_ssb_grp_userid': 'encr_subkey_encr_sec_keygrip',
                'encr_ssb_key_length': 'encr_subkey_encr_sec_keybits',
                'encr_ssb_creationdate': 'encr_subkey_encr_sec_creationdate',
                'encr_ssb_keyid': 'encr_subkey_encr_sec_keyid',
                'encr_ssb_expirationdate':
                'encr_subkey_encr_sec_expirationdate',
            },
        }

        #
        # rename result keys / only use the relevant ones from the mapping
        #
        result = {}
        resultiterator = tmpresult.copy()
        km = keymap.get(mapping)
        for k, v in resultiterator.items():

            # only for keys which exist in the mapping dict
            if k in km:
                nk = km.get(k)
                result[nk] = tmpresult.pop(k)

        #
        # return results
        #
        return result