kms_client.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. # Copyright 2014 Baidu, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  4. # except in compliance with the License. You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software distributed under the
  9. # License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  10. # either express or implied. See the License for the specific language governing permissions
  11. # and limitations under the License.
  12. """
  13. This module provides a client class for KMS.
  14. """
  15. import copy
  16. import json
  17. import logging
  18. import random
  19. import string
  20. import uuid
  21. import binascii
  22. from baidubce.bce_base_client import BceBaseClient
  23. from baidubce.utils import required
  24. from baidubce.auth import bce_v1_signer
  25. from baidubce.http import bce_http_client
  26. from baidubce.http import handler
  27. from baidubce.http import http_methods
  28. import base64
  29. _logger = logging.getLogger(__name__)
  30. class KmsClient(BceBaseClient):
  31. """
  32. sdk client
  33. """
  34. def __init__(self, config=None):
  35. BceBaseClient.__init__(self, config)
  36. def _merge_config(self, config=None):
  37. if config is None:
  38. return self.config
  39. else:
  40. new_config = copy.copy(self.config)
  41. new_config.merge_non_none_values(config)
  42. return new_config
  43. def _send_request(self, http_method, path,
  44. body=None, headers=None, params=None,
  45. config=None, body_parser=None):
  46. config = self._merge_config(config)
  47. if body_parser is None:
  48. body_parser = handler.parse_json
  49. if headers is None:
  50. headers = {b'Accept': b'*/*',
  51. b'Content-Type': b'application/json;charset=utf-8'}
  52. return bce_http_client.send_request(config, bce_v1_signer.sign,
  53. [handler.parse_error, body_parser],
  54. http_method, path, body, headers,
  55. params)
  56. @required(protectedBy=(bytes, str), keySpec=(bytes, str), origin=(bytes, str), rotateCycle=(int))
  57. def create_masterKey(self, description, protectedBy, keySpec,
  58. origin, keyUsage="ENCRYPT_DECRYPT", rotateCycle=0, config=None):
  59. """
  60. create a master key with the specified options.
  61. :type description: string
  62. :param description: a description about the master key
  63. :type protectedBy: constants.ProtectedBy
  64. :param protectedBy: the protect level about the master key, you can choose HSM or SOFTWARE
  65. :type keySpec: constants.KeySpec
  66. :param keySpec: key specification about the master key. now you can choose the BAIDU_AES_256,
  67. AES_128, AES_256, RSA_1024, RSA_2048, RSA_4096
  68. :type keyUsage: string
  69. :param keyUsage: default "ENCRYPT_DECRYPT"
  70. :type origin: constants.Origin
  71. :param origin: origin of the master key. you can choose BAIDU_KMS or EXTERNAL
  72. :type rotateCycle: int
  73. :param rotateCycle: rotateCycle of the master key.
  74. """
  75. path = b'/'
  76. params = {}
  77. params['action'] = b'CreateKey'
  78. body={}
  79. if description:
  80. body['description'] = description
  81. body['protectedBy'] = protectedBy
  82. body['keySpec'] = keySpec
  83. body['origin'] = origin
  84. body['keyUsage'] = keyUsage
  85. body['rotateCycle'] = rotateCycle
  86. return self._send_request(http_methods.POST, path, json.dumps(body),
  87. params=params, config=config)
  88. @required(limit=int)
  89. def list_masterKey(self, limit, marker="", config=None):
  90. """
  91. list your masterkey
  92. :type limit: int
  93. :param limit: the number of masterKey you want list
  94. :type marker: string
  95. :param marker: the marker keyid , kms will search from the marker, default ""
  96. """
  97. path = b'/'
  98. params = {}
  99. params['action'] = b'ListKeys'
  100. body={}
  101. body['limit'] = limit
  102. body['marker'] = marker
  103. return self._send_request(http_methods.POST, path, json.dumps(body),
  104. params=params, config=config)
  105. @required(keyId=(str, bytes), plaintext=(str, bytes))
  106. def encrypt(self, keyId, plaintext, config=None):
  107. """
  108. encrypt the plaintext
  109. :type keyId: string
  110. :param keyId: indicate kms will use which masterkey to encrypt
  111. :type plaintext: string
  112. :param plaintext: the plaintext need encrypted by kms
  113. """
  114. path = b'/'
  115. params = {}
  116. params['action'] = b'Encrypt'
  117. body={}
  118. body['keyId'] = keyId
  119. body['plaintext'] = plaintext
  120. try:
  121. base64.b64decode(plaintext)
  122. except TypeError:
  123. raise TypeError("please input base64 string")
  124. return self._send_request(http_methods.POST, path, json.dumps(body),
  125. params=params, config=config)
  126. @required(keyId=(str, bytes), ciphertext=(str, bytes))
  127. def decrypt(self, keyId, ciphertext, config=None):
  128. """
  129. decrypt the ciphertext
  130. :type keyId: string
  131. :param keyId: indicate kms will use which masterkey to decrypt
  132. :type ciphertext: string
  133. :param ciphertext: the ciphertext need decrypted by kms
  134. """
  135. path = b'/'
  136. params = {}
  137. params['action'] = b'Decrypt'
  138. body={}
  139. body['keyId'] = keyId
  140. body['ciphertext'] = ciphertext
  141. try:
  142. base64.b64decode(ciphertext)
  143. except TypeError:
  144. raise TypeError("please input base64 string")
  145. return self._send_request(http_methods.POST, path, json.dumps(body),
  146. params=params, config=config)
  147. @required(keyId=(str, bytes), algorithm=(str, bytes), message=(str, bytes))
  148. def sign(self, keyId, algorithm, message, keyVersion=None, messageType='RAW', config=None):
  149. """
  150. Sign the message using asymmetric key
  151. :type keyId: string
  152. :param keyId: indicate which masterkey to use for signing
  153. :type algorithm: string
  154. :param algorithm: signing algorithm (RSA_PKCS1_SHA_256/SM2DSA)
  155. :type message: string
  156. :param message: message to sign (Base64 encoded)
  157. :type keyVersion: string
  158. :param keyVersion: key version (optional)
  159. :type messageType: string
  160. :param messageType: message type (RAW/DIGEST, default RAW)
  161. :raises: ValueError if message length or format is invalid
  162. """
  163. if messageType not in ('RAW', 'DIGEST'):
  164. raise ValueError("messageType must be either 'RAW' or 'DIGEST'")
  165. # Validate message length based on type
  166. if messageType == 'RAW':
  167. if len(message) > 4096:
  168. raise ValueError("Base64 encoded RAW message length must be <= 4096 bytes")
  169. elif messageType == 'DIGEST':
  170. try:
  171. decoded = base64.b64decode(message)
  172. if len(decoded) != 32:
  173. raise ValueError("Digest length must be 32 bytes after base64 decoding")
  174. except (binascii.Error, TypeError):
  175. raise ValueError("message must be base64 encoded")
  176. path = b'/'
  177. params = {}
  178. params['action'] = b'Sign'
  179. body = {
  180. 'keyId': keyId,
  181. 'algorithm': algorithm,
  182. 'message': message,
  183. 'messageType': messageType
  184. }
  185. if keyVersion:
  186. body['keyVersion'] = keyVersion
  187. return self._send_request(http_methods.POST, path, json.dumps(body),
  188. params=params, config=config)
  189. @required(keyId=(str, bytes), algorithm=(str, bytes), signature=(str, bytes), message=(str, bytes))
  190. def verify(self, keyId, algorithm, signature, message, keyVersion=None, messageType='RAW', config=None):
  191. """
  192. Verify the signature using asymmetric key
  193. :type keyId: string
  194. :param keyId: indicate which masterkey to use for verification
  195. :type algorithm: string
  196. :param algorithm: signing algorithm (RSA_PKCS1_SHA_256/SM2DSA)
  197. :type signature: string
  198. :param signature: signature to verify (Base64 encoded)
  199. :type message: string
  200. :param message: original message (Base64 encoded)
  201. :type keyVersion: string
  202. :param keyVersion: key version (optional)
  203. :type messageType: string
  204. :param messageType: message type (RAW/DIGEST, default RAW)
  205. """
  206. # Validate message type
  207. if messageType not in ('RAW', 'DIGEST'):
  208. raise ValueError("messageType must be either 'RAW' or 'DIGEST'")
  209. # Validate message length based on type
  210. if messageType == 'RAW':
  211. if len(message) > 4096:
  212. raise ValueError("Base64 encoded RAW message length must be <= 4096 bytes")
  213. elif messageType == 'DIGEST':
  214. try:
  215. decoded = base64.b64decode(message)
  216. if len(decoded) != 32:
  217. raise ValueError("Digest length must be 32 bytes after base64 decoding")
  218. except (binascii.Error, TypeError):
  219. raise ValueError("message must be base64 encoded")
  220. # Validate signature length
  221. try:
  222. base64.b64decode(signature)
  223. except (binascii.Error, TypeError):
  224. raise ValueError("signature must be base64 encoded")
  225. path = b'/'
  226. params = {}
  227. params['action'] = b'Verify'
  228. body = {
  229. 'keyId': keyId,
  230. 'algorithm': algorithm,
  231. 'signature': signature,
  232. 'message': message,
  233. 'messageType': messageType
  234. }
  235. if keyVersion:
  236. body['keyVersion'] = keyVersion
  237. try:
  238. base64.b64decode(message)
  239. base64.b64decode(signature)
  240. except (binascii.Error, TypeError):
  241. raise ValueError("message and signature must be base64 encoded")
  242. return self._send_request(http_methods.POST, path, json.dumps(body),
  243. params=params, config=config)
  244. @required(keyId=(str, bytes), keySpec=(str, bytes))
  245. def generate_dataKey(self, keyId, keySpec, numberOfBytes=-1, config=None):
  246. """
  247. generate a data key by master key
  248. :type keyId: string
  249. :param keyId: indicate kms will use which masterkey to generate data key
  250. :type keySpec: string
  251. :param keySpec: AES_128 or AES_256
  252. :type numberOfBytes: int
  253. :param numberOfBytes: The length of data key
  254. """
  255. path = b'/'
  256. params = {}
  257. params['action'] = b'GenerateDataKey'
  258. body={}
  259. body['keyId'] = keyId
  260. if keySpec != "AES_128" and keySpec != "AES_256":
  261. raise ValueError("only support AES_128 and AES_256")
  262. body['keySpec'] = keySpec
  263. body['numberOfBytes'] = numberOfBytes
  264. return self._send_request(http_methods.POST, path, json.dumps(body),
  265. params=params, config=config)
  266. @required(keyId=(str, bytes), rotateCycle=(int))
  267. def updaterotation_masterKey(self, keyId, rotateCycle, config=None):
  268. """
  269. update your master key rptation
  270. :type keyId: string
  271. :type rotateCycle: int
  272. :param keyId: the keyId of masterkey will be enable
  273. :param rotateCycle: the rotatecycle of masterkey
  274. """
  275. path = b'/'
  276. params = {}
  277. params['action'] = b'EnableRotation'
  278. body={}
  279. body['keyId'] = keyId
  280. body['rotateCycle'] = rotateCycle
  281. return self._send_request(http_methods.POST, path, json.dumps(body),
  282. params=params, config=config)
  283. @required(keyId=(str, bytes))
  284. def enable_masterKey(self, keyId, config=None):
  285. """
  286. enable your master key
  287. :type keyId: string
  288. :param keyId: the keyId of masterkey will be enable
  289. """
  290. path = b'/'
  291. params = {}
  292. params['action'] = b'EnableKey'
  293. body={}
  294. body['keyId'] = keyId
  295. return self._send_request(http_methods.POST, path, json.dumps(body),
  296. params=params, config=config)
  297. @required(keyId=(str, bytes))
  298. def disable_masterKey(self, keyId, config=None):
  299. """
  300. disable your master key
  301. :type keyId: string
  302. :param keyId: the keyId of masterkey will be diable
  303. """
  304. path = b'/'
  305. params = {}
  306. params['action'] = b'DisableKey'
  307. body={}
  308. body['keyId'] = keyId
  309. return self._send_request(http_methods.POST, path, json.dumps(body),
  310. params=params, config=config)
  311. @required(keyId=(str, bytes), pendingWindowInDays=int)
  312. def scheduleDelete_masterKey(self, keyId, pendingWindowInDays, config=None):
  313. """
  314. schedule delete master key
  315. :type keyId: string
  316. :param keyId: the keyId of masterkey will be deleted
  317. :type pendingWindowInDays: int
  318. :pram pendingWindowInDays: kms will wait pendingWindowInDays day then delete the key
  319. """
  320. path = b'/'
  321. params = {}
  322. params['action'] = b'ScheduleKeyDeletion'
  323. body={}
  324. body['keyId'] = keyId
  325. if pendingWindowInDays > 30 or pendingWindowInDays < 7:
  326. raise ValueError("please input pendingWindowInDays >=7 and <=30")
  327. body['pendingWindowInDays'] = pendingWindowInDays
  328. return self._send_request(http_methods.POST, path, json.dumps(body),
  329. params=params, config=config)
  330. @required(keyId=(str, bytes))
  331. def cancelDelete_masterKey(self, keyId, config=None):
  332. """
  333. cancel delete master key
  334. :type keyId: string
  335. :param keyId: the keyId of masterkey will cancel delete
  336. """
  337. path = b'/'
  338. params = {}
  339. params['action'] = b'CancelKeyDeletion'
  340. body={}
  341. body['keyId'] = keyId
  342. return self._send_request(http_methods.POST, path, json.dumps(body),
  343. params=params, config=config)
  344. @required(keyId=(str, bytes))
  345. def describe_masterKey(self, keyId, config=None):
  346. """
  347. descript the master key
  348. :type keyId: string
  349. :param keyId: the keyId of masterkey
  350. """
  351. path = b'/'
  352. params = {}
  353. params['action'] = b'DescribeKey'
  354. body={}
  355. body['keyId'] = keyId
  356. return self._send_request(http_methods.POST, path, json.dumps(body),
  357. params=params, config=config)
  358. @required(keyId=(str, bytes))
  359. def get_parameters_for_import(self, keyId, publicKeyEncoding, wrappingAlgorithm="RSAES_PKCS1_V1_5",
  360. wrappingKeySpec="RSA_2048", config=None):
  361. """
  362. get parameters for import
  363. :type keyId: string
  364. :param keyId: the keyId of masterkey
  365. :type wrappingAlgorithm: string
  366. :param wrappingAlgorithm: the algorithm for user encrypt local key
  367. :type wrappingKeySpec:string
  368. :param wrappingKeySpec: the pubkey spec for user encrypt local key
  369. """
  370. path = b'/'
  371. params = {}
  372. params['action'] = b'GetParametersForImport'
  373. body={}
  374. body['keyId'] = keyId
  375. if wrappingAlgorithm != "RSAES_PKCS1_V1_5":
  376. raise TypeError("only support RSAES_PKCS1_V1_5")
  377. body['wrappingAlgorithm'] = wrappingAlgorithm
  378. if wrappingKeySpec != "RSA_2048":
  379. raise TypeError("only support RSA_2048")
  380. body['wrappingKeySpec'] = wrappingKeySpec
  381. if publicKeyEncoding != "RAW_HEX" and publicKeyEncoding != "BASE64" and publicKeyEncoding != "PEM":
  382. raise ValueError("only support RAW_HEX or BASE64 or PEM")
  383. body['publicKeyEncoding'] = publicKeyEncoding
  384. return self._send_request(http_methods.POST, path, json.dumps(body),
  385. params=params, config=config)
  386. @required(keyId=(str, bytes), importToken=(str, bytes), encryptedKey=(str, bytes), keySpec=(str, bytes))
  387. def import_symmetricMasterKey(self, keyId, importToken, encryptedKey, keySpec,
  388. keyUsage="ENCRYPT_DECRYPT", config=None):
  389. """
  390. import symmetric key
  391. :type keyId: string
  392. :param keyId: the keyId of masterkey
  393. :type importToken: string
  394. :param importToken: token from import parameter
  395. :type encryptedKey: string
  396. :param encryptedKey: the symmetric key encrypted by pubkey
  397. :type keySpec: string
  398. :param keySpec: the import key spec
  399. :type keyUsage: string
  400. :param keyUsage: default "ENCRYPT_DECRYPT"
  401. """
  402. path = b'/'
  403. params = {}
  404. params['action'] = b'ImportKey'
  405. body={}
  406. body['keyId'] = keyId
  407. body['importToken'] = importToken
  408. body['encryptedKey'] = encryptedKey
  409. body['keySpec'] = keySpec
  410. body['keyUsage'] = keyUsage
  411. return self._send_request(http_methods.POST, path, json.dumps(body),
  412. params=params, config=config)
  413. @required(keyId=(str, bytes),
  414. importToken=(str, bytes),
  415. asymmetricKeySpec=(str, bytes),
  416. asymmetricKeyUsage=(str, bytes),
  417. encryptedKeyEncryptionKey=(str, bytes),
  418. asymmetricKey=object)
  419. def import_asymmetricMasterKey(self, keyId, importToken, asymmetricKeySpec, encryptedKeyEncryptionKey,
  420. asymmetricKeyUsage="ENCRYPT_DECRYPT", config=None, **kwargs):
  421. """
  422. import asymmetric key
  423. :type keyId: string
  424. :param keyId: the keyId of masterkey
  425. :type importToken: string
  426. :param importToken: token from import parameter
  427. :type asymmetricKeySpec: string
  428. :param asymmetricKeySpec: the import key spec
  429. :type encryptedKeyEncryptionKey: string
  430. :param encryptedKeyEncryptionKey: EncryptionKey
  431. :type asymmetricKey: **args
  432. :param asymmetricKey: include publicKeyDer encryptedD encryptedP encryptedQ encryptedDp encryptedDq encryptedQinv
  433. """
  434. path = b'/'
  435. params = {}
  436. params['action'] = b'ImportAsymmetricKey'
  437. body={}
  438. body['keyId'] = keyId
  439. body['importToken'] = importToken
  440. body['asymmetricKeySpec'] = asymmetricKeySpec
  441. body['asymmetricKeyUsage'] = asymmetricKeyUsage
  442. body['encryptedKeyEncryptionKey'] = encryptedKeyEncryptionKey
  443. body['encryptedRsaKey'] = {}
  444. if kwargs['publicKeyDer'] is None:
  445. raise ValueError('arg "publicKeyDer" should not be None')
  446. body['encryptedRsaKey']['publicKeyDer'] = kwargs['publicKeyDer']
  447. if kwargs['encryptedD'] is None:
  448. raise ValueError('arg "encryptedD" should not be None')
  449. body['encryptedRsaKey']['encryptedD'] = kwargs['encryptedD']
  450. if kwargs['encryptedP'] is None:
  451. raise ValueError('arg "encryptedP" should not be None')
  452. body['encryptedRsaKey']['encryptedP'] = kwargs['encryptedP']
  453. if kwargs['encryptedQ'] is None:
  454. raise ValueError('arg "encryptedQ" should not be None')
  455. body['encryptedRsaKey']['encryptedQ'] = kwargs['encryptedQ']
  456. if kwargs['encryptedDp'] is None:
  457. raise ValueError('arg "encryptedDp" should not be None')
  458. body['encryptedRsaKey']['encryptedDp'] = kwargs['encryptedDp']
  459. if kwargs['encryptedDq'] is None:
  460. raise ValueError('arg "encryptedDq" should not be None')
  461. body['encryptedRsaKey']['encryptedDq'] = kwargs['encryptedDq']
  462. if kwargs['encryptedQinv'] is None:
  463. raise ValueError('arg "encryptedQinv" should not be None')
  464. body['encryptedRsaKey']['encryptedQinv'] = kwargs['encryptedQinv']
  465. return self._send_request(http_methods.POST, path, json.dumps(body),
  466. params=params, config=config)
  467. @required(keyId=(str, bytes),
  468. importToken=(str, bytes),
  469. asymmetricKeySpec=(str, bytes),
  470. asymmetricKeyUsage=(str, bytes),
  471. encryptedKeyEncryptionKey=(str, bytes),
  472. asymmetricKey=object)
  473. def import_asymmetricSM2MasterKey(self, keyId, importToken, asymmetricKeySpec, encryptedKeyEncryptionKey,
  474. asymmetricKeyUsage="ENCRYPT_DECRYPT", config=None, **kwargs):
  475. """
  476. import asymmetric key
  477. :type keyId: string
  478. :param keyId: the keyId of masterkey
  479. :type importToken: string
  480. :param importToken: token from import parameter
  481. :type asymmetricKeySpec: string
  482. :param asymmetricKeySpec: the import key spec
  483. :type encryptedKeyEncryptionKey: string
  484. :param encryptedKeyEncryptionKey: EncryptionKey
  485. :type asymmetricKey: **args
  486. :param asymmetricKey: include publicKeyDer encryptedPrivateKey
  487. """
  488. path = b'/'
  489. params = {}
  490. params['action'] = b'ImportAsymmetricKey'
  491. body={}
  492. body['keyId'] = keyId
  493. body['importToken'] = importToken
  494. body['asymmetricKeySpec'] = asymmetricKeySpec
  495. body['asymmetricKeyUsage'] = asymmetricKeyUsage
  496. body['encryptedKeyEncryptionKey'] = encryptedKeyEncryptionKey
  497. body['encryptedSm2Key'] = {}
  498. if kwargs['publicKeyDer'] is None:
  499. raise ValueError('arg "publicKeyDer" should not be None')
  500. body['encryptedSm2Key']['publicKeyDer'] = kwargs['publicKeyDer']
  501. if kwargs['encryptedPrivateKey'] is None:
  502. raise ValueError('arg "encryptedPrivateKey" should not be None')
  503. body['encryptedSm2Key']['encryptedPrivateKey'] = kwargs['encryptedPrivateKey']
  504. return self._send_request(http_methods.POST, path, json.dumps(body),
  505. params=params, config=config)