# Copyright 2014 Baidu, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file # except in compliance with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software distributed under the # License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, # either express or implied. See the License for the specific language governing permissions # and limitations under the License. """ This module provides a client class for KMS. """ import copy import json import logging import random import string import uuid from baidubce.bce_base_client import BceBaseClient from baidubce.utils import required from baidubce.auth import bce_v1_signer from baidubce.http import bce_http_client from baidubce.http import handler from baidubce.http import http_methods import base64 _logger = logging.getLogger(__name__) class KmsClient(BceBaseClient): """ sdk client """ def __init__(self, config=None): BceBaseClient.__init__(self, config) def _merge_config(self, config=None): if config is None: return self.config else: new_config = copy.copy(self.config) new_config.merge_non_none_values(config) return new_config def _send_request(self, http_method, path, body=None, headers=None, params=None, config=None, body_parser=None): config = self._merge_config(config) if body_parser is None: body_parser = handler.parse_json if headers is None: headers = {b'Accept': b'*/*', b'Content-Type': b'application/json;charset=utf-8'} return bce_http_client.send_request(config, bce_v1_signer.sign, [handler.parse_error, body_parser], http_method, path, body, headers, params) @required(protectedBy=(bytes, str), keySpec=(bytes, str), origin=(bytes, str), rotateCycle=(int)) def create_masterKey(self, description, protectedBy, keySpec, origin, keyUsage="ENCRYPT_DECRYPT", rotateCycle=0, config=None): """ create a master key with the specified options. :type description: string :param description: a description about the master key :type protectedBy: constants.ProtectedBy :param protectedBy: the protect level about the master key, you can choose HSM or SOFTWARE :type keySpec: constants.KeySpec :param keySpec: key specification about the master key. now you can choose the BAIDU_AES_256, AES_128, AES_256, RSA_1024, RSA_2048, RSA_4096 :type keyUsage: string :param keyUsage: default "ENCRYPT_DECRYPT" :type origin: constants.Origin :param origin: origin of the master key. you can choose BAIDU_KMS or EXTERNAL :type rotateCycle: int :param rotateCycle: rotateCycle of the master key. """ path = b'/' params = {} params['action'] = b'CreateKey' body={} if description: body['description'] = description body['protectedBy'] = protectedBy body['keySpec'] = keySpec body['origin'] = origin body['keyUsage'] = keyUsage body['rotateCycle'] = rotateCycle return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(limit=int) def list_masterKey(self, limit, marker="", config=None): """ list your masterkey :type limit: int :param limit: the number of masterKey you want list :type marker: string :param marker: the marker keyid , kms will search from the marker, default "" """ path = b'/' params = {} params['action'] = b'ListKeys' body={} body['limit'] = limit body['marker'] = marker return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(keyId=(str, bytes), plaintext=(str, bytes)) def encrypt(self, keyId, plaintext, config=None): """ encrypt the plaintext :type keyId: string :param keyId: indicate kms will use which masterkey to encrypt :type plaintext: string :param plaintext: the plaintext need encrypted by kms """ path = b'/' params = {} params['action'] = b'Encrypt' body={} body['keyId'] = keyId body['plaintext'] = plaintext try: base64.b64decode(plaintext) except TypeError: raise TypeError("please input base64 string") return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(keyId=(str, bytes), ciphertext=(str, bytes)) def decrypt(self, keyId, ciphertext, config=None): """ decrypt the ciphertext :type keyId: string :param keyId: indicate kms will use which masterkey to decrypt :type ciphertext: string :param ciphertext: the ciphertext need decrypted by kms """ path = b'/' params = {} params['action'] = b'Decrypt' body={} body['keyId'] = keyId body['ciphertext'] = ciphertext try: base64.b64decode(ciphertext) except TypeError: raise TypeError("please input base64 string") return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(keyId=(str, bytes), keySpec=(str, bytes)) def generate_dataKey(self, keyId, keySpec, numberOfBytes=-1, config=None): """ generate a data key by master key :type keyId: string :param keyId: indicate kms will use which masterkey to generate data key :type keySpec: string :param keySpec: AES_128 or AES_256 :type numberOfBytes: int :param numberOfBytes: The length of data key """ path = b'/' params = {} params['action'] = b'GenerateDataKey' body={} body['keyId'] = keyId if keySpec != "AES_128" and keySpec != "AES_256": raise ValueError("only support AES_128 and AES_256") body['keySpec'] = keySpec body['numberOfBytes'] = numberOfBytes return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(keyId=(str, bytes), rotateCycle=(int)) def updaterotation_masterKey(self, keyId, rotateCycle, config=None): """ update your master key rptation :type keyId: string :type rotateCycle: int :param keyId: the keyId of masterkey will be enable :param rotateCycle: the rotatecycle of masterkey """ path = b'/' params = {} params['action'] = b'EnableRotation' body={} body['keyId'] = keyId body['rotateCycle'] = rotateCycle return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(keyId=(str, bytes)) def enable_masterKey(self, keyId, config=None): """ enable your master key :type keyId: string :param keyId: the keyId of masterkey will be enable """ path = b'/' params = {} params['action'] = b'EnableKey' body={} body['keyId'] = keyId return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(keyId=(str, bytes)) def disable_masterKey(self, keyId, config=None): """ disable your master key :type keyId: string :param keyId: the keyId of masterkey will be diable """ path = b'/' params = {} params['action'] = b'DisableKey' body={} body['keyId'] = keyId return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(keyId=(str, bytes), pendingWindowInDays=int) def scheduleDelete_masterKey(self, keyId, pendingWindowInDays, config=None): """ schedule delete master key :type keyId: string :param keyId: the keyId of masterkey will be deleted :type pendingWindowInDays: int :pram pendingWindowInDays: kms will wait pendingWindowInDays day then delete the key """ path = b'/' params = {} params['action'] = b'ScheduleKeyDeletion' body={} body['keyId'] = keyId if pendingWindowInDays > 30 or pendingWindowInDays < 7: raise ValueError("please input pendingWindowInDays >=7 and <=30") body['pendingWindowInDays'] = pendingWindowInDays return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(keyId=(str, bytes)) def cancelDelete_masterKey(self, keyId, config=None): """ cancel delete master key :type keyId: string :param keyId: the keyId of masterkey will cancel delete """ path = b'/' params = {} params['action'] = b'CancelKeyDeletion' body={} body['keyId'] = keyId return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(keyId=(str, bytes)) def describe_masterKey(self, keyId, config=None): """ descript the master key :type keyId: string :param keyId: the keyId of masterkey """ path = b'/' params = {} params['action'] = b'DescribeKey' body={} body['keyId'] = keyId return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(keyId=(str, bytes)) def get_parameters_for_import(self, keyId, publicKeyEncoding, wrappingAlgorithm="RSAES_PKCS1_V1_5", wrappingKeySpec="RSA_2048", config=None): """ get parameters for import :type keyId: string :param keyId: the keyId of masterkey :type wrappingAlgorithm: string :param wrappingAlgorithm: the algorithm for user encrypt local key :type wrappingKeySpec:string :param wrappingKeySpec: the pubkey spec for user encrypt local key """ path = b'/' params = {} params['action'] = b'GetParametersForImport' body={} body['keyId'] = keyId if wrappingAlgorithm != "RSAES_PKCS1_V1_5": raise TypeError("only support RSAES_PKCS1_V1_5") body['wrappingAlgorithm'] = wrappingAlgorithm if wrappingKeySpec != "RSA_2048": raise TypeError("only support RSA_2048") body['wrappingKeySpec'] = wrappingKeySpec if publicKeyEncoding != "RAW_HEX" and publicKeyEncoding != "BASE64" and publicKeyEncoding != "PEM": raise ValueError("only support RAW_HEX or BASE64 or PEM") body['publicKeyEncoding'] = publicKeyEncoding return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(keyId=(str, bytes), importToken=(str, bytes), encryptedKey=(str, bytes), keySpec=(str, bytes)) def import_symmetricMasterKey(self, keyId, importToken, encryptedKey, keySpec, keyUsage="ENCRYPT_DECRYPT", config=None): """ import symmetric key :type keyId: string :param keyId: the keyId of masterkey :type importToken: string :param importToken: token from import parameter :type encryptedKey: string :param encryptedKey: the symmetric key encrypted by pubkey :type keySpec: string :param keySpec: the import key spec :type keyUsage: string :param keyUsage: default "ENCRYPT_DECRYPT" """ path = b'/' params = {} params['action'] = b'ImportKey' body={} body['keyId'] = keyId body['importToken'] = importToken body['encryptedKey'] = encryptedKey body['keySpec'] = keySpec body['keyUsage'] = keyUsage return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(keyId=(str, bytes), importToken=(str, bytes), asymmetricKeySpec=(str, bytes), asymmetricKeyUsage=(str, bytes), encryptedKeyEncryptionKey=(str, bytes), asymmetricKey=object) def import_asymmetricMasterKey(self, keyId, importToken, asymmetricKeySpec, encryptedKeyEncryptionKey, asymmetricKeyUsage="ENCRYPT_DECRYPT", config=None, **kwargs): """ import asymmetric key :type keyId: string :param keyId: the keyId of masterkey :type importToken: string :param importToken: token from import parameter :type asymmetricKeySpec: string :param asymmetricKeySpec: the import key spec :type encryptedKeyEncryptionKey: string :param encryptedKeyEncryptionKey: EncryptionKey :type asymmetricKey: **args :param asymmetricKey: include publicKeyDer encryptedD encryptedP encryptedQ encryptedDp encryptedDq encryptedQinv """ path = b'/' params = {} params['action'] = b'ImportAsymmetricKey' body={} body['keyId'] = keyId body['importToken'] = importToken body['asymmetricKeySpec'] = asymmetricKeySpec body['asymmetricKeyUsage'] = asymmetricKeyUsage body['encryptedKeyEncryptionKey'] = encryptedKeyEncryptionKey body['encryptedRsaKey'] = {} if kwargs['publicKeyDer'] is None: raise ValueError('arg "publicKeyDer" should not be None') body['encryptedRsaKey']['publicKeyDer'] = kwargs['publicKeyDer'] if kwargs['encryptedD'] is None: raise ValueError('arg "encryptedD" should not be None') body['encryptedRsaKey']['encryptedD'] = kwargs['encryptedD'] if kwargs['encryptedP'] is None: raise ValueError('arg "encryptedP" should not be None') body['encryptedRsaKey']['encryptedP'] = kwargs['encryptedP'] if kwargs['encryptedQ'] is None: raise ValueError('arg "encryptedQ" should not be None') body['encryptedRsaKey']['encryptedQ'] = kwargs['encryptedQ'] if kwargs['encryptedDp'] is None: raise ValueError('arg "encryptedDp" should not be None') body['encryptedRsaKey']['encryptedDp'] = kwargs['encryptedDp'] if kwargs['encryptedDq'] is None: raise ValueError('arg "encryptedDq" should not be None') body['encryptedRsaKey']['encryptedDq'] = kwargs['encryptedDq'] if kwargs['encryptedQinv'] is None: raise ValueError('arg "encryptedQinv" should not be None') body['encryptedRsaKey']['encryptedQinv'] = kwargs['encryptedQinv'] return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config) @required(keyId=(str, bytes), importToken=(str, bytes), asymmetricKeySpec=(str, bytes), asymmetricKeyUsage=(str, bytes), encryptedKeyEncryptionKey=(str, bytes), asymmetricKey=object) def import_asymmetricSM2MasterKey(self, keyId, importToken, asymmetricKeySpec, encryptedKeyEncryptionKey, asymmetricKeyUsage="ENCRYPT_DECRYPT", config=None, **kwargs): """ import asymmetric key :type keyId: string :param keyId: the keyId of masterkey :type importToken: string :param importToken: token from import parameter :type asymmetricKeySpec: string :param asymmetricKeySpec: the import key spec :type encryptedKeyEncryptionKey: string :param encryptedKeyEncryptionKey: EncryptionKey :type asymmetricKey: **args :param asymmetricKey: include publicKeyDer encryptedPrivateKey """ path = b'/' params = {} params['action'] = b'ImportAsymmetricKey' body={} body['keyId'] = keyId body['importToken'] = importToken body['asymmetricKeySpec'] = asymmetricKeySpec body['asymmetricKeyUsage'] = asymmetricKeyUsage body['encryptedKeyEncryptionKey'] = encryptedKeyEncryptionKey body['encryptedSm2Key'] = {} if kwargs['publicKeyDer'] is None: raise ValueError('arg "publicKeyDer" should not be None') body['encryptedSm2Key']['publicKeyDer'] = kwargs['publicKeyDer'] if kwargs['encryptedPrivateKey'] is None: raise ValueError('arg "encryptedPrivateKey" should not be None') body['encryptedSm2Key']['encryptedPrivateKey'] = kwargs['encryptedPrivateKey'] return self._send_request(http_methods.POST, path, json.dumps(body), params=params, config=config)