459 lines
17 KiB
Python

# 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)