Skip to content

Commit

Permalink
feat: Support configuring AsymmetricCipherManager through Django sett…
Browse files Browse the repository at this point in the history
…ings (closed #14)
  • Loading branch information
ZhuoZhuoCrayon committed Jul 19, 2023
1 parent d9b9773 commit f9d00dc
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 31 deletions.
30 changes: 29 additions & 1 deletion bkcrypto/contrib/django/ciphers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from bkcrypto.symmetric.ciphers import BaseSymmetricCipher
from bkcrypto.symmetric.options import SymmetricOptions

from .init_configs import SymmetricCipherInitConfig
from .init_configs import AsymmetricCipherInitConfig, SymmetricCipherInitConfig
from .settings import crypto_settings


Expand Down Expand Up @@ -80,4 +80,32 @@ def cipher(
return cipher


class AsymmetricCipherManager:
_cache: typing.Optional[typing.Dict[str, BaseAsymmetricCipher]] = None

def __init__(self):
self._cache: [str, BaseAsymmetricCipher] = {}

def cipher(
self, using: typing.Optional[str] = None, cipher_type: typing.Optional[str] = None
) -> BaseAsymmetricCipher:

using: str = using or "default"
if using not in crypto_settings.ASYMMETRIC_CIPHERS:
raise RuntimeError(f"Invalid using {using}")

cipher_type: str = cipher_type or crypto_settings.ASYMMETRIC_CIPHER_TYPE
cache_key: str = f"{using}-{cipher_type}"
if cache_key in self._cache:
return self._cache[cache_key]

init_config: AsymmetricCipherInitConfig = crypto_settings.ASYMMETRIC_CIPHERS[using]
cipher: BaseAsymmetricCipher = get_asymmetric_cipher(**init_config.as_get_cipher_params(cipher_type))
self._cache[cache_key] = cipher
return cipher


symmetric_cipher_manager = SymmetricCipherManager()


asymmetric_cipher_manager = AsymmetricCipherManager()
29 changes: 21 additions & 8 deletions bkcrypto/contrib/django/init_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,48 @@
import typing
from dataclasses import asdict, dataclass

from bkcrypto.symmetric.configs import KeyConfig
from bkcrypto.asymmetric.configs import KeyConfig as AsymmetricKeyConfig
from bkcrypto.asymmetric.options import AsymmetricOptions
from bkcrypto.symmetric.configs import KeyConfig as SymmetricKeyConfig
from bkcrypto.symmetric.options import SymmetricOptions
from bkcrypto.utils.module_loding import import_string


@dataclass
class SymmetricCipherInitConfig:
get_key_config: typing.Optional[str] = None
class CipherInitConfig:
# 默认取值 f"{cipher_type}_str:::"
db_prefix_map: typing.Dict[str, str] = None
prefix_cipher_type_map: typing.Dict[str, str] = None
get_key_config_func: typing.Optional[typing.Callable[[str], KeyConfig]] = None
get_key_config: typing.Optional[str] = None
get_key_config_func: typing.Optional[
typing.Callable[[str], typing.Union[AsymmetricKeyConfig, SymmetricKeyConfig]]
] = None
common: typing.Optional[typing.Dict[str, typing.Any]] = None
cipher_options: typing.Optional[typing.Dict[str, typing.Optional[SymmetricOptions]]] = None
cipher_options: typing.Optional[typing.Dict[str, typing.Union[SymmetricOptions, AsymmetricOptions, None]]] = None

def __post_init__(self):
self.db_prefix_map = self.db_prefix_map or {}

if self.get_key_config:
self.get_key_config_func = import_string(self.get_key_config)
self.db_prefix_map = self.db_prefix_map or {}

def as_get_cipher_params(self, cipher_type: str):
# get key hook 不为空,优先从此处取 key
if self.get_key_config_func:
key_config: KeyConfig = self.get_key_config_func(cipher_type)
key_config = self.get_key_config_func(cipher_type)
key_dict: typing.Dict = asdict(key_config)
else:
key_dict: typing.Dict = {}

common = self.common or {}
common.update(key_dict)
return {"cipher_type": cipher_type, "common": common, "cipher_options": self.cipher_options}


@dataclass
class SymmetricCipherInitConfig(CipherInitConfig):
get_key_config_func: typing.Optional[typing.Callable[[str], SymmetricKeyConfig]] = None


@dataclass
class AsymmetricCipherInitConfig(CipherInitConfig):
get_key_config_func: typing.Optional[typing.Callable[[str], AsymmetricKeyConfig]] = None
28 changes: 23 additions & 5 deletions bkcrypto/contrib/django/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from bkcrypto import constants
from bkcrypto.asymmetric.ciphers import RSAAsymmetricCipher, SM2AsymmetricCipher
from bkcrypto.contrib.django.init_configs import SymmetricCipherInitConfig
from bkcrypto.contrib.django.init_configs import AsymmetricCipherInitConfig, CipherInitConfig, SymmetricCipherInitConfig
from bkcrypto.symmetric.ciphers import AESSymmetricCipher, SM4SymmetricCipher
from bkcrypto.utils import module_loding

Expand All @@ -43,6 +43,17 @@
},
},
},
"ASYMMETRIC_CIPHERS": {
"default": {
# 可选,用于在 settings 没法直接获取 key 的情况
"get_key_config": None,
# 前缀和 cipher type 必须一一对应,且不能有前缀匹配关系
"db_prefix_map": {
constants.AsymmetricCipherType.RSA.value: f"{constants.AsymmetricCipherType.RSA.value.lower()}_str:::",
constants.AsymmetricCipherType.SM2.value: f"{constants.AsymmetricCipherType.SM2.value.lower()}_str:::",
},
},
},
}

IMPORT_STRINGS = []
Expand Down Expand Up @@ -107,20 +118,27 @@ def __getattr__(self, attr):
for cipher_type, cipher_import_path in val.items()
}

if attr in ["SYMMETRIC_CIPHERS"]:
using__init_config_map: typing.Dict[str, SymmetricCipherInitConfig] = {}
if attr in ["SYMMETRIC_CIPHERS", "ASYMMETRIC_CIPHERS"]:
using__init_config_map: typing.Dict[str, CipherInitConfig] = {}
for using, init_config_params in val.items():
prefix_cipher_type_map: typing.Dict[str, str] = {}
db_prefix_map: typing.Dict[str, str] = init_config_params.get("db_prefix_map") or {}
for cipher_type in self.SYMMETRIC_CIPHER_CLASSES.keys():
cipher_types: typing.List[str] = [
self.SYMMETRIC_CIPHER_CLASSES.keys(),
self.ASYMMETRIC_CIPHER_CLASSES.keys(),
][attr == "ASYMMETRIC_CIPHERS"]
for cipher_type in cipher_types:
if cipher_type in db_prefix_map:
prefix_cipher_type_map[db_prefix_map[cipher_type]] = cipher_type
continue
db_prefix_map[cipher_type] = f"{cipher_type.lower()}_str:::"
prefix_cipher_type_map[f"{cipher_type.lower()}_str:::"] = cipher_type
init_config_params["db_prefix_map"] = db_prefix_map
init_config_params["prefix_cipher_type_map"] = prefix_cipher_type_map
using__init_config_map[using] = from_dict(SymmetricCipherInitConfig, init_config_params)
using__init_config_map[using] = from_dict(
[SymmetricCipherInitConfig, AsymmetricCipherInitConfig][attr == "ASYMMETRIC_CIPHER_CLASSES"],
init_config_params,
)
val = using__init_config_map

# Coerce import strings into classes
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "bk-crypto-python-sdk"
version = "1.0.2"
version = "1.0.3"
description = "bk-crypto-python-sdk is a lightweight cryptography toolkit for Python applications based on Cryptodome / tongsuopy and other encryption libraries."
authors = ["TencentBlueKing <[email protected]>"]
readme = "readme.md"
Expand Down
42 changes: 41 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ $ pip install bk-crypto-python-sdk
在项目中配置

```python
import os
from bkcrypto import constants
from bkcrypto.symmetric.options import AESSymmetricOptions, SM4SymmetricOptions
from bkcrypto.asymmetric.options import RSAAsymmetricOptions

BKCRYPTO = {
# 声明项目所使用的非对称加密算法
Expand All @@ -69,12 +69,26 @@ BKCRYPTO = {
constants.SymmetricCipherType.SM4.value: SM4SymmetricOptions(mode=constants.SymmetricMode.CTR)
}
},
},
"ASYMMETRIC_CIPHERS": {
# 配置同 SYMMETRIC_CIPHERS
"default": {
"common": {"public_key_string": "your key"},
"cipher_options": {
constants.AsymmetricCipherType.RSA.value: RSAAsymmetricOptions(
padding=constants.RSACipherPadding.PKCS1_OAEP
),
constants.AsymmetricCipherType.SM2.value: SM4SymmetricOptions()
},
},
}
}
```

#### 非对称加密

使用 `get_asymmetric_cipher` 获取 `cipher`

```python
from bkcrypto.asymmetric.ciphers import BaseAsymmetricCipher
from bkcrypto.contrib.django.ciphers import get_asymmetric_cipher
Expand All @@ -87,8 +101,34 @@ assert "123" == asymmetric_cipher.decrypt(asymmetric_cipher.encrypt("123"))
assert asymmetric_cipher.verify(plaintext="123", signature=asymmetric_cipher.sign("123"))
```

使用 `asymmetric_cipher_manager` 获取 `BKCRYPTO.ASYMMETRIC_CIPHERS` 配置的 `cipher`

```python
from bkcrypto.asymmetric.ciphers import BaseAsymmetricCipher
from bkcrypto.contrib.django.ciphers import asymmetric_cipher_manager

asymmetric_cipher: BaseAsymmetricCipher = asymmetric_cipher_manager.cipher(using="default")

# 加解密
assert "123" == asymmetric_cipher.decrypt(asymmetric_cipher.encrypt("123"))
# 验签
assert asymmetric_cipher.verify(plaintext="123", signature=asymmetric_cipher.sign("123"))
```

#### 对称加密

使用 `get_symmetric_cipher` 获取 `cipher`

```python
from bkcrypto.symmetric.ciphers import BaseSymmetricCipher
from bkcrypto.contrib.django.ciphers import get_symmetric_cipher

symmetric_cipher: BaseSymmetricCipher = get_symmetric_cipher()
assert "123" == symmetric_cipher.decrypt(symmetric_cipher.encrypt("123"))
```

使用 `symmetric_cipher_manager` 获取 `BKCRYPTO.SYMMETRIC_CIPHERS` 配置的 `cipher`

```python
from bkcrypto.symmetric.ciphers import BaseSymmetricCipher
from bkcrypto.contrib.django.ciphers import symmetric_cipher_manager
Expand Down
71 changes: 56 additions & 15 deletions readme_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,45 +38,60 @@ $ pip install bk-crypto-python-sdk

### Usage

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

```python
import os
from bkcrypto import constants
from bkcrypto.symmetric.options import AESSymmetricOptions, SM4SymmetricOptions
from bkcrypto.asymmetric.options import RSAAsymmetricOptions

BKCRYPTO = {
# Declare the asymmetric encryption algorithm used in the project
# Specify the asymmetric encryption algorithm used in the project
"ASYMMETRIC_CIPHER_TYPE": constants.AsymmetricCipherType.SM2.value,
# Declare the symmetric encryption algorithm used in the project
# Specify the symmetric encryption algorithm used in the project
"SYMMETRIC_CIPHER_TYPE": constants.SymmetricCipherType.SM4.value,
"SYMMETRIC_CIPHERS": {
# default - The configured symmetric encryption instance, multiple instances can be configured according to project needs
# default - The configured symmetric encryption instance can be configured with multiple instances according to project needs
"default": {
# Optional, used when the key cannot be obtained directly in settings
# Optional, for scenarios where the key cannot be obtained directly from settings
# "get_key_config": "apps.utils.encrypt.key.get_key_config",
# 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
# ⚠️ Prefix and cipher type must correspond one-to-one, and there can be no prefix matching relationship
# Optional, used for ModelField; carry the prefix when encrypting and store it in the database,
# analyze the prefix when decrypting and choose the appropriate decryption algorithm
# ⚠️ Prefix and cipher type must correspond one-to-one, and no prefix match relationship is allowed
# "db_prefix_map": {
# SymmetricCipherType.AES.value: "aes_str:::",
# SymmetricCipherType.SM4.value: "sm4_str:::"
# },
# Common parameter configuration, different cipher initialization shares these parameters
# Common parameter configuration, shared by different ciphers during initialization
"common": {"key": "your key"},
"cipher_options": {
constants.SymmetricCipherType.AES.value: AESSymmetricOptions(key_size=16),
# BlueKing recommended configuration
# Recommended configuration by BlueKing
constants.SymmetricCipherType.SM4.value: SM4SymmetricOptions(mode=constants.SymmetricMode.CTR)
}
},
},
"ASYMMETRIC_CIPHERS": {
# Configuration is the same as SYMMETRIC_CIPHERS
"default": {
"common": {"public_key_string": "your key"},
"cipher_options": {
constants.AsymmetricCipherType.RSA.value: RSAAsymmetricOptions(
padding=constants.RSACipherPadding.PKCS1_OAEP
),
constants.AsymmetricCipherType.SM2.value: SM4SymmetricOptions()
},
},
}
}
```

#### Asymmetric encryption
#### Asymmetric Encryption

Use `get_asymmetric_cipher` to get the `cipher`

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

# Encrypt and decrypt
assert "123" == asymmetric_cipher.decrypt(asymmetric_cipher.encrypt("123"))
# Verify signature
# Signature verification
assert asymmetric_cipher.verify(plaintext="123", signature=asymmetric_cipher.sign("123"))
```

Use `asymmetric_cipher_manager` to get the `BKCRYPTO.ASYMMETRIC_CIPHERS` configured `cipher`

```python
from bkcrypto.asymmetric.ciphers import BaseAsymmetricCipher
from bkcrypto.contrib.django.ciphers import asymmetric_cipher_manager

asymmetric_cipher: BaseAsymmetricCipher = asymmetric_cipher_manager.cipher(using="default")

# Encrypt and decrypt
assert "123" == asymmetric_cipher.decrypt(asymmetric_cipher.encrypt("123"))
# Signature verification
assert asymmetric_cipher.verify(plaintext="123", signature=asymmetric_cipher.sign("123"))
```

#### Symmetric encryption
#### Symmetric Encryption

Use `get_symmetric_cipher` to get the `cipher`

```python
from bkcrypto.symmetric.ciphers import BaseSymmetricCipher
from bkcrypto.contrib.django.ciphers import get_symmetric_cipher

symmetric_cipher: BaseSymmetricCipher = get_symmetric_cipher()
assert "123" == symmetric_cipher.decrypt(symmetric_cipher.encrypt("123"))
```

Use `symmetric_cipher_manager` to get the `BKCRYPTO.SYMMETRIC_CIPHERS` configured `cipher`

```python
from bkcrypto.symmetric.ciphers import BaseSymmetricCipher
from bkcrypto.contrib.django.ciphers import symmetric_cipher_manager

# using - Specify the symmetric encryption instance, the default is `default`
# using - Specify the symmetric encryption instance, using `default` by default
symmetric_cipher: BaseSymmetricCipher = symmetric_cipher_manager.cipher(using="default")
assert "123" == symmetric_cipher.decrypt(symmetric_cipher.encrypt("123"))
```
Expand Down
7 changes: 7 additions & 0 deletions release.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,10 @@
### Feature

* [ Feature ] Add support for backward compatibility to Python v3.6.2 ([#12](https://github.com/TencentBlueKing/crypto-python-sdk/issues/12))


## 1.0.3 - 2023-07-19

### Feature

* [ Feature ] Support configuring AsymmetricCipherManager through Django settings ([#14](https://github.com/TencentBlueKing/crypto-python-sdk/issues/14))

0 comments on commit f9d00dc

Please sign in to comment.