def import_key(client, vault_base_url, key_name, destination=None, key_ops=None, disabled=False, expires=None, not_before=None, tags=None, pem_file=None, pem_password=None, byok_file=None): """ Import a private key. Supports importing base64 encoded private keys from PEM files. Supports importing BYOK keys into HSM for premium KeyVaults. """ from azure.keyvault.generated.models import \ (KeyAttributes, JsonWebKey) def _to_bytes(hex_string): # zero pads and decodes a hex string if len(hex_string) % 2: hex_string = '0{}'.format(hex_string) return codecs.decode(hex_string, 'hex_codec') def _set_rsa_parameters(dest, src): # map OpenSSL parameter names to JsonWebKey property names conversion_dict = { 'modulus': 'n', 'publicExponent': 'e', 'privateExponent': 'd', 'prime1': 'p', 'prime2': 'q', 'exponent1': 'dp', 'exponent2': 'dq', 'coefficient': 'qi' } # regex: looks for matches that fit the following patterns: # integerPattern: 65537 (0x10001) # hexPattern: # 00:a0:91:4d:00:23:4a:c6:83:b2:1b:4c:15:d5:be: # d8:87:bd:c9:59:c2:e5:7a:f5:4a:e7:34:e8:f0:07: # The desired match should always be the first component of the match regex = re.compile( r'([^:\s]*(:[^\:)]+\))|([^:\s]*(:\s*[0-9A-Fa-f]{2})+))') # regex2: extracts the hex string from a format like: 65537 (0x10001) regex2 = re.compile(r'(?<=\(0x{1})([0-9A-Fa-f]*)(?=\))') key_params = crypto.dump_privatekey(crypto.FILETYPE_TEXT, src).decode('utf-8') for match in regex.findall(key_params): comps = match[0].split(':', 1) name = conversion_dict.get(comps[0], None) if name: value = comps[1].replace(' ', '').replace('\n', '').replace(':', '') try: value = _to_bytes(value) except Exception as ex: # pylint:disable=broad-except # if decoding fails it is because of an integer pattern. Extract the hex # string and retry value = _to_bytes(regex2.findall(value)[0]) setattr(dest, name, value) key_attrs = KeyAttributes(not disabled, not_before, expires) key_obj = JsonWebKey(key_ops=key_ops) if pem_file: key_obj.kty = 'RSA' logger.info('Reading %s', pem_file) with open(pem_file, 'r') as f: pem_data = f.read() # load private key and prompt for password if encrypted try: pem_password = str(pem_password).encode() if pem_password else None # despite documentation saying password should be a string, it needs to actually # be UTF-8 encoded bytes pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, pem_data, pem_password) except crypto.Error as ex: raise CLIError( 'Import failed: Unable to decrypt private key. --pem-password may be incorrect.' ) except TypeError as ex: raise CLIError('Invalid --pem-password.') logger.info('setting RSA parameters from PEM data') _set_rsa_parameters(key_obj, pkey) elif byok_file: with open(byok_file, 'rb') as f: byok_data = f.read() key_obj.kty = 'RSA-HSM' key_obj.t = byok_data return client.import_key(vault_base_url, key_name, key_obj, destination == 'hsm', key_attrs, tags)
def import_key(client, vault_base_url, key_name, destination=None, key_ops=None, disabled=False, expires=None, not_before=None, tags=None, pem_file=None, pem_password=None, byok_file=None): """ Import a private key. Supports importing base64 encoded private keys from PEM files. Supports importing BYOK keys into HSM for premium KeyVaults. """ from azure.keyvault.generated.models import \ (KeyAttributes, JsonWebKey) def _to_bytes(hex_string): # zero pads and decodes a hex string if len(hex_string) % 2: hex_string = '0{}'.format(hex_string) return codecs.decode(hex_string, 'hex_codec') def _set_rsa_parameters(dest, src): # map OpenSSL parameter names to JsonWebKey property names conversion_dict = { 'modulus': 'n', 'publicExponent': 'e', 'privateExponent': 'd', 'prime1': 'p', 'prime2': 'q', 'exponent1': 'dp', 'exponent2': 'dq', 'coefficient': 'qi' } # regex: looks for matches that fit the following patterns: # integerPattern: 65537 (0x10001) # hexPattern: # 00:a0:91:4d:00:23:4a:c6:83:b2:1b:4c:15:d5:be: # d8:87:bd:c9:59:c2:e5:7a:f5:4a:e7:34:e8:f0:07: # The desired match should always be the first component of the match regex = re.compile(r'([^:\s]*(:[^\:)]+\))|([^:\s]*(:\s*[0-9A-Fa-f]{2})+))') # regex2: extracts the hex string from a format like: 65537 (0x10001) regex2 = re.compile(r'(?<=\(0x{1})([0-9A-Fa-f]*)(?=\))') key_params = crypto.dump_privatekey(crypto.FILETYPE_TEXT, src).decode('utf-8') for match in regex.findall(key_params): comps = match[0].split(':', 1) name = conversion_dict.get(comps[0], None) if name: value = comps[1].replace(' ', '').replace('\n', '').replace(':', '') try: value = _to_bytes(value) except Exception as ex: # pylint:disable=broad-except # if decoding fails it is because of an integer pattern. Extract the hex # string and retry value = _to_bytes(regex2.findall(value)[0]) setattr(dest, name, value) key_attrs = KeyAttributes(not disabled, not_before, expires) key_obj = JsonWebKey(key_ops=key_ops) if pem_file: key_obj.kty = 'RSA' logger.info('Reading %s', pem_file) with open(pem_file, 'r') as f: pem_data = f.read() # load private key and prompt for password if encrypted try: pem_password = str(pem_password).encode() if pem_password else None # despite documentation saying password should be a string, it needs to actually # be UTF-8 encoded bytes pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, pem_data, pem_password) except crypto.Error as ex: raise CLIError( 'Import failed: Unable to decrypt private key. --pem-password may be incorrect.') except TypeError as ex: raise CLIError('Invalid --pem-password.') logger.info('setting RSA parameters from PEM data') _set_rsa_parameters(key_obj, pkey) elif byok_file: with open(byok_file, 'rb') as f: byok_data = f.read() key_obj.kty = 'RSA-HSM' key_obj.t = byok_data return client.import_key( vault_base_url, key_name, key_obj, destination == 'hsm', key_attrs, tags)