Skip to content

Commit f9d00dc

Browse files
feat: Support configuring AsymmetricCipherManager through Django settings (closed #14)
1 parent d9b9773 commit f9d00dc

7 files changed

Lines changed: 178 additions & 31 deletions

File tree

bkcrypto/contrib/django/ciphers.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from bkcrypto.symmetric.ciphers import BaseSymmetricCipher
1818
from bkcrypto.symmetric.options import SymmetricOptions
1919

20-
from .init_configs import SymmetricCipherInitConfig
20+
from .init_configs import AsymmetricCipherInitConfig, SymmetricCipherInitConfig
2121
from .settings import crypto_settings
2222

2323

@@ -80,4 +80,32 @@ def cipher(
8080
return cipher
8181

8282

83+
class AsymmetricCipherManager:
84+
_cache: typing.Optional[typing.Dict[str, BaseAsymmetricCipher]] = None
85+
86+
def __init__(self):
87+
self._cache: [str, BaseAsymmetricCipher] = {}
88+
89+
def cipher(
90+
self, using: typing.Optional[str] = None, cipher_type: typing.Optional[str] = None
91+
) -> BaseAsymmetricCipher:
92+
93+
using: str = using or "default"
94+
if using not in crypto_settings.ASYMMETRIC_CIPHERS:
95+
raise RuntimeError(f"Invalid using {using}")
96+
97+
cipher_type: str = cipher_type or crypto_settings.ASYMMETRIC_CIPHER_TYPE
98+
cache_key: str = f"{using}-{cipher_type}"
99+
if cache_key in self._cache:
100+
return self._cache[cache_key]
101+
102+
init_config: AsymmetricCipherInitConfig = crypto_settings.ASYMMETRIC_CIPHERS[using]
103+
cipher: BaseAsymmetricCipher = get_asymmetric_cipher(**init_config.as_get_cipher_params(cipher_type))
104+
self._cache[cache_key] = cipher
105+
return cipher
106+
107+
83108
symmetric_cipher_manager = SymmetricCipherManager()
109+
110+
111+
asymmetric_cipher_manager = AsymmetricCipherManager()

bkcrypto/contrib/django/init_configs.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,48 @@
1414
import typing
1515
from dataclasses import asdict, dataclass
1616

17-
from bkcrypto.symmetric.configs import KeyConfig
17+
from bkcrypto.asymmetric.configs import KeyConfig as AsymmetricKeyConfig
18+
from bkcrypto.asymmetric.options import AsymmetricOptions
19+
from bkcrypto.symmetric.configs import KeyConfig as SymmetricKeyConfig
1820
from bkcrypto.symmetric.options import SymmetricOptions
1921
from bkcrypto.utils.module_loding import import_string
2022

2123

2224
@dataclass
23-
class SymmetricCipherInitConfig:
24-
get_key_config: typing.Optional[str] = None
25+
class CipherInitConfig:
2526
# 默认取值 f"{cipher_type}_str:::"
2627
db_prefix_map: typing.Dict[str, str] = None
2728
prefix_cipher_type_map: typing.Dict[str, str] = None
28-
get_key_config_func: typing.Optional[typing.Callable[[str], KeyConfig]] = None
29+
get_key_config: typing.Optional[str] = None
30+
get_key_config_func: typing.Optional[
31+
typing.Callable[[str], typing.Union[AsymmetricKeyConfig, SymmetricKeyConfig]]
32+
] = None
2933
common: typing.Optional[typing.Dict[str, typing.Any]] = None
30-
cipher_options: typing.Optional[typing.Dict[str, typing.Optional[SymmetricOptions]]] = None
34+
cipher_options: typing.Optional[typing.Dict[str, typing.Union[SymmetricOptions, AsymmetricOptions, None]]] = None
3135

3236
def __post_init__(self):
33-
self.db_prefix_map = self.db_prefix_map or {}
34-
3537
if self.get_key_config:
3638
self.get_key_config_func = import_string(self.get_key_config)
39+
self.db_prefix_map = self.db_prefix_map or {}
3740

3841
def as_get_cipher_params(self, cipher_type: str):
3942
# get key hook 不为空,优先从此处取 key
4043
if self.get_key_config_func:
41-
key_config: KeyConfig = self.get_key_config_func(cipher_type)
44+
key_config = self.get_key_config_func(cipher_type)
4245
key_dict: typing.Dict = asdict(key_config)
4346
else:
4447
key_dict: typing.Dict = {}
4548

4649
common = self.common or {}
4750
common.update(key_dict)
4851
return {"cipher_type": cipher_type, "common": common, "cipher_options": self.cipher_options}
52+
53+
54+
@dataclass
55+
class SymmetricCipherInitConfig(CipherInitConfig):
56+
get_key_config_func: typing.Optional[typing.Callable[[str], SymmetricKeyConfig]] = None
57+
58+
59+
@dataclass
60+
class AsymmetricCipherInitConfig(CipherInitConfig):
61+
get_key_config_func: typing.Optional[typing.Callable[[str], AsymmetricKeyConfig]] = None

bkcrypto/contrib/django/settings.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
from bkcrypto import constants
1919
from bkcrypto.asymmetric.ciphers import RSAAsymmetricCipher, SM2AsymmetricCipher
20-
from bkcrypto.contrib.django.init_configs import SymmetricCipherInitConfig
20+
from bkcrypto.contrib.django.init_configs import AsymmetricCipherInitConfig, CipherInitConfig, SymmetricCipherInitConfig
2121
from bkcrypto.symmetric.ciphers import AESSymmetricCipher, SM4SymmetricCipher
2222
from bkcrypto.utils import module_loding
2323

@@ -43,6 +43,17 @@
4343
},
4444
},
4545
},
46+
"ASYMMETRIC_CIPHERS": {
47+
"default": {
48+
# 可选,用于在 settings 没法直接获取 key 的情况
49+
"get_key_config": None,
50+
# 前缀和 cipher type 必须一一对应,且不能有前缀匹配关系
51+
"db_prefix_map": {
52+
constants.AsymmetricCipherType.RSA.value: f"{constants.AsymmetricCipherType.RSA.value.lower()}_str:::",
53+
constants.AsymmetricCipherType.SM2.value: f"{constants.AsymmetricCipherType.SM2.value.lower()}_str:::",
54+
},
55+
},
56+
},
4657
}
4758

4859
IMPORT_STRINGS = []
@@ -107,20 +118,27 @@ def __getattr__(self, attr):
107118
for cipher_type, cipher_import_path in val.items()
108119
}
109120

110-
if attr in ["SYMMETRIC_CIPHERS"]:
111-
using__init_config_map: typing.Dict[str, SymmetricCipherInitConfig] = {}
121+
if attr in ["SYMMETRIC_CIPHERS", "ASYMMETRIC_CIPHERS"]:
122+
using__init_config_map: typing.Dict[str, CipherInitConfig] = {}
112123
for using, init_config_params in val.items():
113124
prefix_cipher_type_map: typing.Dict[str, str] = {}
114125
db_prefix_map: typing.Dict[str, str] = init_config_params.get("db_prefix_map") or {}
115-
for cipher_type in self.SYMMETRIC_CIPHER_CLASSES.keys():
126+
cipher_types: typing.List[str] = [
127+
self.SYMMETRIC_CIPHER_CLASSES.keys(),
128+
self.ASYMMETRIC_CIPHER_CLASSES.keys(),
129+
][attr == "ASYMMETRIC_CIPHERS"]
130+
for cipher_type in cipher_types:
116131
if cipher_type in db_prefix_map:
117132
prefix_cipher_type_map[db_prefix_map[cipher_type]] = cipher_type
118133
continue
119134
db_prefix_map[cipher_type] = f"{cipher_type.lower()}_str:::"
120135
prefix_cipher_type_map[f"{cipher_type.lower()}_str:::"] = cipher_type
121136
init_config_params["db_prefix_map"] = db_prefix_map
122137
init_config_params["prefix_cipher_type_map"] = prefix_cipher_type_map
123-
using__init_config_map[using] = from_dict(SymmetricCipherInitConfig, init_config_params)
138+
using__init_config_map[using] = from_dict(
139+
[SymmetricCipherInitConfig, AsymmetricCipherInitConfig][attr == "ASYMMETRIC_CIPHER_CLASSES"],
140+
init_config_params,
141+
)
124142
val = using__init_config_map
125143

126144
# Coerce import strings into classes

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "bk-crypto-python-sdk"
3-
version = "1.0.2"
3+
version = "1.0.3"
44
description = "bk-crypto-python-sdk is a lightweight cryptography toolkit for Python applications based on Cryptodome / tongsuopy and other encryption libraries."
55
authors = ["TencentBlueKing <contactus_bk@tencent.com>"]
66
readme = "readme.md"

readme.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ $ pip install bk-crypto-python-sdk
4141
在项目中配置
4242

4343
```python
44-
import os
4544
from bkcrypto import constants
4645
from bkcrypto.symmetric.options import AESSymmetricOptions, SM4SymmetricOptions
46+
from bkcrypto.asymmetric.options import RSAAsymmetricOptions
4747

4848
BKCRYPTO = {
4949
# 声明项目所使用的非对称加密算法
@@ -69,12 +69,26 @@ BKCRYPTO = {
6969
constants.SymmetricCipherType.SM4.value: SM4SymmetricOptions(mode=constants.SymmetricMode.CTR)
7070
}
7171
},
72+
},
73+
"ASYMMETRIC_CIPHERS": {
74+
# 配置同 SYMMETRIC_CIPHERS
75+
"default": {
76+
"common": {"public_key_string": "your key"},
77+
"cipher_options": {
78+
constants.AsymmetricCipherType.RSA.value: RSAAsymmetricOptions(
79+
padding=constants.RSACipherPadding.PKCS1_OAEP
80+
),
81+
constants.AsymmetricCipherType.SM2.value: SM4SymmetricOptions()
82+
},
83+
},
7284
}
7385
}
7486
```
7587

7688
#### 非对称加密
7789

90+
使用 `get_asymmetric_cipher` 获取 `cipher`
91+
7892
```python
7993
from bkcrypto.asymmetric.ciphers import BaseAsymmetricCipher
8094
from bkcrypto.contrib.django.ciphers import get_asymmetric_cipher
@@ -87,8 +101,34 @@ assert "123" == asymmetric_cipher.decrypt(asymmetric_cipher.encrypt("123"))
87101
assert asymmetric_cipher.verify(plaintext="123", signature=asymmetric_cipher.sign("123"))
88102
```
89103

104+
使用 `asymmetric_cipher_manager` 获取 `BKCRYPTO.ASYMMETRIC_CIPHERS` 配置的 `cipher`
105+
106+
```python
107+
from bkcrypto.asymmetric.ciphers import BaseAsymmetricCipher
108+
from bkcrypto.contrib.django.ciphers import asymmetric_cipher_manager
109+
110+
asymmetric_cipher: BaseAsymmetricCipher = asymmetric_cipher_manager.cipher(using="default")
111+
112+
# 加解密
113+
assert "123" == asymmetric_cipher.decrypt(asymmetric_cipher.encrypt("123"))
114+
# 验签
115+
assert asymmetric_cipher.verify(plaintext="123", signature=asymmetric_cipher.sign("123"))
116+
```
117+
90118
#### 对称加密
91119

120+
使用 `get_symmetric_cipher` 获取 `cipher`
121+
122+
```python
123+
from bkcrypto.symmetric.ciphers import BaseSymmetricCipher
124+
from bkcrypto.contrib.django.ciphers import get_symmetric_cipher
125+
126+
symmetric_cipher: BaseSymmetricCipher = get_symmetric_cipher()
127+
assert "123" == symmetric_cipher.decrypt(symmetric_cipher.encrypt("123"))
128+
```
129+
130+
使用 `symmetric_cipher_manager` 获取 `BKCRYPTO.SYMMETRIC_CIPHERS` 配置的 `cipher`
131+
92132
```python
93133
from bkcrypto.symmetric.ciphers import BaseSymmetricCipher
94134
from bkcrypto.contrib.django.ciphers import symmetric_cipher_manager

readme_en.md

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,45 +38,60 @@ $ pip install bk-crypto-python-sdk
3838

3939
### Usage
4040

41-
> For more usage, refer
41+
> For more usage guidelines, please refer
4242
> to: [Usage Documentation](https://github.com/TencentBlueKing/crypto-python-sdk/blob/main/docs/usage.md)
4343
44-
Configure in the project
44+
Configure in the project:
4545

4646
```python
47-
import os
4847
from bkcrypto import constants
4948
from bkcrypto.symmetric.options import AESSymmetricOptions, SM4SymmetricOptions
49+
from bkcrypto.asymmetric.options import RSAAsymmetricOptions
5050

5151
BKCRYPTO = {
52-
# Declare the asymmetric encryption algorithm used in the project
52+
# Specify the asymmetric encryption algorithm used in the project
5353
"ASYMMETRIC_CIPHER_TYPE": constants.AsymmetricCipherType.SM2.value,
54-
# Declare the symmetric encryption algorithm used in the project
54+
# Specify the symmetric encryption algorithm used in the project
5555
"SYMMETRIC_CIPHER_TYPE": constants.SymmetricCipherType.SM4.value,
5656
"SYMMETRIC_CIPHERS": {
57-
# default - The configured symmetric encryption instance, multiple instances can be configured according to project needs
57+
# default - The configured symmetric encryption instance can be configured with multiple instances according to project needs
5858
"default": {
59-
# Optional, used when the key cannot be obtained directly in settings
59+
# Optional, for scenarios where the key cannot be obtained directly from settings
6060
# "get_key_config": "apps.utils.encrypt.key.get_key_config",
61-
# Optional, used for ModelField, when encrypting, it carries this prefix into the database, when decrypting, it analyzes this prefix and selects the corresponding decryption algorithm
62-
# ⚠️ Prefix and cipher type must correspond one-to-one, and there can be no prefix matching relationship
61+
# Optional, used for ModelField; carry the prefix when encrypting and store it in the database,
62+
# analyze the prefix when decrypting and choose the appropriate decryption algorithm
63+
# ⚠️ Prefix and cipher type must correspond one-to-one, and no prefix match relationship is allowed
6364
# "db_prefix_map": {
6465
# SymmetricCipherType.AES.value: "aes_str:::",
6566
# SymmetricCipherType.SM4.value: "sm4_str:::"
6667
# },
67-
# Common parameter configuration, different cipher initialization shares these parameters
68+
# Common parameter configuration, shared by different ciphers during initialization
6869
"common": {"key": "your key"},
6970
"cipher_options": {
7071
constants.SymmetricCipherType.AES.value: AESSymmetricOptions(key_size=16),
71-
# BlueKing recommended configuration
72+
# Recommended configuration by BlueKing
7273
constants.SymmetricCipherType.SM4.value: SM4SymmetricOptions(mode=constants.SymmetricMode.CTR)
7374
}
7475
},
76+
},
77+
"ASYMMETRIC_CIPHERS": {
78+
# Configuration is the same as SYMMETRIC_CIPHERS
79+
"default": {
80+
"common": {"public_key_string": "your key"},
81+
"cipher_options": {
82+
constants.AsymmetricCipherType.RSA.value: RSAAsymmetricOptions(
83+
padding=constants.RSACipherPadding.PKCS1_OAEP
84+
),
85+
constants.AsymmetricCipherType.SM2.value: SM4SymmetricOptions()
86+
},
87+
},
7588
}
7689
}
7790
```
7891

79-
#### Asymmetric encryption
92+
#### Asymmetric Encryption
93+
94+
Use `get_asymmetric_cipher` to get the `cipher`
8095

8196
```python
8297
from bkcrypto.asymmetric.ciphers import BaseAsymmetricCipher
@@ -86,17 +101,43 @@ asymmetric_cipher: BaseAsymmetricCipher = get_asymmetric_cipher()
86101

87102
# Encrypt and decrypt
88103
assert "123" == asymmetric_cipher.decrypt(asymmetric_cipher.encrypt("123"))
89-
# Verify signature
104+
# Signature verification
105+
assert asymmetric_cipher.verify(plaintext="123", signature=asymmetric_cipher.sign("123"))
106+
```
107+
108+
Use `asymmetric_cipher_manager` to get the `BKCRYPTO.ASYMMETRIC_CIPHERS` configured `cipher`
109+
110+
```python
111+
from bkcrypto.asymmetric.ciphers import BaseAsymmetricCipher
112+
from bkcrypto.contrib.django.ciphers import asymmetric_cipher_manager
113+
114+
asymmetric_cipher: BaseAsymmetricCipher = asymmetric_cipher_manager.cipher(using="default")
115+
116+
# Encrypt and decrypt
117+
assert "123" == asymmetric_cipher.decrypt(asymmetric_cipher.encrypt("123"))
118+
# Signature verification
90119
assert asymmetric_cipher.verify(plaintext="123", signature=asymmetric_cipher.sign("123"))
91120
```
92121

93-
#### Symmetric encryption
122+
#### Symmetric Encryption
123+
124+
Use `get_symmetric_cipher` to get the `cipher`
125+
126+
```python
127+
from bkcrypto.symmetric.ciphers import BaseSymmetricCipher
128+
from bkcrypto.contrib.django.ciphers import get_symmetric_cipher
129+
130+
symmetric_cipher: BaseSymmetricCipher = get_symmetric_cipher()
131+
assert "123" == symmetric_cipher.decrypt(symmetric_cipher.encrypt("123"))
132+
```
133+
134+
Use `symmetric_cipher_manager` to get the `BKCRYPTO.SYMMETRIC_CIPHERS` configured `cipher`
94135

95136
```python
96137
from bkcrypto.symmetric.ciphers import BaseSymmetricCipher
97138
from bkcrypto.contrib.django.ciphers import symmetric_cipher_manager
98139

99-
# using - Specify the symmetric encryption instance, the default is `default`
140+
# using - Specify the symmetric encryption instance, using `default` by default
100141
symmetric_cipher: BaseSymmetricCipher = symmetric_cipher_manager.cipher(using="default")
101142
assert "123" == symmetric_cipher.decrypt(symmetric_cipher.encrypt("123"))
102143
```

release.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,10 @@
2424
### Feature
2525

2626
* [ Feature ] Add support for backward compatibility to Python v3.6.2 ([#12](https://github.com/TencentBlueKing/crypto-python-sdk/issues/12))
27+
28+
29+
## 1.0.3 - 2023-07-19
30+
31+
### Feature
32+
33+
* [ Feature ] Support configuring AsymmetricCipherManager through Django settings ([#14](https://github.com/TencentBlueKing/crypto-python-sdk/issues/14))

0 commit comments

Comments
 (0)