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
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
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
def gen_default(self): # set filenames file_key = self.filenames["private"].format(self.tmpfile) file_pub = self.filenames["public"].format(self.tmpfile) # merge arguments and command # bits are ignored for ed25519 keys cmd = [self.opts.get('sshkey_bin_keygen')] args = [ "-t{0}".format(self.opts.get('sshkey_type')), "-b{0}".format(self.opts.get('sshkey_bits')), "-C{0}".format(self.opts.get('sshkey_comment')), "-f{0}".format(file_key), "-N{0}".format(self.passphrase) ] cmd += args # run subprocess proc = Process("ssh-keygen", cmd).run() # read file contents result = {} result['private'] = hlp.get_file_contents(file_key) result['public'] = hlp.get_file_contents(file_pub) # generate fingerprints / bubble babble result = self.opts.merge(result, self.gen_fingerprints("md5", file_pub)) result = self.opts.merge(result, self.gen_fingerprints("sha256", file_pub)) result = self.opts.merge(result, self.gen_bubblebabble(file_pub)) # return result return result
def export_keys(self, ltype, fpr, pwdfile): # set keyfiles ascfile = "{}{}".format(self.opts.get_tmp_filename(), '.asc') # set export type if ltype == 'sec': exp = '-secret-keys' elif ltype == 'ssb': exp = '-secret-subkeys' else: exp = '' # set base command cmd = [self.opts.get('gpgkey_bin')] args = [ '--homedir={}'.format(self.opts.get_tmp_dir()), '--passphrase-file={}'.format(pwdfile), '--pinentry-mode=loopback', '--quiet', '--armor', '--export{}'.format(exp), '{}'.format(fpr), #'--output={}'.format(ascfile) - output to stdout instead ] cmd += args # output unencrypted file proc = Process("gpg", cmd, failonstderr=False).run() # return stdout as it contains the armored key return proc.stdout
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
def init_gnupg_dir(self): # simply running the list command will make gnupg create the trustdb etc cmd = [self.opts.get('gpgkey_bin')] args = self.get_publist_args() cmd += args # run subprocess; ignore any output, even though gpg sends the init messages to stderr proc = Process("gpg", cmd, failonstderr=False).run()
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
def generate_key(self, usage, pwdfile, fpr=None, uidprefix=None): # create the command to generate a new master key cmd = [self.opts.get('gpgkey_bin')] args = self.get_generate_args(usage, pwdfile, fpr, uidprefix) cmd += args # 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() return True
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
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
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
def sign_key(self, pwdfile, sign_with_key, fpr_to_sign): # create the command to sign the key cmd = [self.opts.get('gpgkey_bin')] args = self.get_signkey_args(pwdfile=pwdfile, sign_with_key=sign_with_key, fpr_to_sign=fpr_to_sign) cmd += args # 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() return True
def gen_openssh(self): # set filenames file_key = self.filenames["private"].format(self.tmpfile) # merge arguments and command cmd = [ self.opts.get('sshkey_bin_keygen'), "-f{}".format(file_key), "-p", "-P{}".format(self.passphrase), "-N{}".format(self.passphrase), "-o", "-a", "100" ] proc = Process("ssh-keygen", cmd).run() # set result result = {} result['private_openssh'] = hlp.get_file_contents(file_key) # return result return result
def gen_sshcom(self): # set filenames file_key = self.filenames["private"].format(self.tmpfile) file_sshcom = self.filenames["private_sshcom"].format(self.tmpfile) # merge arguments and command cmd = [ self.opts.get('sshkey_bin_puttygen'), "{}".format(file_key), "-O", "private-sshcom", "--old-passphrase", "{}".format(self.pwdfile), "--new-passphrase", "{}".format(self.pwdfile), "-o", "{}".format(file_sshcom), "-C", "{}".format(self.opts.get('sshkey_comment')) ] proc = Process("puttygen", cmd).run() # set result result = {} result['private_sshcom'] = hlp.get_file_contents(file_sshcom) # return result return result
def generate(self): # options shorthand o = self.opts.getall() msg.display("generating new SSH host keys; this may take a while") # # SUPPORTED KEYTYPES # +=============================================+ # | DESC : PRI | PUB | FIP | DNS | # | X | # +=============================================+ # | rsa : 1 | 1 | 6 | | 8 | A | # | dsa : 1 | 1 | 6 | | 8 | B | - not supported # | ecdsa : 1 | 1 | 6 | | 8 | C | - not supported # | ed25519 : 1 | 1 | 6 | | 8 | D | # +=============================================+ # TOTAL 32 # # set a bunch of filenames for all different private/public keytypes # set filenames file_key = self.filenames['private'].format(self.tmpfile) file_pub = self.filenames['public'].format(self.tmpfile) # set bits for key strength sw_rsa = { 'medium': 2048, 'strong': 4096, } sw_ed25519 = { 'medium': 256, 'strong': 256, } # set shorthand for keytype and strength t = o['sshhostkey_type'] s = o['sshhostkey_strength'] # get proper bits for selected keytype and strength if t == "rsa": b = sw_rsa.get(s) elif t == "ed25519": b = sw_ed25519.get(s) # merge arguments and command cmd = "{} -b {} -t {} -f {} -N '' -C '{}'".format( o['sshhostkey_bin_keygen'], b, t, file_key, o['sshhostkey_comment']) proc = Process("ssh-keygen", cmd, shell=True).run() # set result keys result = {} result['private'] = hlp.get_file_contents(file_key) result['public'] = hlp.get_file_contents(file_pub) # generate fingerprints result = self.opts.merge(result, self.gen_fingerprints("md5", file_pub)) result = self.opts.merge(result, self.gen_fingerprints("sha256", file_pub)) result = self.opts.merge(result, self.gen_bubblebabble(file_pub)) # generate and merge dns records # don't use the self.hostname variable for DNS, but the value given by the user result = self.opts.merge(result, self.gen_dns(file_pub)) # add keybits; keytype is added to keyname itself in next step result['private_keybits'] = str(b) # rename keys #resultiterator = result.copy() #for k,v in resultiterator.items(): # nk = "{}_{}".format(self.opts.get('sshhostkey_type'), k) # result[nk] = result.pop(k) # return return result
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
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