├── .gitignore ├── DEVELOP.md ├── LICENSE ├── README.md ├── examples ├── sm2_cert.py ├── sm2_enc.py ├── sm2_key.py ├── sm2_sign.py ├── sm3.py ├── sm3_hmac.py ├── sm3_pbkdf2.py ├── sm4.py ├── sm4_cbc.py ├── sm4_ctr.py ├── sm4_gcm.py ├── sm9_enc.py ├── sm9_sign.py └── zuc.py ├── gmssl.py ├── pyproject.toml └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | *.pem 131 | .DS_Store 132 | -------------------------------------------------------------------------------- /DEVELOP.md: -------------------------------------------------------------------------------- 1 | # GmSSL-Python Develop 2 | 3 | ## Publish to PiPy 4 | 5 | See https://packaging.python.org/distributing/ 6 | 7 | 1. Update version in `gmssl.py` 8 | 2. Update version in `pyproject.toml` 9 | 3. Build package, run `python3 -m build` 10 | 4. Publish package to PiPy, run `python3 -m twine upload dist/*` 11 | 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GmSSL-Python 2 | 3 | ## 简介 4 | 5 | `gmssl-python`是GmSSL密码库 https://github.com/guanzhi/GmSSL 的Python语言封装,以`ctypes`方式实现,通过Python类和函数提供了如下密码接口: 6 | 7 | * 密码随机数生成器 8 | * SM2加密和签名,SM2密钥生成、私钥口令加密保护、密钥PEM文件导入导出 9 | * SM2数字证书的导入、解析和验证 10 | * SM3哈希函数、HMAC-SM3消息认证码、基于SM3的PBKDF2密钥导出函数 11 | * SM4分组加密,以及SM4的CBC、CTR、GCM三种加密模式 12 | * SM9加密和签名,以及SM9密钥生成、密钥口令加密保护、密钥PEM文件导入导出 13 | * ZUC序列密码加密 14 | 15 | 目前`gmssl-python`功能可以覆盖除SSL/TLS/TLCP之外的国密算法主要应用开发场景。 16 | 17 | ## 安装 18 | 19 | 由于`gmssl-python`以`ctypes`方式实现,因此所有密码功能都是通过调用本地安装的GmSSL动态库 (如`/usr/local/lib/libgmssl.so`)实现的,在安装和调用`gmssl-python`之前必须首先在系统上安装GmSSL,然后通过Python的包管理工具`pip`从Python代码仓库安装,或者从`gmssl-python`项目的代码仓库https://github.com/GmSSL/GmSSL-Python 下载最新的源代码,从本地安装。 20 | 21 | ### 安装GmSSL 22 | 23 | 首先在https://github.com/guanzhi/GmSSL 项目上下载最新的GmSSL代码[GmSSL-master.zip](https://github.com/guanzhi/GmSSL/archive/refs/heads/master.zip),编译并安装。GmSSL代码是C语言编写的,需要安装GCC、CMake来编译,在Ubuntu/Debian系统上可以执行 24 | 25 | ```bash 26 | sudo install build-essentials cmake 27 | ``` 28 | 29 | 安装依赖的编译工具,然后解压GmSSL源代码,进入源码目录`GmSSL-master`并执行如下指令: 30 | 31 | ```bash 32 | $ mkdir build 33 | $ cd build 34 | $ cmake .. 35 | $ make 36 | $ make test 37 | $ sudo make install 38 | ``` 39 | 40 | 安装完成后可以执行`gmssl`命令行工具检查是否安装完毕。 41 | 42 | ```bash 43 | $ gmssl help 44 | ``` 45 | 46 | 由于`gmssl-python`需要`libgmssl`动态库,因此GmSSL安装时不要改变配置,仅以静态库安装时`gmssl-python`是不可用的。安装后执行`gmssl`命令可能提示找不到动态库,在Ubuntu系统下可以执行`sudo ldconfig`来发现新安装的动态库,在CentOS系统上需要在`/etc/ld.so.conf`配置文件中将`libgmssl`动态库的目录`/usr/local/lib`加入到配置文件中。 47 | 48 | ### 从Python代码仓库安装`gmssl-python` 49 | 50 | `gmssl-python` 会定期发布到Python代码仓库中,可以通过`pip`工具安装 51 | 52 | ```bash 53 | $ pip install gmssl-python 54 | $ pip show gmssl-python 55 | ``` 56 | 57 | 通过`pip show`命令可以查看当前安装的`gmssl-python`的版本信息。 58 | 59 | ### 下载源码本地安装 60 | 61 | 从代码仓库中安装的`gmssl-python`通常不是最新版本,可以下载最新的GmSSL-Python代码 [GmSSL-Python-main.zip](https://github.com/GmSSL/GmSSL-Python/archive/refs/heads/main.zip),本地安装。 62 | 63 | 解压缩并进入源代码目录`GmSSL-Python-main`。由于最新代码可能还处于开发过程中,在安装前必须进行测试确保全部功能正确,`gmssl-python`中提供了测试,执行如下命令 64 | 65 | 运行测试 66 | 67 | ```bash 68 | $ python -m unittest -v 69 | ................ 70 | ---------------------------------------------------------------------- 71 | Ran 16 tests in 1.407s 72 | 73 | OK 74 | ``` 75 | 76 | 上面的输出表明测试通过。 77 | 78 | 然后可以通过`pip`命令安装当前目录下的代码 79 | 80 | ```bash 81 | $ pip install . 82 | $ pip show gmssl-python 83 | ``` 84 | 85 | ### 验证安装成功 86 | 87 | 注意`gmssl-python`包中只包含一个`gmssl`模块(而不是`gmssl_python`模块)。 88 | 89 | 可以在Python交互环境中做简单的测试 90 | 91 | ```python 92 | >>> import gmssl 93 | >>> gmssl.GMSSL_PYTHON_VERSION 94 | >>> gmssl.GMSSL_LIBRARY_VERSION 95 | ``` 96 | 97 | 分别查看当前`gmssl-python`的版本和`libgmssl`的版本。 98 | 99 | 编写一个简单的测试程序`sm3.py` 100 | 101 | ```python 102 | from gmssl import * 103 | 104 | sm3 = Sm3() 105 | sm3.update(b'abc') 106 | dgst = sm3.digest() 107 | print("sm3('abc') : " + dgst.hex()) 108 | ``` 109 | 110 | 执行这个程序 111 | 112 | ```bash 113 | $ python demo.py 114 | sm3('abc') : 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0 115 | ``` 116 | 117 | 可以看到运行成功。通过`gmssl`命令行验证输出是正确的 118 | 119 | ``` 120 | echo -n abc | gmssl sm3 121 | ``` 122 | 123 | 可以看到输出相同的SM3哈希值 124 | 125 | 126 | 127 | 128 | 129 | ## 开发手册 130 | 131 | ### 随机数生成器 132 | 133 | 函数`rand_bytes`实现随机数生成功能。 134 | 135 | ```python 136 | rand_bytes(size : int) -> bytes 137 | ``` 138 | 139 | 输入参数`size` 是输出字节数组长度,返回值为`size`长度的随机字节数组。 140 | 141 | 通过`rand_bytes`方法生成的是具备密码安全性的随机数,可以用于密钥、IV或者其他随机数生成器的随机种子。 142 | 143 | ```python 144 | >>> import gmssl 145 | >>> key = gmssl.rand_bytes(16) 146 | >>> print(key.hex()) 147 | ``` 148 | 149 | `rand_bytes`是通过调用操作系统的密码随机数生成器(如`/dev/urandom`)实现的。由于底层操作系统的限制,在一次调用`rand_bytes`时不要指定明显超过密钥长度的输出长度,例如参数`size`的值不要超过128,否则可能导致阻塞,或者产生错误和异常。如果应用需要大量的随机数据,不应使用`rand_bytes`,而是应该考虑其他伪随机数生成算法。 150 | 151 | 需要注意的是,`rand_bytes`的安全性依赖于底层的操作系统随机数生成器的安全性。在服务器、笔记本等主流硬件和Windows、Linux、Mac主流服务器、桌面操作系统环境上,当计算机已经启动并且经过一段时间的用户交互和网络通信后,`rand_bytes`可以输出高质量的随机数。但是在缺乏用户交互和网络通信的嵌入式设备中,`rand_bytes`返回的随机数可能存在随机性不足的问题,在这些特殊的环境中,开发者需要提前或在运行时检测`rand_bytes`是否能够提供具有充分的随机性。 152 | 153 | ### SM3哈希 154 | 155 | SM3密码杂凑函数可以将任意长度的输入数据计算为固定32字节长度的哈希值。 156 | 157 | 模块`gmssl`中包含如下SM3的常量 158 | 159 | * `SM3_DIGEST_SIZE` 即SM3哈希值的字节长度 160 | 161 | 类`Sm3`实现了SM3功能,类`Sm3`的对象是由构造函数生成的 162 | 163 | ``` 164 | gmssl.Sm3() 165 | ``` 166 | 167 | 对象sm3的方法: 168 | 169 | * `sm3.update(data : bytes)` 要哈希的消息是通过`update`方法输入的,输入`data`的数据类型是`bytes`类型,如果输入的数据是字符串,需要通过字符串的`encode`方法转换成`bytes`,否则无法生成正确的哈希值。 170 | * `sm3.digest() -> bytes` 在通过`update`输入完所有消息后,就可以通过`digest`方法获得输出的哈希值,输出的结果类型为`bytes`类型,长度为`SM3_DIGEST_SIZE`。 171 | * `sm3.reset()` 在SM3对象完成一个消息的哈希后,可以通过`reset`方法重置对象状态,效果等同于构造函数,重置后可以通过`update`、`digest`计算新一个消息的哈希值。`reset`方法使得应用可以只创建一个`Sm3`的对象,计算任意数量的哈希值。 172 | 173 | 下面的例子展示了如何通过类`Sm3`计算字符串的SM3哈希值。 174 | 175 | ```Python 176 | >>> from gmssl import * 177 | >>> sm3 = Sm3() 178 | >>> sm3.update(b'abc') 179 | >>> sm3.digest().hex() 180 | ``` 181 | 182 | 注意这里提供的消息字符串是`bytes`格式的。这个例子的源代码在`examples/sm3.py`文件中,编译并运行这个例子。 183 | 184 | ```bash 185 | $ python examples/sm3.py 186 | ``` 187 | 188 | 打印出的`66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0`就是字符串`abc`的哈希值。字符串`abc`的哈希值也是SM3标准文本中给出的第一个测试数据,通过对比标准文本可以确定这个哈希值是正确的。 189 | 190 | 也可以通过`gmssl`命令行来验证`Sm3`类的计算是正确的。 191 | 192 | ```bash 193 | $ echo -n abc | gmssl sm3 194 | 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0 195 | ``` 196 | 197 | 可以看到输出的结果是一样。 198 | 199 | 注意,如果将字符串`abc`写入到文本文件中,文本编辑器通常会在文本结尾处增加格外的结束符,如`0x0a`字符,那么计算出的哈希值将不是上面的结果,比如可能是`12d4e804e1fcfdc181ed383aa07ba76cc69d8aedcbb7742d6e28ff4fb7776c34`。如果命令`echo`不使用`-n`的参数,也会出现同样的错误。这是很多开发者在初次进行哈希函数开发时容易遇到的错误,哈希函数的安全性质保证,即使输入的消息只差一个比特,那么输出的哈希值也完全不同。 200 | 201 | 如果需要哈希的数据来自于网络或者文件,那么应用可能需要多次读取才能获得全部的数据。在通过`Sm3`计算哈希值时,应用不需要通过保存一个缓冲区来保存全部的数据,而是可以通过多次调用`update`方法,将数据输入给`Sm3`对象,在数据全都输入完之后,最后调用`digest`方法得到全部数据的SM3哈希值。下面的代码片段展示了这一用法。 202 | 203 | ```python 204 | >>> from gmssl import * 205 | >>> sm3 = Sm3() 206 | >>> sm3.update(b"Hello ") 207 | >>> sm3.update(b"world!") 208 | >>> dgst = sm3.digest() 209 | ``` 210 | 211 | 这个例子中两次调用了`update`方法,效果等同于 212 | 213 | ```python 214 | sm3.update(b"Hello world!"); 215 | ``` 216 | 217 | 注意,SM3算法也支持生成空数据的哈希值,因此下面的代码片段也是合法的。 218 | 219 | ```java 220 | >>> from gmssl import * 221 | >>> sm3 = Sm3() 222 | >>> dgst = sm3.digest() 223 | ``` 224 | 225 | GmSSL-Python其他类的`update`方法通常也都提供了这种形式的接口。在输入完所有的数据之后,通过调用`digest`方法就可以获得所有输入数据的SM3哈希值了。`digest`方法输出的是长度为`SM3_DIGEST_SIZE`字节(即32字节)的二进制哈希值。 226 | 227 | 如果应用要计算多组数据的不同SM3哈希值,可以通过`reset`方法重置`Sm3`对象的状态,然后可以再次调用`update`和`digest`方法计算新一组数据的哈希值。这样只需要一个`Sm3`对象就可以完成多组哈希值的计算。 228 | 229 | ```python 230 | >>> from gmssl import * 231 | >>> sm3 = Sm3() 232 | >>> sm3.update(b"abc") 233 | >>> dgst1 = sm3.digest() 234 | >>> 235 | >>> sm3.reset() 236 | >>> sm3.update(b"Hello ") 237 | >>> sm3.update(b"world!") 238 | >>> dgst2 = sm3.digest() 239 | ``` 240 | 241 | GmSSL-Python的部分其他类也提供了`reset`方法。 242 | 243 | ### HMAC-SM3消息认证码 244 | 245 | HMAC-SM3是基于SM3密码杂凑算法的消息认证码(MAC)算法,消息认证码算法可以看作带密钥的哈希函数,主要用于保护消息不受篡改。通信双方需要事先协商出一个密钥,比如32字节的随机字节序列,数据的发送方用这个密钥对消息计算MAC值,并且把MAC值附在消息后面。消息的接收方在收到消息后,用相同的密钥计算消息的MAC值,并且和发送消息附带的MAC值做对比,如果一致说明消息没有被篡改,如果不一致,说明消息被篡改了。 246 | 247 | 模块`gmssl`中包含如下Sm3Hmac的常量: 248 | 249 | * `SM3_HMAC_MIN_KEY_SIZE` 250 | * `SM3_HMAC_MAX_KEY_SIZE` 251 | * `SM3_HMAC_SIZE` HMAC-SM3密钥长度,与SM3哈希值的长度相等 252 | 253 | `Sm3Hmac`类实现了基于SM3的HMAC消息认证码算法,类`Sm3Hmac`的对象是由构造函数生成的。 254 | 255 | ``` 256 | gmssl.Sm3Hmac(key) 257 | ``` 258 | 259 | 对象Sm3Hmac的方法: 260 | 261 | * `Sm3Hmac.update(data : bytes)` 262 | * `Sm3Hmac.generate_mac() -> bytes` 263 | * `Sm3Hmac.reset()` 264 | 265 | HMAC-SM3算法可以看作是带密钥的SM3算法,因此在生成`Sm3Hmac`对象时需要传入一个密钥`key`作为输入参数。虽然HMAC-SM3在算法和实现上对密钥长度没有限制,但是出于安全性、效率等方面的考虑,HMAC-SM3算法的密钥长度建议采用32字节(等同于SM3哈希值的长度),不应少于16字节,采用比32字节更长的密钥长度会增加计算开销而不会增加安全性。 266 | 267 | 下面的例子显示了如何用HMAC-SM3生成消息`abc`的MAC值。 268 | 269 | ```python 270 | >>> from gmssl import * 271 | >>> key = rand_bytes(SM3_HMAC_MIN_KEY_SIZE) 272 | >>> sm3_hmac = Sm3Hmac(key) 273 | >>> sm3_hmac.update(b'abc') 274 | >>> sm3_hmac.generate_mac().hex() 275 | ``` 276 | 277 | `Sm3Hmac`也通过`update`方法来提供输入消息,应用可以多次调用`update`。 278 | 279 | 应用在通过`update`完成数据输入后,调用`generate_mac`可以获得消息认证码。 280 | 281 | ### 基于口令的密钥导出函数PBKDF2 282 | 283 | 常用软件如Word、PDF、WinRAR等支持基于口令的文件加密,字符串形式的口令相对于随机的密钥字节序列对用户来说更容易记忆和输入,对用户更加友好。但是由于口令中存在的信息熵远低于随机的二进制密钥,直接将口令字符串作为密钥,甚至无法抵御来自个人计算机的暴力破解攻击。一种典型的错误用法是直接用哈希函数计算口令的哈希值,将看起来随机的哈希值作为密钥使用。但是由于口令的空间相对较小,攻击者仍然可以尝试所有可能口令的哈希值,对于暴力破解来说,破解口令的哈希值和原始口令,在攻击难度上没有太大差别。 284 | 285 | 安全和规范的做法是采用一个基于口令的密钥导出函数(Password-Based Key Derivation Function, PBKDF)从口令中导出密钥。通过PBKDF导出密钥并不会降低攻击者在暴力破解时尝试的口令数量,但是可以防止攻击者通过查预计算表的方式来加速破解,并且可以大大增加攻击者尝试每一个可能口令的计算时间。PBKDF2是安全的并且使用广泛的PBKDF算法标准之一,算法采用哈希函数作为将口令映射为密钥的主要部件,通过加入随机并且公开的盐值(Salt)来抵御预计算,通过增加多轮的循环计算来增加在线破解的难度,并且支持可变的导出密钥长度。 286 | 287 | 模块`gmssl`中包含如下Sm3Pbkdf2的常量 288 | 289 | * `SM3_PBKDF2_MIN_ITER` 290 | * `SM3_PBKDF2_MAX_ITER` 291 | * `SM3_PBKDF2_MAX_SALT_SIZE` 292 | * `SM3_PBKDF2_DEFAULT_SALT_SIZE` 293 | * `SM3_PBKDF2_MAX_KEY_SIZE` 294 | 295 | 函数`Sm3Pbkdf2`实现了基于SM3的PBKDF2算法。 296 | 297 | ```python 298 | sm3_pbkdf2(passwd, salt, iterator, keylen) 299 | ``` 300 | 301 | 其中: 302 | 303 | - `passwd`用于导出密钥的用户口令。 304 | - `salt`是用于抵御与计算的盐值。这个值需要用随机生成(比如通过`Random`类),并且具有一定的长度。Salt值不需要保密,因此在口令加密数据时,可以直接将这个值附在密文前,传输给接收方。Salt值越长,抵御预计算攻击的效果就更好。例如当Salt为8字节(64比特)长的随机值时,攻击者预计算表就要扩大$2^{64}$倍。`Sm3Pbkdf2`提供一个推荐的Salt值长度`SM3_PBKDF2_DEFAULT_SALT_SIZE`常量,并且在实现上不支持超过`SM3_PBKDF2_MAX_KEY_SIZE`长度的Salt值。 305 | - `iterator`参数用于表示在导出密钥时调用SM3算法的循环次数,`iterator`值越大,暴力破解的难度越大,但是同时用户在调用这个函数时的开销也增大了。一般来说`iterator`值的应该选择在用户可接收延迟情况下的最大值,比如当`iterator = 10000`时,用户延迟为100毫秒,但是对于用户来说延迟感受不明显,但是对于暴力攻击者来说`iterator = 10000`意味着攻击的开销增加了大约1万倍。`Sm3Pbkdf2`通过`SM3_PBKDF2_MIN_ITER`和`SM3_PBKDF2_MAX_ITER`两个常量给出了`iterator`值的范围,用户可以根据当前计算机的性能及用户对延迟的可感知度,在这个范围内选择合适的值。 306 | - `keylen`参数表示希望导出的密钥长度,这个长度不可超过常量`SM3_PBKDF2_MAX_KEY_SIZE`。 307 | 308 | 下面的例子展示了如何从口令字符串导出一个密钥。 309 | 310 | ```python 311 | >>> from gmssl import * 312 | >>> passwd = "Password" 313 | >>> salt = rand_bytes(SM3_PBKDF2_DEFAULT_SALT_SIZE) 314 | >>> iterator = SM3_PBKDF2_MIN_ITER 315 | >>> keylen = 32 316 | >>> sm3_pbkdf2(passwd, salt, iterator, keylen).hex() 317 | ``` 318 | 319 | ### SM4分组密码 320 | 321 | SM4算法是分组密码算法,其密钥长度为128比特(16字节),分组长度为128比特(16字节)。SM4算法每次只能加密或者解密一个固定16字节长度的分组,不支持加解密任意长度的消息。分组密码通常作为更高层密码方案的一个组成部分,不适合普通上层应用调用。如果应用需要保护数据和消息,那么应该优先选择采用SM4-GCM模式,或者为了兼容已有的系统,也可以使用SM4-CBC或SM4-CTR模式。 322 | 323 | 模块`gmssl`中包含如下SM4的常量 324 | 325 | * `SM4_KEY_SIZE` 326 | * `SM4_BLOCK_SIZE` 327 | 328 | `SM4`类实现了基本的SM4分组密码算法,类`SM4`的对象是由构造函数生成的。 329 | 330 | ``` 331 | gmssl.Sm4(key, encrypt) 332 | ``` 333 | 334 | 对象SM4的方法: 335 | 336 | * `Sm4.encrypt(block : int) -> bytes` 337 | 338 | `Sm4`对象在创建时需要提供`SM4_KEY_SIZE`字节长度的密钥,以及一个布尔值`DO_ENCRYPT`表示是用于加密还是解密。方法`encrypt`根据创建时的选择进行加密或解密,每次调用`encrypt`只处理一个分组,即读入`SM4_BLOCK_SIZE`长度的输入。 339 | 340 | 下面的例子展示SM4分组加密 341 | 342 | ```python 343 | >>> from gmssl import * 344 | >>> key = rand_bytes(SM4_KEY_SIZE) 345 | >>> plaintext = rand_bytes(SM4_BLOCK_SIZE) 346 | >>> sm4_enc = Sm4(key, DO_ENCRYPT) 347 | >>> ciphertext = sm4_enc.encrypt(plaintext) 348 | >>> sm4_dec = Sm4(key, DO_DECRYPT) 349 | >>> decrypted = sm4_dec.encrypt(ciphertext) 350 | ``` 351 | 352 | 多次调用`Sm4`的分组加密解密功能可以实现ECB模式,由于ECB模式在消息加密应用场景中并不安全,因此GmSSL中没有提供ECB模式。如果应用需要开发SM4的其他加密模式,也可以基于`Sm4`类来开发这些模式。 353 | 354 | ### SM4-CBC加密模式 355 | 356 | CBC模式是应用最广泛的分组密码加密模式之一,虽然目前不建议在新的应用中继续使用CBC默认,为了保证兼容性,应用仍然可能需要使用CBC模式。 357 | 358 | 模块`gmssl`中包含如下Sm4Cbc的常量: 359 | 360 | * `SM4_CBC_IV_SIZE` 361 | 362 | `Sm4Cbc`类实现了基本的SM4-CBC分组密码算法,类`Sm4Cbc`的对象是由构造函数生成的。 363 | 364 | ``` 365 | gmssl.Sm4Cbc(key, iv, encrypt) 366 | ``` 367 | 368 | 对象Sm4Cbc的方法: 369 | 370 | * `Sm4Cbc.update(data : bytes)` 371 | * `Sm4Cbc.finish() -> bytes` 372 | 373 | `Sm4Cbc`类实现了SM4的带填充CBC模式,可以实现对任意长度数据的加密。由于需要对明文进行填充,因此`Sm4Cbc`输出的密文长度总是长于明文长度,并且密文的长度是整数个分组长度。 374 | 375 | 通过`Sm4Cbc`加密时,`key`和`iv`都必须为16字节长度。由于CBC模式中加密和解密的计算过程不同,因此必须通过布尔值`DO_ENCRYPT`指定是加密还是解密。 376 | 377 | 由于`Sm4Cbc`在加解密时维护了内部的缓冲区,因此`update`的输出长度可能不等于输入长度,应该保证输出缓冲区的长度至少比输入长度长一个`SM4_CBC_IV_SIZE`长度。 378 | 379 | 下面的例子显示了采用SM4-CBC加密和解密的过程。 380 | 381 | ```python 382 | >>> from gmssl import * 383 | >>> key = rand_bytes(SM4_KEY_SIZE) 384 | >>> iv = rand_bytes(SM4_CBC_IV_SIZE) 385 | >>> plaintext = b'abc' 386 | >>> sm4_enc = Sm4Cbc(key, iv, DO_ENCRYPT) 387 | >>> ciphertext = sm4_enc.update(plaintext) 388 | >>> ciphertext += sm4_enc.finish() 389 | >>> sm4_dec = Sm4Cbc(key, iv, DO_DECRYPT) 390 | >>> decrypted = sm4_dec.update(ciphertext) 391 | >>> decrypted += sm4_dec.finish() 392 | ``` 393 | 394 | ### SM4-CTR加密模式 395 | 396 | CTR加密模式可以加密任意长度的消息,和CBC模式不同,并不需要采用填充方案,因此SM4-CTR加密输出的密文长度和输入的明文等长。对于存储或传输带宽有限的应用场景,SM4-CTR相对SM4-CBC模式,密文不会增加额外长度。 397 | 398 | 模块`gmssl`中包含如下Sm4Ctr的常量: 399 | 400 | * `SM4_CTR_IV_SIZE` 401 | 402 | `Sm4Ctr`类实现了基本的SM4-CBC分组密码算法,类`Sm4Ctr`的对象是由构造函数生成的。 403 | 404 | ``` 405 | gmssl.Sm4Ctr(key, iv) 406 | ``` 407 | 408 | 对象Sm4Cbc的方法: 409 | 410 | * `Sm4Ctr.update(data : bytes)` 411 | * `Sm4Ctr.finish() -> bytes` 412 | 413 | SM4-CTR在加密和解密时计算过程一样,因此在初始化时不需要指定加密或解密,因此没有`Sm4Cbc`中的`DO_ENCRYPT`参数。其他过程和SM4-CBC是一样的。 414 | 415 | 由于`Sm4Ctr`在加解密时维护了内部的缓冲区,因此`update`的输出长度可能不等于输入长度,应该保证输出缓冲区的长度至少比输入长度长一个`SM4_BLOCK_SIZE`长度。 416 | 417 | 注意 ,SM4-CBC和SM4-CTR模式都不能保证消息的完整性,在使用这两个模式时,应用还需要生成一个独立的HMAC-SM3密钥,并且生成密文的MAC值。 418 | 419 | ### SM4-GCM认证加密模式 420 | 421 | SM4的GCM模式是一种认证加密模式,和CBC、CTR等加密模式的主要区别在于,GCM模式的加密过程默认在密文最后添加完整性标签,也就是MAC标签,因此应用在采用SM4-GCM模式时,没有必要再计算并添加SM3-HMAC了。在有的应用场景中,比如对消息报文进行加密,对于消息头部的一段数据(报头字段)只需要做完整性保护,不需要加密,SM4-GCM支持这种场景。在`Sm4Gcm`类的`init`方法中,除了`key`、`iv`参数,还可以提供`aad`字节数字用于提供不需要加密的消息头部数据。 422 | 423 | 模块`gmssl`中包含如下Sm4Gcm的常量: 424 | 425 | * `SM4_GCM_MIN_IV_SIZE` 426 | * `SM4_GCM_MAX_IV_SIZE` 427 | * `SM4_GCM_DEFAULT_IV_SIZE` 428 | * `SM4_GCM_DEFAULT_TAG_SIZE` 429 | * `SM4_GCM_MAX_TAG_SIZE ` 430 | 431 | `Sm4Gcm`类实现了基本的SM4-CBC分组密码算法,类`Sm4Gcm`的对象是由构造函数生成的。 432 | 433 | ``` 434 | gmssl.Sm4Gcm(key, iv, aad, taglen = SM4_GCM_DEFAULT_TAG_SIZE, encrypt = True) 435 | ``` 436 | 437 | 对象Sm4Gcm的方法: 438 | 439 | * `Sm4Gcm.update(data : bytes)` 440 | * `Sm4Gcm.finish() -> bytes` 441 | 442 | GCM模式和CBC、CTR、HMAC不同之处还在于可选的IV长度和MAC长度,其中IV的长度必须在`SM4_GCM_MIN_IV_SIZE`和`SM4_GCM_MAX_IV_SIZE`之间,长度为`SM4_GCM_DEFAULT_IV_SIZE`有最佳的计算效率。MAC的长度也是可选的,通过`init`方法中的`taglen`设定,其长度不应低于8字节,不应长于`SM4_GCM_DEFAULT_TAG_SIZE = 16`字节。 443 | 444 | 下面例子展示SM4-GCM加密和解密的过程。 445 | 446 | ```python 447 | >>> from gmssl import * 448 | >>> key = rand_bytes(SM4_KEY_SIZE) 449 | >>> iv = rand_bytes(SM4_GCM_DEFAULT_IV_SIZE) 450 | >>> aad = b'Additional auth-data' 451 | >>> plaintext = b'abc' 452 | >>> taglen = SM4_GCM_DEFAULT_TAG_SIZE 453 | >>> sm4_enc = Sm4Gcm(key, iv, aad, taglen, DO_ENCRYPT) 454 | >>> ciphertext = sm4_enc.update(plaintext) 455 | >>> ciphertext += sm4_enc.finish() 456 | >>> sm4_dec = Sm4Gcm(key, iv, aad, taglen, DO_DECRYPT) 457 | >>> decrypted = sm4_dec.update(ciphertext) 458 | >>> decrypted += sm4_dec.finish() 459 | ``` 460 | 461 | 通过上面的例子可以看出,SM4-GCM加密模式中可以通过指定了一个不需要加密的字段`aad`,注意`aad`是不会在`update`中输出的。由于GCM模式输出个外的完整性标签,因此`update`和`finish`输出的总密文长度会比总的输入明文长度多`taglen`个字节。 462 | 463 | ### Zuc序列密码 464 | 465 | 祖冲之密码算法(ZU Cipher, ZUC)是一种序列密码,密钥和IV长度均为16字节。作为序列密码ZUC可以加密可变长度的输入数据,并且输出的密文数据长度和输入数据等长,因此适合不允许密文膨胀的应用场景。在国密算法体系中,ZUC算法的设计晚于SM4,在32位通用处理器上通常比SM4-CBC明显要快。 466 | 467 | 在安全性方面,不建议在一组密钥和IV的情况下用ZUC算法加密大量的数据(比如GB级或TB级),避免序列密码超长输出时安全性降低。另外ZUC算法本身并不支持数据的完整性保护,因此在采用ZUC算法加密应用数据时,应考虑配合HMAC-SM3提供完整性保护。ZUC的标准中还包括针对移动通信底层数据报文加密的128-EEA3方案和用于消息完整性保护的128-EIA3算法,目前GmSSL-Python中不支持这两个算法。 468 | 469 | 模块`gmssl`中包含如下Sm4Gcm的常量: 470 | 471 | * `ZUC_KEY_SIZE` 472 | * `ZUC_IV_SIZE` 473 | 474 | `Zuc`类实现了基本的Zuc序列密码算法,类`Zuc`的对象是由构造函数生成的。 475 | 476 | ``` 477 | gmssl.Zuc(key, iv) 478 | ``` 479 | 480 | 对象Sm4Cbc的方法: 481 | 482 | * `Zuc.update(data : bytes)` 483 | * `Zuc.finish() -> bytes` 484 | 485 | `Zuc`类的接口说明如下: 486 | 487 | - 序列密码通过生成密钥序列和输入数据进行异或操作的方式来加密或解密,因此序列密码的加密和解密的过程一致,因此创建`Zuc`对象时不需要格外的参数表明加密还是解密。 488 | - 由于CTR模式实际上是以分组密码实现了序列密码的能力,因此可以发现`Zuc`和`Sm4Cbc`的接口是完全一致的。 489 | - ZUC算法内部实现是以32比特字(4字节)为单位进行处理,因此`Zuc`实现加解密过程中也有内部的状态缓冲区,因此`update`的输出长度可能和输入长度不一致,调用方应该保证输出缓冲区长度比输入长度长`BLOCK_SIZE`个字节。注意,`BLOCK_SIZE`的实际值在未来也有可能会变化。 490 | 491 | 下面的例子展示了`Zuc`的加密和解密过程。 492 | 493 | ```python 494 | >>> from gmssl import * 495 | >>> iv = rand_bytes(ZUC_IV_SIZE) 496 | >>> plaintext = b'abc' 497 | >>> zuc_enc = Zuc(key, iv) 498 | >>> ciphertext = zuc_enc.update(plaintext) 499 | >>> ciphertext += zuc_enc.finish() 500 | >>> zuc_dec = Zuc(key, iv) 501 | >>> decrypted = zuc_dec.update(ciphertext) 502 | >>> decrypted += zuc_dec.finish() 503 | ``` 504 | 505 | ### SM2 506 | 507 | SM2是国密标准中的椭圆曲线公钥密码,包含数字签名算法和公钥加密算法。SM2相关的功能由类`Sm2Key`和`Sm2Signature`实现,其中`Sm2Key`实现了SM2密钥对的生成、基础的加密和签名方案,`Sm2Signature`类实现了对任意长度消息签名的签名方案。 508 | 509 | 模块`gmssl`中包含如下Sm2Key的常量: 510 | 511 | * `SM2_DEFAULT_ID` 512 | * `SM2_MAX_SIGNATURE_SIZE` 513 | * `SM2_MIN_PLAINTEXT_SIZE` 514 | * `SM2_MAX_PLAINTEXT_SIZE ` 515 | * `SM2_MIN_CIPHERTEXT_SIZE` 516 | * `SM2_MAX_CIPHERTEXT_SIZE` 517 | 518 | `Sm2Key`类实现了基本的SM4-CBC分组密码算法,类`Sm2Key`的对象是由构造函数生成的。 519 | 520 | ``` 521 | gmssl.Sm2Key() 522 | ``` 523 | 524 | 对象Sm2Key的方法: 525 | 526 | * `Sm2Key.generate_key()` 527 | * `Sm2Key.compute_z()` 528 | * `Sm2Key.export_encrypted_private_key_info_pem()` 529 | * `Sm2Key.import_encrypted_private_key_info_pem()` 530 | * `Sm2Key.export_public_key_info_pem()` 531 | * `Sm2Key.import_public_key_info_pem()` 532 | * `Sm2Key.sign()` 533 | * `Sm2Key.verify()` 534 | * `Sm2Key.encrypt()` 535 | * `Sm2Key.decrypt()` 536 | 537 | 需要注意的是,通过构造函数生成的新`Sm2Key`对象是一个空白的对象,可以通过`generate_key`方法生成一个新的密钥对,或者通过导入函数从外部导入密钥。`Sm2Key`一共提供了2个不同的导入方法: 538 | 539 | - `import_encrypted_private_key_info_pem` 从加密的PEM文件中导入SM2私钥,因此调用时需要提供PEM文件的路径和解密的口令(Password)。 540 | - `import_public_key_info_pem`从PEM文件中导入SM2公钥,只需要提供文件的路径,不需要提供口令。 541 | 542 | 上面2个导入函数也都有对应的导出函数。从PEM文件中导入导出公钥私钥和`gmssl`命令行工具的默认密钥格式一致,并且在处理私钥时安全性更高。因此建议在默认情况下,在导入导出私钥时默认采用加密的PEM文件格式。 543 | 544 | 下面的代码片段展示了`Sm2Key`密钥对和导出为加密的PEM私钥文件: 545 | 546 | ```python 547 | >>> sm2 = Sm2Key() 548 | >>> sm2.generate_key() 549 | >>> 550 | >>> sm2.export_encrypted_private_key_info_pem('sm2.pem', 'password') 551 | >>> private_key = Sm2Key() 552 | >>> private_key.import_encrypted_private_key_info_pem('sm2.pem', 'password') 553 | ``` 554 | 555 | 用文本编辑器打开`sm2.pem`文件可以看到如下内容 556 | 557 | ``` 558 | -----BEGIN ENCRYPTED PRIVATE KEY----- 559 | MIIBBjBhBgkqhkiG9w0BBQ0wVDA0BgkqhkiG9w0BBQwwJwQQaADudE4Ycenuoth4 560 | ZqcewgIDAQAAAgEQMAsGCSqBHM9VAYMRAjAcBggqgRzPVQFoAgQQ9aUmOaXn0mZD 561 | 7xhBdd+FlQSBoKc0GG7US2SsmQIrppPNQeyDFpG8xthNI6G4R/YbSPJCvSMJ/9y3 562 | LQ/jrdUumuKevgg9miAcjbKndm7HC07lMYUk1ZXlaEG/1awER4RJsRvZ64GlBQOV 563 | D7jbu93mSs9t3SDt4TniDua5WyXo5Y8S6DjkkUD5epHRzYZ4uFFC/8pTeehK7X+S 564 | p2b6CndfB6H4LrvCGuRnjX4l5Q5AgfWDmWU= 565 | -----END ENCRYPTED PRIVATE KEY----- 566 | ``` 567 | 568 | 下面的代码片段展示了`Sm2Key`导出为PEM公钥文件,这是一个标准的PKCS #8 EncryptPrivateKeyInfo类型并且PEM编码的私钥文件格式,`openssl pkeyutil`命令行工具也默认采用这个格式的私钥,但是由于GmSSL在私钥文件中采用SM4-CBC、HMAC-SM3组合加密了SM2的私钥,因此对于默认使用3DES的`openssl`等工具可能无法解密这个私钥(即使这个工具包含SM2算法的实现)。 569 | 570 | ```python 571 | >>> sm2.export_public_key_info_pem('sm2pub.pem') 572 | >>> public_key = Sm2Key() 573 | >>> public_key.import_public_key_info_pem('sm2pub.pem') 574 | ``` 575 | 576 | 用文本编辑器打开`sm2pub.pem`文件可以看到如下内容 577 | 578 | ``` 579 | -----BEGIN PUBLIC KEY----- 580 | MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE5djp+Gw/Wdg9JwVwYDiQn1AocezI 581 | C2qT54fqJBNWevCNru8ENwj4t/52Yf50LF5+fMlcoWPbfm/TcCgYPb49jw== 582 | -----END PUBLIC KEY----- 583 | ``` 584 | 585 | 由于公钥文件是不加密的,因此这个公钥可以被支持SM2的第三方工具、库打开和访问。 586 | 587 | `Sm2Key`类除了`generate_key`方法之外,提供了`compute_z`、`sign`、`verify`、`encrypt`、`decrypt`这几个密码计算相关的方法。 588 | 589 | 其中`compute_z`是由公钥和用户的字符串ID值计算出一个称为“Z值”的哈希值,用于对消息的签名。由于`Sm2Signature`类中提供了SM2消息签名的完整功能,因此这个`compute_z`方法只是用于实验验证。 590 | 591 | ```python 592 | >>> z = public_key.compute_z(SM2_DEFAULT_ID) 593 | ``` 594 | 595 | 类`Sm2Key`的`sign`和`verify`方法实现了SM2签名的底层功能,这两个方法不支持对数据或消息的签名,只能实现对SM3哈希值的签名和验证,并没有实现SM2签名的完整功能。应用需要保证调用时提供的`dgst`参数的字节序列长度为32。只有密码协议的底层开发者才需要调用`compute_z`、`sign`、`verify`这几个底层方法。 596 | 597 | ```python 598 | >>> dgst = sm3.digest() 599 | >>> sig = private_key.sign(dgst) 600 | >>> ret = public_key.verify(dgst, sig) 601 | ``` 602 | 603 | 类`Sm2Key`的`encrypt`和`decrypt`方法实现了SM2加密和解密功能。注意,虽然SM2标准中没有限制加密消息的长度,但是公钥加密应该主要用于加密较短的对称密钥、主密钥等密钥数据,因此GmSSL库中限制了SM2加密消息的最大长度。应用在调用`encrypt`时,需要保证输入的明文长度不超过`SM2_MAX_PLAINTEXT_SIZE `的限制。如果需要加密引用层的消息,应该首先生成对称密钥,用SM4-GCM加密消息,再用SM2加密对称密钥。 604 | 605 | ```python 606 | >>> ciphertext = public_key.encrypt(plaintext) 607 | >>> decrypted = private_key.decrypt(ciphertext) 608 | ``` 609 | 610 | 类`Sm2Signatue`提供了对任意长消息的签名、验签功能。 611 | 612 | 模块`gmssl`中包含如下Sm2Signatue的常量: 613 | 614 | * `DO_ENCRYPT = True` 615 | * `DO_DECRYPT = False` 616 | * `DO_SIGN = True` 617 | * `DO_VERIFY = False` 618 | 619 | `Sm2Signatue`类实现了基本的SM4-CBC分组密码算法,类`Sm2Signatue`的对象是由构造函数生成的。 620 | 621 | ``` 622 | gmssl.Sm2Signatue(sm2_key, signer_id = SM2_DEFAULT_ID, sign = DO_SIGN) 623 | ``` 624 | 625 | 对象Sm2Signatue的方法: 626 | 627 | * `Sm2Signatue.update()` 628 | * `Sm2Signatue.sign()` 629 | * `Sm2Signatue.verify()` 630 | 631 | 在生成`Sm2Signature`对象时,不仅需要提供`Sm2Key`,还需要提供签名方的字符串ID,以满足SM2签名的标准。如果提供的`Sm2Key`来自于导入的公钥,那么这个`Sm2Signature`对象只能进行签名验证操作,即在构造时`DO_SIGN = False`,并且只能调用`verify`方法,不能调用`sign`方法。 632 | 633 | ```python 634 | signer = Sm2Signature(private_key, SM2_DEFAULT_ID, DO_SIGN) 635 | signer.update(b'abc') 636 | sig2 = signer.sign() 637 | 638 | verifier = Sm2Signature(public_key, SM2_DEFAULT_ID, DO_VERIFY) 639 | verifier.update(b'abc') 640 | ret = verifier.verify(sig2) 641 | ``` 642 | 643 | 不管是`Sm2Key`的`sign`还是`Sm2Signature`的`sign`方法输出的都是DER编码的签名值。这个签名值的第一个字节总是`0x30`,并且长度是可变的,常见的长度包括70字节、71字节、72字节,也可能短于70字节。一些SM2的实现不能输出DER编码的签名,只能输出固定64字节长度的签名值。可以通过签名值的长度以及首字节的值来判断SM2签名值的格式。 644 | 645 | ### SM2数字证书 646 | 647 | 类`Sm2Certificate`实现了SM2证书的导入、导出、解析和验证等功能。这里的“SM2证书”含义和“RSA证书”类似,是指证书中的公钥字段是SM2公钥,证书中签名字段是SM2签名,证书格式就是标准的X.509v3证书。由于GmSSL库目前只支持SM2签名算法,不支持ECDSA、RSA、DSA等签名算法,因此`Sm2Certificate`类无法支持其他公钥类型的证书。注意,有一种不常见的情况,一个证书可以公钥是SM2公钥而数字签名是RSA签名,这种证书可能是采用RSA公钥的CA中心对SM2证书请求签发而产生的,由于目前GmSSL不支持SM2之外的签名算法,因此`Sm2Certificate`不支持此类证书。 648 | 649 | 类`Sm2Certificate`只支持SM2证书的解析和验证等功能,不支持SM2证书的签发和生成,如果应用需要实现证书申请(即生成CSR文件)或者自建CA签发证书功能,那么可以通过GmSSL库或者`gmssl`命令行工具实现,GmSSL-Python目前不考虑支持证书签发、生成的相关功能。 650 | 651 | 模块`gmssl`中包含如下Sm2Certificate的常量: 652 | 653 | * `ZUC_KEY_SIZE` 654 | * `ZUC_IV_SIZE` 655 | 656 | Sm2Certificate的方法: 657 | 658 | * `Sm2Certificate.import_pem()` 659 | * `Sm2Certificate.get_raw()` 660 | * `Sm2Certificate.export_pem()` 661 | * `Sm2Certificate.get_serial_number()` 662 | * `Sm2Certificate.get_issuer()` 663 | * `Sm2Certificate.get_subject()` 664 | * `Sm2Certificate.get_subject_public_key()` 665 | * `Sm2Certificate.get_validity()` 666 | * `Sm2Certificate.verify_by_ca_certificate()` 667 | 668 | 新生成的`Sm2Certificate`对象中的证书数据为空,必须通过导入证书数据才能实现真正的初始化。证书有很多种不同格式的编码,如二进制DER编码的`crt`文件或者文本PEM编码的`cer`文件或者`pem`文件,有的证书也会把二进制的证书数据编码为一串连续的十六进制字符串,也有的CA会把多个证书构成的证书链封装在一个PKCS#7格式的密码消息中,而这个密码消息可能是二进制的,也可能是PEM编码的。 669 | 670 | 在这些格式中最常用的格式是本文的PEM格式,这也是`Sm2Certificate`类默认支持的证书格式。下面这个例子中就是一个证书的PEM文件内容,可以看到内容是由文本构成的,并且总是以`-----BEGIN CERTIFICATE-----`一行作为开头,以`-----END CERTIFICATE-----`一行作为结尾。PEM格式的好处是很容易用文本编辑器打开来,容易作为文本被复制、传输,一个文本文件中可以依次写入多个证书,从而在一个文件中包含多个证书或证书链。因此PEM格式也是CA签发生成证书使用的最主流的格式。由于PEM文件中头尾之间的文本就是证书二进制DER数据的BASE64编码,因此PEM文件也很容易和二进制证书进行手动或自动的互相转换。 671 | 672 | ``` 673 | -----BEGIN CERTIFICATE----- 674 | MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG 675 | EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw 676 | MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO 677 | UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE 678 | MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT 679 | V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti 680 | W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ 681 | MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b 682 | 53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI 683 | pDoiVhsLwg== 684 | -----END CERTIFICATE----- 685 | ``` 686 | 687 | 通过`gmssl certparse`命令可以打印这个证书的内容 688 | 689 | ```python 690 | $ gmssl certparse -in ROOTCA.pemCertificate 691 | tbsCertificate 692 | version: v3 (2) 693 | serialNumber: 69E2FEC0170AC67B 694 | signature 695 | algorithm: sm2sign-with-sm3 696 | parameters: NULL 697 | issuer 698 | countryName: CN 699 | organizationName: NRCAC 700 | commonName: ROOTCA 701 | validity 702 | notBefore: Sat Jul 14 11:11:59 2012 703 | notAfter: Mon Jul 7 11:11:59 2042 704 | subject 705 | countryName: CN 706 | organizationName: NRCAC 707 | commonName: ROOTCA 708 | subjectPulbicKeyInfo 709 | algorithm 710 | algorithm: ecPublicKey 711 | namedCurve: sm2p256v1 712 | subjectPublicKey 713 | ECPoint: 0430F09C6BAA6681C721B137F652705E2FDAEDA789F0FA2B64D4ACEB99B9EAA34E655309309562BEE0E22BB45740AA745357B43DBF586D92FE364EC22EB73775DB 714 | extensions 715 | Extension 716 | extnID: AuthorityKeyIdentifier (2.5.29.35) 717 | AuthorityKeyIdentifier 718 | keyIdentifier: 4C32B197D9331BC4A605C1C6E58B625BF0977658 719 | Extension 720 | extnID: BasicConstraints (2.5.29.19) 721 | BasicConstraints 722 | cA: true 723 | Extension 724 | extnID: KeyUsage (2.5.29.15) 725 | KeyUsage: keyCertSign,cRLSign 726 | Extension 727 | extnID: SubjectKeyIdentifier (2.5.29.14) 728 | SubjectKeyIdentifier: 4C32B197D9331BC4A605C1C6E58B625BF0977658 729 | signatureAlgorithm 730 | algorithm: sm2sign-with-sm3 731 | parameters: NULL 732 | signatureValue: 304502201B56D22DE397A77A01F07EDBE775BE08A38F9763E49E6584ABF94C86D9F6E479022100DA1C3816C5616D9C2AC18C7D7AFD6DC4CE7EFF53F563A39C48A43A22561B0BC2 733 | ``` 734 | 735 | 可以看到一个证书的主要内容是包含证书持有者信息的tbsCertificate字段,以及权威机构对tbsCertificate字段的签名算法signatureAlgorithm和签名值signatureValue。因为这个证书是SM2证书,因此其中的签名算法是`sm2sign-with-sm3`,签名值是`0x30`开头的DER编码的可变长度签名值。 736 | 737 | 证书中持有者信息包含如下字段: 738 | 739 | - 证书格式的版本号 version,目前版本号应该是第3版,即`v3`。 740 | - 证书的序列号 serialNumber,早期证书中的序列号是一个递增的整数,但是近年来的证书必须是随机值。、 741 | - 证书的签名算法 signature,这个字段的值必须和最后的signatureAlgorithm保持一致。 742 | - 证书签发机构的名字 issuer,通常是一个CA中心,issuer的内容是由多个Key-Value格式的多个字段组合而成,其中的Key包括国家countryName、省stateOrProvinceName、城市localityName、组织organizationName、组织内单位organizationUnitName、常用名commonName等,其中commonName应该是CA机构的名字。 743 | - 证书的有效期 validity,有效期是由起始时间notBefore和终止时间notAfter两个时间构成的,如果当前时间早于notBefore,说明证书还没有启用,如果当前时间晚于notAfter,说明证书已经过期作废。 744 | - 证书持有者(证书主体)的名字 subject,这个字段的数据类型和issuer是一样的,一般对于网站服务器证书来说,subject的commonName应该是服务器的域名。 745 | - 证书持有者的公钥信息subjectPulbicKeyInfo,对于SM2证书来说,公钥算法必须是ecPublicKey并且曲线必须是sm2p256v1,公钥的值是一个编码的椭圆曲线点,这个值总是以`0x04`开头,后跟总共64字节的点的X、Y坐标。 746 | - 证书中通常还有多个扩展,其中有的扩展是关键的(critical)扩展,有些则不重要,只是提供了参考信息,这里介绍两个比较重要的扩展: 747 | - BasicConstraints (2.5.29.19) 扩展,这个扩展标明证书是权威机构的CA证书(比如北京市CA中心)还是普通用户的证书(比如某个网站的证书),如果一个证书中没有包含这个扩展,或者扩展中的`cA: true`字段不存在,那么这个证书不能作为CA证书使用。 748 | - KeyUsage (2.5.29.15) 扩展,这个扩展表明证书持有者公钥的用途,类似于驾驶证中的A照、B照、C照等划分大客车、大货车、小客车准驾车型,密钥用途表明证书是否可以签名、加密、签发证书等用途。如果一个数字签名附带的证书中有KeyUsage扩展并且扩展包含的密钥用途只有加密,没有签名,那么这个证书对于这个签名来说就是无效的。 749 | 750 | `Sm2Certificate`类只支持第3版证书的解析,因此没有提供`getVersion`方法获取证书的版本号。GmSSL支持常用扩展的解析和验证,如果某个证书中有GmSSL不支持的非关键扩展,那么GmSSL会忽略这个扩展,如果存在GmSSL不识别或无法验证的关键性扩展,那么GmSSL在解析证书的时候会返回失败,因此如果`Sm2Certificate`类`import_pem`成功,说明证书的格式、内容是可以识别的并且是正确的。 751 | 752 | 拿他其他人提供的证书还必须验证该证书是否有效,首先需要检查证书的有效期。目前很多CA中心的策略是颁发有效期尽可能短的证书(比如3个月有效期),因此拿到的证书很有可能已经过期了。可以通过`get_validity()`方法获得有效期时间,判断当前时间点是否在有效期范围内。如果要验证过去某个时间点证书支持者的操作是否合法,那么应该检查那个时间点是否在证书的有效期范围内。 753 | 754 | 对证书最重要的验证之一是这个证书是否是由权威机构签发的。证书用户需要先通过`get_issuer`方法获得签发机构的名字,确认这个签发机构是否可信。例如,如果一个北京市政府机构的证书中的签发机构是一个商业性CA中心,那么这个证书的有效性就是存疑的。在确认CA中心名字(即整个issuer字段)无误之后,还需要通过Issuer字段从可信的渠道获得这个CA中心的证书,然后调用`verify_by_ca_certificate`方法,用获得的CA证书验证当前证书中的签名是否正确。在典型的应用中,开发者和软件发行方应该将所有可信的CA中心的证书硬编码到软件中,或者内置到软件或系统的证书库中,避免应用的用户需要手动添加、导入CA证书。 755 | 756 | 所有的私钥都有泄露的可能,安全性不佳的自建CA有被攻击者渗透的可能,商业性的小CA甚至有被收购、收买的可能,因此有效期范围内的证书也存在被作废的可能。检查证书是否作废主要是通过证书作废列表CRL文件检查,或者通过证书状态在线检查协议OCSP来在线查询。目前`Sm2Certificate`类没有支持证书作为查询的功能,开发者暂时可以通过`GmSSL`库或者`gmssl`命令行工具进行CRL的检查。 757 | 758 | 在完成所有证书检查之后,应用可以完全信任从证书中读取的持有者身份信息(subject)和支持有的公钥了,这两个信息分别通过`get_subject()`和`get_subject_public_key`方法获得。 759 | 760 | ### SM9基于身份的密码 761 | 762 | SM9算法属于基于身份的密码。基于身份的密码是一种“高级”的公钥密码方案,在具备常规公钥密码加密、签名等密码功能的同时,基于身份的密码体系不需要CA中心和数字证书体系。SM9方案的基本原理是,可以由用户的唯一身份ID(如对方的电子邮件地址、域名或ID号等),从系统的全局主密钥中导出对应的私钥或公钥,导出密钥的正确性是由算法保证的,因此在进行加密、验签的时候,只需要获得解密方或签名方的ID即可,不再需要对方的数字证书了。因此如果应用面对的是一个内部的封闭环境,所有参与用户都是系统内用户,那么采用SM9方案而不是SM2证书和CA的方案,可以简化系统的开发、设计和使用,并降低后续CA体系的维护成本。 763 | 764 | 对应数字证书体系中的CA中心,SM9体系中也存在一个权威中心,用于生成全局的主密钥(MasterKey),并且为系统中的每个用户生成、分配用户的私钥。和SM2密钥对一样,SM9的主密钥也包含私钥和公钥,其中主公钥(PublicMasterKey)是可以导出并公开给系统中全体用户的。而SM9中用户的密钥对比较特殊,其中的公钥并不能从私钥中导出,SM9用户密钥需要包含用户的ID起到公钥的作用,在加密和验证签名等密码计算中,真正的用户公钥是在计算中,在运行时通过用户ID从主公钥中导出的。因此从应用的角度看,SM9中用户的公钥就是一个字符串形式的ID。 765 | 766 | SM9算法体系中包括SM9加密、SM9签名和SM9密钥交换协议,GmSSL-Java中实现了SM9加密和SM9签名,没有实现SM9密钥交换。其中SM9加密功能包含`Sm9EncMasterKey`类和`Sm9EncKey`类,分别实现了SM9加密主密钥和SM9加密用户密钥,SM9签名功能包含`Sm9SignMasterKey`类、`Sm9SignKey`类和`Sm9Signature`类,分别实现了SM9签名主密钥、SM9签名用户密钥和SM9签名功能。 767 | 768 | 和SM2算法中相同的密钥对既可以用于加密又可以用于签名不同,SM9中加密、签名的主密钥、用户密钥的组成是完全不同的,因此GmSSL中分别实现为不同的类。SM9签名由于需要特殊的哈希过程,因此SM9用户签名私钥不提供直接签哈希值的底层签名功能实现,只能通过`Sm9Signature`实现对消息的签名、验证。 769 | 770 | 模块`gmssl`中包含如下Sm9EncMasterKey的常量: 771 | 772 | * `SM9_MAX_ID_SIZE` 773 | * `SM9_MAX_PLAINTEXT_SIZE` 774 | * `SM9_MAX_CIPHERTEXT_SIZE` 775 | 776 | SM9加密主密钥由类`Sm9EncMasterKey`实现。 777 | 778 | ``` 779 | gmssl.Sm9EncMasterKey() 780 | ``` 781 | 782 | 对象Sm9EncMasterKey的接口包括: 783 | 784 | * `Sm9EncMasterKey.generate_master_key()` 主密钥的生成 785 | * `Sm9EncMasterKey.extract_key()`用户私钥的生成 786 | * `Sm9EncMasterKey.import_encrypted_master_key_info_pem()` 主密钥的导入,注意`Sm2Key`的对应接口类似,这里主密钥都是以口令加密的方式导出到文件上的 787 | * `Sm9EncMasterKey.export_encrypted_master_key_info_pem()`主密钥的导出 788 | * `Sm9EncMasterKey.export_public_master_key_pem()`主公钥(主密钥的公钥部分)的导入 789 | * `Sm9EncMasterKey.import_public_master_key_pem()`主公钥(主密钥的公钥部分)的导出 790 | * `Sm9EncMasterKey.encrypt()`数据加密 791 | 792 | 这个类的用户包括两个不同角色,权威中心和用户。其中权威中心调用主密钥的生成、主密钥的导入导出、主公钥导出和用户私钥生成这几个接口,而用户调用主公钥导入和加密这两个接口。 793 | 794 | 类`Sm9EncKey`对象是由`Sm9SEncMasterKey`的`extract_key`方法生成的。 795 | 796 | ``` 797 | gmssl.Sm9EncKey() 798 | ``` 799 | 800 | 对象Sm9EncKey的方法: 801 | 802 | * `Sm9EncKey.get_id()` 803 | * `Sm9EncKey.import_encrypted_private_key_info_pem()` 804 | * `Sm9EncKey.export_encrypted_private_key_info_pem()` 805 | * `Sm9EncKey.decrypt()` 806 | 807 | 类`Sm9EncKey`提供了解密、导入导出等接口,由于在SM9中用户密钥总是包含私钥的,因此导出的是经过口令加密的密钥。 808 | 809 | 下面的例子中给出了SM9加密方案的主密钥生成、用户密钥导出、加密、解密的整个过程。 810 | 811 | ```python 812 | master_key = Sm9EncMasterKey() 813 | master_key.generate_master_key() 814 | print("SM9 master key generated") 815 | 816 | master_key.export_encrypted_master_key_info_pem('enc_msk.pem', 'password') 817 | master_key.export_public_master_key_pem('enc_mpk.pem') 818 | print("Export master key and public master key") 819 | 820 | # Encrypt 821 | master_pub = Sm9EncMasterKey() 822 | master_pub.import_public_master_key_pem('enc_mpk.pem') 823 | 824 | plaintext = rand_bytes(SM4_KEY_SIZE + SM3_HMAC_MIN_KEY_SIZE) 825 | 826 | receiver_id = 'Alice' 827 | 828 | ciphertext = master_pub.encrypt(plaintext, receiver_id) 829 | 830 | # Decrypt 831 | master = Sm9EncMasterKey() 832 | master.import_encrypted_master_key_info_pem('enc_msk.pem', 'password') 833 | 834 | receiver_key = master.extract_key(receiver_id) 835 | 836 | decrypted = receiver_key.decrypt(ciphertext) 837 | ``` 838 | 839 | SM9签名功能由`Sm9SignMasterKey`、`Sm9SignKey`和`Sm9Signature`几个类实现,前两者在接口上和SM9加密非常类似,只是这两个类不直接提供签名、验签的功能。 840 | 841 | ```python 842 | gmssl.Sm9SignMasterKey() 843 | gmssl.Sm9SignKey(owner_id) 844 | ``` 845 | 846 | 对象Sm9SignMasterKey的方法: 847 | 848 | * `Sm9SignMasterKey.generate_master_key()` 849 | * `Sm9SignMasterKey.extract_key()` 850 | * `Sm9SignMasterKey.import_encrypted_master_key_info_pem()` 851 | * `Sm9SignMasterKey.export_encrypted_master_key_info_pem()` 852 | * `Sm9SignMasterKey.export_public_master_key_pem()` 853 | * `Sm9SignMasterKey.import_public_master_key_pem()` 854 | 855 | 对象Sm9SignKey的方法: 856 | 857 | - `Sm9SignKey.get_id()` 858 | - `Sm9SignKey.import_encrypted_private_key_info_pem()` 859 | - `Sm9SignKey.export_encrypted_private_key_info_pem()` 860 | 861 | 类`Sm9Signature`实现对数据的SM9签名和验证功能。SM9签名时需要提供`Sm9SignKey`类型的签名方私钥(其中包含签名者的ID),在验证签名时需要提供`Sm9SignMasterKey`格式的系统主公钥和签名方的ID。`Sm9Signature`和`Sm2Signature`提供类似的`update`、`sign`、`verify`接口,只是在验证的时候需要提供的不是公钥,而是系统的主公钥和签名方的ID。 862 | 863 | ```python 864 | gmssl.Sm9Signature(sign = DO_SIGN) 865 | ``` 866 | 867 | 模块`gmssl`中包含如下Sm9Signature的常量: 868 | 869 | - `SM9_SIGNATURE_SIZE` 870 | 871 | 对象Sm9Signature的方法: 872 | 873 | - `Sm9Signature.reset()` 874 | - `Sm9Signature.update()` 875 | - `Sm9Signature.sign()` 876 | - `Sm9Signature.verify()` 877 | 878 | 下面的例子展示了SM9签名的主密钥生成、用户私钥生成、签名、验证的过程。 879 | 880 | ```python 881 | master_key = Sm9SignMasterKey() 882 | master_key.generate_master_key() 883 | print("SM9 master key generated") 884 | 885 | master_key.export_encrypted_master_key_info_pem('sign_msk.pem', 'password') 886 | master_key.export_public_master_key_pem('sign_mpk.pem') 887 | print("Export master key and public master key") 888 | 889 | 890 | master = Sm9SignMasterKey() 891 | master.import_encrypted_master_key_info_pem('sign_msk.pem', 'password') 892 | 893 | signer_id = 'Alice' 894 | key = master.extract_key(signer_id) 895 | 896 | message = "Message to be signed" 897 | 898 | sign = Sm9Signature(DO_SIGN) 899 | sign.update(message.encode('utf-8')) 900 | sig = sign.sign(key) 901 | 902 | 903 | master_pub = Sm9SignMasterKey() 904 | master_pub.import_public_master_key_pem('sign_mpk.pem') 905 | 906 | verify = Sm9Signature(DO_VERIFY) 907 | verify.update(message.encode('utf-8')) 908 | ret = verify.verify(sig, master_pub, signer_id) 909 | ``` 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | -------------------------------------------------------------------------------- /examples/sm2_cert.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | from gmssl import * 9 | 10 | cert_txt = '''\ 11 | -----BEGIN CERTIFICATE----- 12 | MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG 13 | EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw 14 | MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO 15 | UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE 16 | MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT 17 | V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti 18 | W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ 19 | MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b 20 | 53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI 21 | pDoiVhsLwg== 22 | -----END CERTIFICATE-----''' 23 | with open('ROOTCA.pem', 'w') as file: 24 | file.write(cert_txt) 25 | file.close() 26 | 27 | cert = Sm2Certificate() 28 | cert.import_pem('ROOTCA.pem') 29 | 30 | print("Certificate") 31 | 32 | serial = cert.get_serial_number() 33 | print("Serial :", serial.hex()) 34 | 35 | validity = cert.get_validity() 36 | print("Validity.notBefore :", validity.not_before) 37 | print("Validity.notAfter :", validity.not_after) 38 | 39 | issuer = cert.get_issuer() 40 | print("Issuer :") 41 | for key in issuer: 42 | if key == 'raw_data': 43 | print(" ", key, ":", issuer[key].hex()) 44 | else: 45 | print(" ", key, ":", issuer[key]) 46 | 47 | 48 | subject = cert.get_subject() 49 | print("Subject :") 50 | for key in subject: 51 | if key == 'raw_data': 52 | print(" ", key, ":", subject[key].hex()) 53 | else: 54 | print(" ", key, ":", subject[key]) 55 | 56 | public_key = cert.get_subject_public_key() 57 | public_key.export_public_key_info_pem('subject_public_key.pem') 58 | 59 | file = open('subject_public_key.pem',mode='r') 60 | fulltext = file.read() 61 | file.close() 62 | print("Subject Public Key:") 63 | print(fulltext) 64 | 65 | 66 | -------------------------------------------------------------------------------- /examples/sm2_enc.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | from gmssl import * 9 | 10 | # run sm2_key.py first 11 | 12 | 13 | print("SM2_MIN_PLAINTEXT_SIZE =", SM2_MIN_PLAINTEXT_SIZE) 14 | print("SM2_MAX_PLAINTEXT_SIZE =", SM2_MAX_PLAINTEXT_SIZE) 15 | print("SM2_MIN_CIPHERTEXT_SIZE =", SM2_MIN_CIPHERTEXT_SIZE) 16 | print("SM2_MAX_CIPHERTEXT_SIZE =", SM2_MAX_CIPHERTEXT_SIZE) 17 | print("") 18 | 19 | # Sender 20 | 21 | public_key = Sm2Key() 22 | public_key.import_public_key_info_pem('sm2pub.pem') 23 | 24 | plaintext = rand_bytes(SM4_KEY_SIZE + SM3_HMAC_MIN_KEY_SIZE) 25 | ciphertext = public_key.encrypt(plaintext) 26 | 27 | 28 | # Receiver 29 | 30 | private_key = Sm2Key() 31 | private_key.import_encrypted_private_key_info_pem('sm2.pem', 'password') 32 | 33 | decrypted = private_key.decrypt(ciphertext) 34 | 35 | print("plaintext :", plaintext.hex()) 36 | print("ciphertext :", ciphertext.hex()) 37 | print("decrypted :", decrypted.hex()) 38 | 39 | -------------------------------------------------------------------------------- /examples/sm2_key.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | from gmssl import * 9 | 10 | sm2 = Sm2Key() 11 | sm2.generate_key() 12 | 13 | sm2.export_encrypted_private_key_info_pem('sm2.pem', 'password') 14 | print('export private key to encrypted file sm2.pem') 15 | 16 | sm2.export_public_key_info_pem('sm2pub.pem') 17 | print('export public key to file sm2pub.pem') 18 | 19 | private_key = Sm2Key() 20 | private_key.import_encrypted_private_key_info_pem('sm2.pem', 'password') 21 | print("private key has private key :", private_key.has_private_key()) 22 | print("private key has public key :", private_key.has_public_key()) 23 | 24 | public_key = Sm2Key() 25 | public_key.import_public_key_info_pem('sm2pub.pem') 26 | print("public key has private key :", public_key.has_private_key()) 27 | print("public key has public key :", public_key.has_public_key()) 28 | 29 | -------------------------------------------------------------------------------- /examples/sm2_sign.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | from gmssl import * 9 | 10 | # run sm2_key.py first 11 | 12 | 13 | print("SM2_DEFAULT_ID =", SM2_DEFAULT_ID) 14 | print("SM2_MAX_SIGNATURE_SIZE =", SM2_MAX_SIGNATURE_SIZE) 15 | print("") 16 | 17 | # Signer 18 | 19 | private_key = Sm2Key() 20 | private_key.import_encrypted_private_key_info_pem('sm2.pem', 'password') 21 | 22 | z = private_key.compute_z(SM2_DEFAULT_ID) 23 | 24 | sm3 = Sm3() 25 | sm3.update(z) 26 | sm3.update(b'abc') 27 | dgst = sm3.digest() 28 | 29 | sig = private_key.sign(dgst) 30 | print("signature1 :", sig.hex()) 31 | 32 | signer = Sm2Signature(private_key, SM2_DEFAULT_ID, DO_SIGN) 33 | signer.update(b'abc') 34 | sig2 = signer.sign() 35 | print("signature2 :", sig2.hex()) 36 | 37 | # Verifier 38 | 39 | public_key = Sm2Key() 40 | public_key.import_public_key_info_pem('sm2pub.pem') 41 | 42 | z = public_key.compute_z(SM2_DEFAULT_ID) 43 | 44 | sm3 = Sm3() 45 | sm3.update(z) 46 | sm3.update(b'abc') 47 | dgst = sm3.digest() 48 | 49 | ret = public_key.verify(dgst, sig) 50 | print("Verify signature1 success :", ret) 51 | 52 | ret = public_key.verify(dgst, sig2) 53 | print("Verify signature2 success :", ret) 54 | 55 | verifier = Sm2Signature(public_key, SM2_DEFAULT_ID, DO_VERIFY) 56 | verifier.update(b'abc') 57 | ret = verifier.verify(sig) 58 | print("Verify signature1 success :", ret) 59 | 60 | verifier = Sm2Signature(public_key, SM2_DEFAULT_ID, DO_VERIFY) 61 | verifier.update(b'abc') 62 | ret = verifier.verify(sig2) 63 | print("Verify signature2 success :", ret) 64 | 65 | 66 | -------------------------------------------------------------------------------- /examples/sm3.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | from gmssl import * 9 | 10 | print("SM3_DIGEST_SIZE =", SM3_DIGEST_SIZE) 11 | 12 | sm3 = Sm3() 13 | sm3.update(b'abc') 14 | dgst = sm3.digest() 15 | print("sm3('abc') : " + dgst.hex()) 16 | 17 | sm3.reset() 18 | for i in range(16): 19 | sm3.update(b'abcd') 20 | dgst = sm3.digest() 21 | print("sm3('abcd'*16) : " + dgst.hex()) 22 | 23 | -------------------------------------------------------------------------------- /examples/sm3_hmac.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | from gmssl import * 9 | 10 | print("SM3_HMAC_MIN_KEY_SIZE =", SM3_HMAC_MIN_KEY_SIZE) 11 | print("SM3_HMAC_MAX_KEY_SIZE =", SM3_HMAC_MAX_KEY_SIZE) 12 | print("SM3_HMAC_SIZE =", SM3_HMAC_SIZE) 13 | 14 | key = rand_bytes(SM3_HMAC_MIN_KEY_SIZE) 15 | 16 | sm3_hmac = Sm3Hmac(key) 17 | sm3_hmac.update(b'abc') 18 | mac = sm3_hmac.generate_mac() 19 | print("key = " + key.hex()) 20 | print("sm3_hmac('abc') : " + mac.hex()) 21 | 22 | sm3_hmac.reset(key) 23 | for i in range(16): 24 | sm3_hmac.update(b'abcd') 25 | mac = sm3_hmac.generate_mac() 26 | print("sm3_hmac('abcd'*16) : " + mac.hex()) 27 | 28 | -------------------------------------------------------------------------------- /examples/sm3_pbkdf2.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 9 | from gmssl import * 10 | 11 | 12 | print("SM3_PBKDF2_MIN_ITER =", SM3_PBKDF2_MIN_ITER) 13 | print("SM3_PBKDF2_MAX_ITER =", SM3_PBKDF2_MAX_ITER) 14 | print("SM3_PBKDF2_MAX_SALT_SIZE =", SM3_PBKDF2_MAX_SALT_SIZE) 15 | print("SM3_PBKDF2_DEFAULT_SALT_SIZE =", SM3_PBKDF2_DEFAULT_SALT_SIZE) 16 | print("SM3_PBKDF2_MAX_KEY_SIZE =", SM3_PBKDF2_MAX_KEY_SIZE) 17 | print("") 18 | 19 | passwd = "Password" 20 | salt = rand_bytes(SM3_PBKDF2_DEFAULT_SALT_SIZE) 21 | iterator = SM3_PBKDF2_MIN_ITER 22 | keylen = 32 23 | 24 | key = sm3_pbkdf2(passwd, salt, iterator, keylen) 25 | print("Password :", passwd) 26 | print("Salt :", salt.hex()) 27 | print("Iterator :", iterator) 28 | print("Keylen :", keylen) 29 | print("sm2_pbkdf2(Password, Salt, Iter, Keylen) :", key.hex()) 30 | 31 | -------------------------------------------------------------------------------- /examples/sm4.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | from gmssl import * 9 | 10 | print("SM4_KEY_SIZE =", SM4_KEY_SIZE) 11 | print("SM4_BLOCK_SIZE =", SM4_BLOCK_SIZE) 12 | print("") 13 | 14 | key = rand_bytes(SM4_KEY_SIZE) 15 | plaintext = rand_bytes(SM4_BLOCK_SIZE) 16 | 17 | sm4_enc = Sm4(key, DO_ENCRYPT) 18 | ciphertext = sm4_enc.encrypt(plaintext) 19 | 20 | sm4_dec = Sm4(key, DO_DECRYPT) 21 | decrypted = sm4_dec.encrypt(ciphertext) 22 | 23 | print("key =", key.hex()) 24 | print("plaintext =", plaintext.hex()) 25 | print("ciphertext = ", ciphertext.hex()) 26 | print("decrypted =", decrypted.hex()) 27 | 28 | -------------------------------------------------------------------------------- /examples/sm4_cbc.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | from gmssl import * 9 | 10 | print("SM4_KEY_SIZE =", SM4_KEY_SIZE) 11 | print("SM4_CBC_IV_SIZE =", SM4_CBC_IV_SIZE) 12 | print("") 13 | 14 | key = rand_bytes(SM4_KEY_SIZE) 15 | iv = rand_bytes(SM4_CBC_IV_SIZE) 16 | plaintext = b'abc' 17 | 18 | sm4_enc = Sm4Cbc(key, iv, DO_ENCRYPT) 19 | ciphertext = sm4_enc.update(plaintext) 20 | ciphertext += sm4_enc.finish() 21 | 22 | sm4_dec = Sm4Cbc(key, iv, DO_DECRYPT) 23 | decrypted = sm4_dec.update(ciphertext) 24 | decrypted += sm4_dec.finish() 25 | 26 | print("key =", key.hex()) 27 | print("iv =", iv.hex()) 28 | print("plaintext =", plaintext.hex()) 29 | print("ciphertext = ", ciphertext.hex()) 30 | print("decrypted =", decrypted.hex()) 31 | 32 | -------------------------------------------------------------------------------- /examples/sm4_ctr.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | from gmssl import * 9 | 10 | print("SM4_KEY_SIZE =", SM4_KEY_SIZE) 11 | print("SM4_CTR_IV_SIZE =", SM4_CTR_IV_SIZE) 12 | print("") 13 | 14 | key = rand_bytes(SM4_KEY_SIZE) 15 | iv = rand_bytes(SM4_CTR_IV_SIZE) 16 | plaintext = b'abc' 17 | 18 | sm4_enc = Sm4Ctr(key, iv) 19 | ciphertext = sm4_enc.update(plaintext) 20 | ciphertext += sm4_enc.finish() 21 | 22 | sm4_dec = Sm4Ctr(key, iv) 23 | decrypted = sm4_dec.update(ciphertext) 24 | decrypted += sm4_dec.finish() 25 | 26 | print("key =", key.hex()) 27 | print("iv =", iv.hex()) 28 | print("plaintext =", plaintext.hex()) 29 | print("ciphertext = ", ciphertext.hex()) 30 | print("decrypted =", decrypted.hex()) 31 | 32 | -------------------------------------------------------------------------------- /examples/sm4_gcm.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | from gmssl import * 9 | 10 | 11 | print("SM4_GCM_MIN_IV_SIZE =", SM4_GCM_MIN_IV_SIZE) 12 | print("SM4_GCM_MAX_IV_SIZE =", SM4_GCM_MAX_IV_SIZE) 13 | print("SM4_GCM_DEFAULT_IV_SIZE =", SM4_GCM_DEFAULT_IV_SIZE) 14 | print("SM4_GCM_DEFAULT_TAG_SIZE =", SM4_GCM_DEFAULT_TAG_SIZE) 15 | print("SM4_GCM_MAX_TAG_SIZE =", SM4_GCM_MAX_TAG_SIZE) 16 | print("") 17 | 18 | 19 | key = rand_bytes(SM4_KEY_SIZE) 20 | iv = rand_bytes(SM4_GCM_DEFAULT_IV_SIZE) 21 | aad = b'Additional auth-data' 22 | plaintext = b'abc' 23 | taglen = SM4_GCM_DEFAULT_TAG_SIZE 24 | 25 | sm4_enc = Sm4Gcm(key, iv, aad, taglen, DO_ENCRYPT) 26 | ciphertext = sm4_enc.update(plaintext) 27 | ciphertext += sm4_enc.finish() 28 | 29 | sm4_dec = Sm4Gcm(key, iv, aad, taglen, DO_DECRYPT) 30 | decrypted = sm4_dec.update(ciphertext) 31 | decrypted += sm4_dec.finish() 32 | 33 | print("key =", key.hex()) 34 | print("iv =", iv.hex()) 35 | print("aad =", aad.hex()) 36 | print("taglen =", taglen) 37 | print("plaintext =", plaintext.hex()) 38 | print("ciphertext = ", ciphertext.hex()) 39 | print("decrypted =", decrypted.hex()) 40 | 41 | -------------------------------------------------------------------------------- /examples/sm9_enc.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | from gmssl import * 9 | 10 | 11 | print("SM9_MAX_ID_SIZE =", SM9_MAX_ID_SIZE) 12 | print("SM9_MAX_PLAINTEXT_SIZE =", SM9_MAX_PLAINTEXT_SIZE) 13 | print("SM9_MAX_CIPHERTEXT_SIZE =", SM9_MAX_CIPHERTEXT_SIZE) 14 | print("") 15 | 16 | master_key = Sm9EncMasterKey() 17 | master_key.generate_master_key() 18 | print("SM9 master key generated") 19 | 20 | master_key.export_encrypted_master_key_info_pem('enc_msk.pem', 'password') 21 | master_key.export_public_master_key_pem('enc_mpk.pem') 22 | print("Export master key and public master key") 23 | 24 | # Encrypt 25 | master_pub = Sm9EncMasterKey() 26 | master_pub.import_public_master_key_pem('enc_mpk.pem') 27 | 28 | plaintext = rand_bytes(SM4_KEY_SIZE + SM3_HMAC_MIN_KEY_SIZE) 29 | 30 | receiver_id = 'Alice' 31 | 32 | ciphertext = master_pub.encrypt(plaintext, receiver_id) 33 | 34 | # Decrypt 35 | master = Sm9EncMasterKey() 36 | master.import_encrypted_master_key_info_pem('enc_msk.pem', 'password') 37 | 38 | receiver_key = master.extract_key(receiver_id) 39 | 40 | decrypted = receiver_key.decrypt(ciphertext) 41 | 42 | print("receiver :", receiver_id) 43 | print("plaintext :", plaintext.hex()) 44 | print("ciphertext:", ciphertext.hex()) 45 | print("decrypted :", decrypted.hex()) 46 | 47 | -------------------------------------------------------------------------------- /examples/sm9_sign.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | from gmssl import * 9 | 10 | 11 | print("SM9_MAX_ID_SIZE =", SM9_MAX_ID_SIZE) 12 | print("SM9_SIGNATURE_SIZE =", SM9_SIGNATURE_SIZE) 13 | print("") 14 | 15 | 16 | master_key = Sm9SignMasterKey() 17 | master_key.generate_master_key() 18 | print("SM9 master key generated") 19 | 20 | master_key.export_encrypted_master_key_info_pem('sign_msk.pem', 'password') 21 | master_key.export_public_master_key_pem('sign_mpk.pem') 22 | print("Export master key and public master key") 23 | 24 | 25 | master = Sm9SignMasterKey() 26 | master.import_encrypted_master_key_info_pem('sign_msk.pem', 'password') 27 | 28 | signer_id = 'Alice' 29 | key = master.extract_key(signer_id) 30 | 31 | message = "Message to be signed" 32 | 33 | sign = Sm9Signature(DO_SIGN) 34 | sign.update(message.encode('utf-8')) 35 | sig = sign.sign(key) 36 | 37 | 38 | master_pub = Sm9SignMasterKey() 39 | master_pub.import_public_master_key_pem('sign_mpk.pem') 40 | 41 | verify = Sm9Signature(DO_VERIFY) 42 | verify.update(message.encode('utf-8')) 43 | ret = verify.verify(sig, master_pub, signer_id) 44 | 45 | print("signer :", signer_id) 46 | print("message :", message) 47 | print("signature :", sig.hex()) 48 | print("verify success :", ret) 49 | 50 | -------------------------------------------------------------------------------- /examples/zuc.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | from gmssl import * 9 | 10 | print("ZUC_KEY_SIZE =", ZUC_KEY_SIZE) 11 | print("ZUC_IV_SIZE =", ZUC_IV_SIZE) 12 | print("") 13 | 14 | key = rand_bytes(ZUC_KEY_SIZE) 15 | iv = rand_bytes(ZUC_IV_SIZE) 16 | plaintext = b'abc' 17 | 18 | zuc_enc = Zuc(key, iv) 19 | ciphertext = zuc_enc.update(plaintext) 20 | ciphertext += zuc_enc.finish() 21 | 22 | zuc_dec = Zuc(key, iv) 23 | decrypted = zuc_dec.update(ciphertext) 24 | decrypted += zuc_dec.finish() 25 | 26 | print("key =", key.hex()) 27 | print("iv =", iv.hex()) 28 | print("plaintext =", plaintext.hex()) 29 | print("ciphertext = ", ciphertext.hex()) 30 | print("decrypted =", decrypted.hex()) 31 | 32 | -------------------------------------------------------------------------------- /gmssl.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the License); you may 4 | # not use this file except in compliance with the License. 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # GmSSL-Python - Python binding of the GmSSL library with `ctypes` 9 | 10 | 11 | from ctypes import * 12 | from ctypes.util import find_library 13 | import datetime 14 | import sys 15 | 16 | if find_library('gmssl') == None: 17 | raise ValueError('Install GmSSL dynamic library from https://github.com/guanzhi/GmSSL') 18 | gmssl = cdll.LoadLibrary(find_library("gmssl")) 19 | if gmssl.gmssl_version_num() < 30101: 20 | raise ValueError('GmSSL version < 3.1.1') 21 | 22 | if sys.platform == 'win32': 23 | libc = cdll.LoadLibrary(find_library('msvcrt')) 24 | else: 25 | libc = cdll.LoadLibrary(find_library('c')) 26 | 27 | 28 | class NativeError(Exception): 29 | ''' 30 | GmSSL libraray inner error 31 | ''' 32 | 33 | class StateError(Exception): 34 | ''' 35 | Crypto state error 36 | ''' 37 | 38 | GMSSL_PYTHON_VERSION = "2.2.2" 39 | 40 | def gmssl_library_version_num(): 41 | return gmssl.gmssl_version_num() 42 | 43 | def gmssl_library_version_str(): 44 | gmssl.gmssl_version_str.restype = c_char_p 45 | return gmssl.gmssl_version_str().decode('ascii') 46 | 47 | GMSSL_LIBRARY_VERSION = gmssl_library_version_str() 48 | 49 | 50 | def rand_bytes(size): 51 | buf = create_string_buffer(size) 52 | gmssl.rand_bytes(buf, c_size_t(size)) 53 | return buf.raw 54 | 55 | 56 | 57 | SM3_DIGEST_SIZE = 32 58 | _SM3_STATE_WORDS = 8 59 | _SM3_BLOCK_SIZE = 64 60 | 61 | class Sm3(Structure): 62 | 63 | _fields_ = [ 64 | ("dgst", c_uint32 * _SM3_STATE_WORDS), 65 | ("nblocks", c_uint64), 66 | ("block", c_uint8 * _SM3_BLOCK_SIZE), 67 | ("num", c_size_t) 68 | ] 69 | 70 | def __init__(self): 71 | gmssl.sm3_init(byref(self)) 72 | 73 | def reset(self): 74 | gmssl.sm3_init(byref(self)) 75 | 76 | def update(self, data): 77 | gmssl.sm3_update(byref(self), data, c_size_t(len(data))) 78 | 79 | def digest(self): 80 | dgst = create_string_buffer(SM3_DIGEST_SIZE) 81 | gmssl.sm3_finish(byref(self), dgst) 82 | return dgst.raw 83 | 84 | 85 | SM3_HMAC_MIN_KEY_SIZE = 16 86 | SM3_HMAC_MAX_KEY_SIZE = 64 87 | SM3_HMAC_SIZE = SM3_DIGEST_SIZE 88 | 89 | class Sm3Hmac(Structure): 90 | 91 | _fields_ = [ 92 | ("sm3_ctx", Sm3), 93 | ("key", c_uint8 * _SM3_BLOCK_SIZE) 94 | ] 95 | 96 | def __init__(self, key): 97 | if len(key) < SM3_HMAC_MIN_KEY_SIZE or len(key) > SM3_HMAC_MAX_KEY_SIZE: 98 | raise ValueError('Invalid SM3 HMAC key length') 99 | gmssl.sm3_hmac_init(byref(self), key, c_size_t(len(key))) 100 | 101 | def reset(self, key): 102 | if len(key) < SM3_HMAC_MIN_KEY_SIZE or len(key) > SM3_HMAC_MAX_KEY_SIZE: 103 | raise ValueError('Invalid SM3 HMAC key length') 104 | gmssl.sm3_hmac_init(byref(self), key, c_size_t(len(key))) 105 | 106 | def update(self, data): 107 | gmssl.sm3_hmac_update(byref(self), data, c_size_t(len(data))) 108 | 109 | def generate_mac(self): 110 | hmac = create_string_buffer(SM3_HMAC_SIZE) 111 | gmssl.sm3_hmac_finish(byref(self), hmac) 112 | return hmac.raw 113 | 114 | 115 | 116 | SM3_PBKDF2_MIN_ITER = 10000 # from 117 | SM3_PBKDF2_MAX_ITER = 16777216 # 2^24 118 | SM3_PBKDF2_MAX_SALT_SIZE = 64 # from 119 | SM3_PBKDF2_DEFAULT_SALT_SIZE = 8 # from 120 | SM3_PBKDF2_MAX_KEY_SIZE = 256 # from gmssljni.c:sm3_pbkdf2():sizeof(keybuf) 121 | 122 | def sm3_pbkdf2(passwd, salt, iterator, keylen): 123 | 124 | if len(salt) > SM3_PBKDF2_MAX_SALT_SIZE: 125 | raise ValueError('Invalid salt length') 126 | 127 | if iterator < SM3_PBKDF2_MIN_ITER or iterator > SM3_PBKDF2_MAX_ITER: 128 | raise ValueError('Invalid iterator value') 129 | 130 | if keylen > SM3_PBKDF2_MAX_KEY_SIZE: 131 | raise ValueError('Invalid key length') 132 | 133 | passwd = passwd.encode('utf-8') 134 | key = create_string_buffer(keylen) 135 | 136 | if gmssl.pbkdf2_hmac_sm3_genkey(c_char_p(passwd), c_size_t(len(passwd)), 137 | salt, c_size_t(len(salt)), c_size_t(iterator), c_size_t(keylen), key) != 1: 138 | raise NativeError('libgmssl inner error') 139 | 140 | return key.raw 141 | 142 | 143 | 144 | SM4_KEY_SIZE = 16 145 | SM4_BLOCK_SIZE = 16 146 | _SM4_NUM_ROUNDS = 32 147 | 148 | class Sm4(Structure): 149 | 150 | _fields_ = [ 151 | ("rk", c_uint32 * _SM4_NUM_ROUNDS) 152 | ] 153 | 154 | def __init__(self, key, encrypt): 155 | if len(key) != SM4_KEY_SIZE: 156 | raise ValueError('Invalid key length') 157 | if encrypt: 158 | gmssl.sm4_set_encrypt_key(byref(self), key) 159 | else: 160 | gmssl.sm4_set_decrypt_key(byref(self), key) 161 | 162 | def encrypt(self, block): 163 | if len(block) != SM4_BLOCK_SIZE: 164 | raise ValueError('Invalid block size') 165 | outbuf = create_string_buffer(SM4_BLOCK_SIZE) 166 | gmssl.sm4_encrypt(byref(self), block, outbuf) 167 | return outbuf.raw 168 | 169 | 170 | SM4_CBC_IV_SIZE = SM4_BLOCK_SIZE 171 | 172 | 173 | class Sm4Cbc(Structure): 174 | 175 | _fields_ = [ 176 | ("sm4_key", Sm4), 177 | ("iv", c_uint8 * SM4_BLOCK_SIZE), 178 | ("block", c_uint8 * SM4_BLOCK_SIZE), 179 | ("block_nbytes", c_size_t) 180 | ] 181 | 182 | def __init__(self, key, iv, encrypt): 183 | if len(key) != SM4_KEY_SIZE: 184 | raise ValueError('Invalid key length') 185 | if len(iv) != SM4_BLOCK_SIZE: 186 | raise ValueError('Invalid IV size') 187 | if encrypt == DO_ENCRYPT: 188 | if gmssl.sm4_cbc_encrypt_init(byref(self), key, iv) != 1: 189 | raise NativeError('libgmssl inner error') 190 | else: 191 | if gmssl.sm4_cbc_decrypt_init(byref(self), key, iv) != 1: 192 | raise NativeError('libgmssl inner error') 193 | self._encrypt = encrypt 194 | 195 | def update(self, data): 196 | outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) 197 | outlen = c_size_t() 198 | if self._encrypt == DO_ENCRYPT: 199 | if gmssl.sm4_cbc_encrypt_update(byref(self), data, c_size_t(len(data)), 200 | outbuf, byref(outlen)) != 1: 201 | raise NativeError('libgmssl inner error') 202 | else: 203 | if gmssl.sm4_cbc_decrypt_update(byref(self), data, c_size_t(len(data)), 204 | outbuf, byref(outlen)) != 1: 205 | raise NativeError('libgmssl inner error') 206 | return outbuf[0:outlen.value] 207 | 208 | def finish(self): 209 | outbuf = create_string_buffer(SM4_BLOCK_SIZE) 210 | outlen = c_size_t() 211 | if self._encrypt == True: 212 | if gmssl.sm4_cbc_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: 213 | raise NativeError('libgmssl inner error') 214 | else: 215 | if gmssl.sm4_cbc_decrypt_finish(byref(self), outbuf, byref(outlen)) != 1: 216 | raise NativeError('libgmssl inner error') 217 | return outbuf[:outlen.value] 218 | 219 | 220 | 221 | SM4_CTR_IV_SIZE = 16 222 | 223 | 224 | class Sm4Ctr(Structure): 225 | 226 | _fields_ = [ 227 | ("sm4_key", Sm4), 228 | ("ctr", c_uint8 * SM4_BLOCK_SIZE), 229 | ("block", c_uint8 * SM4_BLOCK_SIZE), 230 | ("block_nbytes", c_size_t) 231 | ] 232 | 233 | def __init__(self, key, iv): 234 | if len(key) != SM4_KEY_SIZE: 235 | raise ValueError('Invalid key length') 236 | if len(iv) != SM4_BLOCK_SIZE: 237 | raise ValueError('Invalid IV size') 238 | if gmssl.sm4_ctr_encrypt_init(byref(self), key, iv) != 1: 239 | raise NativeError('libgmssl inner error') 240 | 241 | def update(self, data): 242 | outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) 243 | outlen = c_size_t() 244 | if gmssl.sm4_ctr_encrypt_update(byref(self), data, c_size_t(len(data)), 245 | outbuf, byref(outlen)) != 1: 246 | raise NativeError('libgmssl inner error') 247 | return outbuf[0:outlen.value] 248 | 249 | def finish(self): 250 | outbuf = create_string_buffer(SM4_BLOCK_SIZE) 251 | outlen = c_size_t() 252 | if gmssl.sm4_ctr_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: 253 | raise NativeError('libgmssl inner error') 254 | return outbuf[:outlen.value] 255 | 256 | 257 | ZUC_KEY_SIZE = 16 258 | ZUC_IV_SIZE = 16 259 | 260 | class ZucState(Structure): 261 | _fields_ = [ 262 | ("LFSR", c_uint32 * 16), 263 | ("R1", c_uint32), 264 | ("R2", c_uint32) 265 | ] 266 | 267 | class Zuc(Structure): 268 | 269 | _fields_ = [ 270 | ("zuc_state", ZucState), 271 | ("block", c_uint8 * 4), 272 | ("block_nbytes", c_size_t) 273 | ] 274 | 275 | def __init__(self, key, iv): 276 | if len(key) != ZUC_KEY_SIZE: 277 | raise ValueError('Invalid key length') 278 | if len(iv) != ZUC_IV_SIZE: 279 | raise ValueError('Invalid IV size') 280 | if gmssl.zuc_encrypt_init(byref(self), key, iv) != 1: 281 | raise NativeError('libgmssl inner error') 282 | 283 | def update(self, data): 284 | outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) 285 | outlen = c_size_t() 286 | if gmssl.zuc_encrypt_update(byref(self), data, c_size_t(len(data)), 287 | outbuf, byref(outlen)) != 1: 288 | raise NativeError('libgmssl inner error') 289 | return outbuf[0:outlen.value] 290 | 291 | def finish(self): 292 | outbuf = create_string_buffer(SM4_BLOCK_SIZE) 293 | outlen = c_size_t() 294 | if gmssl.zuc_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: 295 | raise NativeError('libgmssl inner error') 296 | return outbuf[:outlen.value] 297 | 298 | 299 | class gf128_t(Structure): 300 | _fields_ = [ 301 | ("hi", c_uint64), 302 | ("lo", c_uint64) 303 | ] 304 | 305 | 306 | class Ghash(Structure): 307 | _fields_ = [ 308 | ("H", gf128_t), 309 | ("X", gf128_t), 310 | ("aadlen", c_size_t), 311 | ("clen", c_size_t), 312 | ("block", c_uint8 * 16), 313 | ("num", c_size_t) 314 | ] 315 | 316 | 317 | SM4_GCM_MIN_IV_SIZE = 1 318 | SM4_GCM_MAX_IV_SIZE = 64 319 | SM4_GCM_DEFAULT_IV_SIZE = 12 320 | SM4_GCM_DEFAULT_TAG_SIZE = 16 321 | SM4_GCM_MAX_TAG_SIZE = 16 322 | 323 | class Sm4Gcm(Structure): 324 | 325 | _fields_ = [ 326 | ("sm4_ctr_ctx", Sm4Ctr), 327 | ("mac_ctx", Ghash), 328 | ("Y", c_uint8 * 16), 329 | ("taglen", c_size_t), 330 | ("mac", c_uint8 * 16), 331 | ("maclen", c_size_t) 332 | ] 333 | 334 | def __init__(self, key, iv, aad, taglen = SM4_GCM_DEFAULT_TAG_SIZE, encrypt = True): 335 | if len(key) != SM4_KEY_SIZE: 336 | raise ValueError('Invalid key length') 337 | if len(iv) < SM4_GCM_MIN_IV_SIZE or len(iv) > SM4_GCM_MAX_IV_SIZE: 338 | raise ValueError('Invalid IV size') 339 | if taglen < 1 or taglen > SM4_GCM_MAX_TAG_SIZE: 340 | raise ValueError('Invalid Tag length') 341 | if encrypt == DO_ENCRYPT: 342 | if gmssl.sm4_gcm_encrypt_init(byref(self), key, c_size_t(len(key)), 343 | iv, c_size_t(len(iv)), aad, c_size_t(len(aad)), 344 | c_size_t(taglen)) != 1: 345 | raise NativeError('libgmssl inner error') 346 | else: 347 | if gmssl.sm4_gcm_decrypt_init(byref(self), key, c_size_t(len(key)), 348 | iv, c_size_t(len(iv)), aad, c_size_t(len(aad)), 349 | c_size_t(taglen)) != 1: 350 | raise NativeError('libgmssl inner error') 351 | self._encrypt = encrypt 352 | 353 | def update(self, data): 354 | outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) 355 | outlen = c_size_t() 356 | if self._encrypt == DO_ENCRYPT: 357 | if gmssl.sm4_gcm_encrypt_update(byref(self), data, c_size_t(len(data)), 358 | outbuf, byref(outlen)) != 1: 359 | raise NativeError('libgmssl inner error') 360 | else: 361 | if gmssl.sm4_gcm_decrypt_update(byref(self), data, c_size_t(len(data)), 362 | outbuf, byref(outlen)) != 1: 363 | raise NativeError('libgmssl inner error') 364 | return outbuf[0:outlen.value] 365 | 366 | def finish(self): 367 | outbuf = create_string_buffer(SM4_BLOCK_SIZE + SM4_GCM_MAX_TAG_SIZE) 368 | outlen = c_size_t() 369 | if self._encrypt == DO_ENCRYPT: 370 | if gmssl.sm4_gcm_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: 371 | raise NativeError('libgmssl inner error') 372 | else: 373 | if gmssl.sm4_gcm_decrypt_finish(byref(self), outbuf, byref(outlen)) != 1: 374 | raise NativeError('libgmssl inner error') 375 | return outbuf[:outlen.value] 376 | 377 | 378 | SM2_DEFAULT_ID = '1234567812345678' 379 | 380 | SM2_MAX_SIGNATURE_SIZE = 72 381 | 382 | SM2_MIN_PLAINTEXT_SIZE = 1 383 | SM2_MAX_PLAINTEXT_SIZE = 255 384 | SM2_MIN_CIPHERTEXT_SIZE = 45 385 | SM2_MAX_CIPHERTEXT_SIZE = 366 386 | 387 | 388 | class Sm2Point(Structure): 389 | _fields_ = [ 390 | ("x", c_uint8 * 32), 391 | ("y", c_uint8 * 32) 392 | ] 393 | 394 | 395 | class Sm2Key(Structure): 396 | 397 | _fields_ = [ 398 | ("public_key", Sm2Point), 399 | ("private_key", c_uint8 * 32) 400 | ] 401 | 402 | def __init__(self): 403 | self._has_public_key = False 404 | self._has_private_key = False 405 | 406 | def generate_key(self): 407 | if gmssl.sm2_key_generate(byref(self)) != 1: 408 | raise NativeError('libgmssl inner error') 409 | self._has_public_key = True 410 | self._has_private_key = True 411 | 412 | def has_private_key(self): 413 | return self._has_private_key 414 | 415 | def has_public_key(self): 416 | return self._has_public_key 417 | 418 | def compute_z(self, signer_id = SM2_DEFAULT_ID): 419 | if self._has_public_key == False: 420 | raise TypeError('has no public key') 421 | signer_id = signer_id.encode('utf-8') 422 | z = create_string_buffer(SM3_DIGEST_SIZE) 423 | gmssl.sm2_compute_z(z, byref(self), c_char_p(signer_id), c_size_t(len(signer_id))) 424 | return z.raw 425 | 426 | def export_encrypted_private_key_info_pem(self, path, passwd): 427 | if self._has_private_key == False: 428 | raise TypeError('has no private key') 429 | libc.fopen.restype = c_void_p 430 | fp = libc.fopen(path.encode('utf-8'), 'wb') 431 | passwd = passwd.encode('utf-8') 432 | if gmssl.sm2_private_key_info_encrypt_to_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: 433 | raise NativeError('libgmssl inner error') 434 | libc.fclose(c_void_p(fp)) 435 | 436 | def import_encrypted_private_key_info_pem(self, path, passwd): 437 | libc.fopen.restype = c_void_p 438 | fp = libc.fopen(path.encode('utf-8'), 'rb') 439 | passwd = passwd.encode('utf-8') 440 | if gmssl.sm2_private_key_info_decrypt_from_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: 441 | raise NativeError('libgmssl inner error') 442 | libc.fclose(c_void_p(fp)) 443 | self._has_public_key = True 444 | self._has_private_key = True 445 | 446 | def export_public_key_info_pem(self, path): 447 | if self._has_public_key == False: 448 | raise TypeError('has no public key') 449 | libc.fopen.restype = c_void_p 450 | fp = libc.fopen(path.encode('utf-8'), 'wb') 451 | if gmssl.sm2_public_key_info_to_pem(byref(self), c_void_p(fp)) != 1: 452 | raise NativeError('libgmssl inner error') 453 | libc.fclose(c_void_p(fp)) 454 | 455 | def import_public_key_info_pem(self, path): 456 | libc.fopen.restype = c_void_p 457 | fp = libc.fopen(path.encode('utf-8'), 'rb') 458 | if gmssl.sm2_public_key_info_from_pem(byref(self), c_void_p(fp)) != 1: 459 | raise NativeError('libgmssl inner error') 460 | libc.fclose(c_void_p(fp)) 461 | self._has_public_key = True 462 | self._has_private_key = False 463 | 464 | def sign(self, dgst): 465 | if self._has_private_key == False: 466 | raise TypeError('has no private key') 467 | if len(dgst) != SM3_DIGEST_SIZE: 468 | raise ValueError('Invalid SM3 digest size') 469 | sig = create_string_buffer(SM2_MAX_SIGNATURE_SIZE) 470 | siglen = c_size_t() 471 | if gmssl.sm2_sign(byref(self), dgst, sig, byref(siglen)) != 1: 472 | raise NativeError('libgmssl inner error') 473 | return sig[:siglen.value] 474 | 475 | def verify(self, dgst, signature): 476 | if self._has_public_key == False: 477 | raise TypeError('has no public key') 478 | if len(dgst) != SM3_DIGEST_SIZE: 479 | raise ValueError('Invalid SM3 digest size') 480 | if gmssl.sm2_verify(byref(self), dgst, signature, c_size_t(len(signature))) != 1: 481 | return False 482 | return True 483 | 484 | def encrypt(self, data): 485 | if self._has_public_key == False: 486 | raise TypeError('has no public key') 487 | if len(data) > SM2_MAX_PLAINTEXT_SIZE: 488 | raise NativeError('libgmssl inner error') 489 | outbuf = create_string_buffer(SM2_MAX_CIPHERTEXT_SIZE) 490 | outlen = c_size_t() 491 | if gmssl.sm2_encrypt(byref(self), data, c_size_t(len(data)), 492 | outbuf, byref(outlen)) != 1: 493 | raise NativeError('libgmssl inner error') 494 | return outbuf[:outlen.value] 495 | 496 | def decrypt(self, ciphertext): 497 | if self._has_private_key == False: 498 | raise TypeError('has no private key') 499 | outbuf = create_string_buffer(SM2_MAX_PLAINTEXT_SIZE) 500 | outlen = c_size_t() 501 | if gmssl.sm2_decrypt(byref(self), ciphertext, c_size_t(len(ciphertext)) 502 | , outbuf, byref(outlen)) != 1: 503 | raise NativeError('libgmssl inner error') 504 | return outbuf[:outlen.value] 505 | 506 | 507 | DO_ENCRYPT = True 508 | DO_DECRYPT = False 509 | DO_SIGN = True 510 | DO_VERIFY = False 511 | 512 | class Sm2Signature(Structure): 513 | 514 | _fields_ = [ 515 | ("sm3_ctx", Sm3), 516 | ("key", Sm2Key) 517 | ] 518 | 519 | def __init__(self, sm2_key, signer_id = SM2_DEFAULT_ID, sign = DO_SIGN): 520 | signer_id = signer_id.encode('utf-8') 521 | if sign == DO_SIGN: 522 | if sm2_key.has_private_key() != True: 523 | raise NativeError('libgmssl inner error') 524 | if gmssl.sm2_sign_init(byref(self), byref(sm2_key), 525 | c_char_p(signer_id), c_size_t(len(signer_id))) != 1: 526 | raise NativeError('libgmssl inner error') 527 | else: 528 | if sm2_key.has_public_key() != True: 529 | raise NativeError('libgmssl inner error') 530 | if gmssl.sm2_verify_init(byref(self), byref(sm2_key), 531 | c_char_p(signer_id), c_size_t(len(signer_id))) != 1: 532 | raise NativeError('libgmssl inner error') 533 | self._sign = sign 534 | 535 | def update(self, data): 536 | if self._sign == DO_SIGN: 537 | if gmssl.sm2_sign_update(byref(self), data, c_size_t(len(data))) != 1: 538 | raise NativeError('libgmssl inner error') 539 | else: 540 | if gmssl.sm2_verify_update(byref(self), data, c_size_t(len(data))) != 1: 541 | raise NativeError('libgmssl inner error') 542 | 543 | def sign(self): 544 | if self._sign != DO_SIGN: 545 | raise StateError('not sign state') 546 | sig = create_string_buffer(SM2_MAX_SIGNATURE_SIZE) 547 | siglen = c_size_t() 548 | if gmssl.sm2_sign_finish(byref(self), sig, byref(siglen)) != 1: 549 | raise NativeError('libgmssl inner error') 550 | return sig[:siglen.value] 551 | 552 | def verify(self, signature): 553 | if self._sign != DO_VERIFY: 554 | raise StateError('not verify state') 555 | if gmssl.sm2_verify_finish(byref(self), signature, c_size_t(len(signature))) != 1: 556 | return False 557 | return True 558 | 559 | 560 | class sm9_bn_t(Structure): 561 | _fields_ = [ 562 | ("d", c_uint64 * 8) 563 | ] 564 | 565 | class sm9_fp2_t(Structure): 566 | _fields_ = [ 567 | ("d", sm9_bn_t * 2) 568 | ] 569 | 570 | class Sm9Point(Structure): 571 | _fields_ = [ 572 | ("X", sm9_bn_t), 573 | ("Y", sm9_bn_t), 574 | ("Z", sm9_bn_t) 575 | ] 576 | 577 | class Sm9TwistPoint(Structure): 578 | _fields_ = [ 579 | ("X", sm9_fp2_t), 580 | ("Y", sm9_fp2_t), 581 | ("Z", sm9_fp2_t) 582 | ] 583 | 584 | 585 | SM9_MAX_ID_SIZE = 63 586 | SM9_MAX_PLAINTEXT_SIZE = 255 587 | SM9_MAX_CIPHERTEXT_SIZE = 367 588 | 589 | class Sm9EncKey(Structure): 590 | _fields_ = [ 591 | ("Ppube", Sm9Point), 592 | ("de", Sm9TwistPoint) 593 | ] 594 | 595 | def __init__(self, owner_id): 596 | self._id = owner_id.encode('utf-8') 597 | self._has_private_key = False 598 | 599 | def get_id(self): 600 | return self._id; 601 | 602 | def has_private_key(self): 603 | return self._has_private_key 604 | 605 | def import_encrypted_private_key_info_pem(self, path, passwd): 606 | libc.fopen.restype = c_void_p 607 | fp = libc.fopen(path.encode('utf-8'), 'rb') 608 | passwd = passwd.encode('utf-8') 609 | if gmssl.sm9_enc_key_info_decrypt_from_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: 610 | raise NativeError('libgmssl inner error') 611 | libc.fclose(c_void_p(fp)) 612 | self._has_private_key = True 613 | 614 | def export_encrypted_private_key_info_pem(self, path, passwd): 615 | if self._has_private_key != True: 616 | raise TypeError('has no private key') 617 | libc.fopen.restype = c_void_p 618 | fp = libc.fopen(path.encode('utf-8'), 'wb') 619 | passwd = passwd.encode('utf-8') 620 | if gmssl.sm9_enc_key_info_encrypt_to_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: 621 | raise NativeError('libgmssl inner error') 622 | libc.fclose(c_void_p(fp)) 623 | 624 | def decrypt(self, ciphertext): 625 | if self._has_private_key != True: 626 | raise TypeError('has no private key') 627 | plaintext = create_string_buffer(SM9_MAX_PLAINTEXT_SIZE) 628 | outlen = c_size_t() 629 | if gmssl.sm9_decrypt(byref(self), c_char_p(self._id), c_size_t(len(self._id)), 630 | ciphertext, c_size_t(len(ciphertext)), plaintext, byref(outlen)) != 1: 631 | raise NativeError('libgmssl inner error') 632 | return plaintext[0:outlen.value] 633 | 634 | 635 | class Sm9EncMasterKey(Structure): 636 | _fields_ = [ 637 | ("Ppube", Sm9Point), 638 | ("ke", sm9_bn_t) 639 | ] 640 | 641 | def __init__(self): 642 | self._has_public_key = False 643 | self._has_private_key = False 644 | 645 | def generate_master_key(self): 646 | if gmssl.sm9_enc_master_key_generate(byref(self)) != 1: 647 | raise NativeError('libgmssl inner error') 648 | self._has_public_key = True 649 | self._has_private_key = True 650 | 651 | def extract_key(self, identity): 652 | if self._has_private_key != True: 653 | raise TypeError('has no master key') 654 | key = Sm9EncKey(identity) 655 | identity = identity.encode('utf-8') 656 | if gmssl.sm9_enc_master_key_extract_key(byref(self), 657 | c_char_p(identity), c_size_t(len(identity)), byref(key)) != 1: 658 | raise NativeError('libgmssl inner error') 659 | key._has_public_key = True 660 | key._has_private_key = True 661 | return key 662 | 663 | def import_encrypted_master_key_info_pem(self, path, passwd): 664 | libc.fopen.restype = c_void_p 665 | fp = libc.fopen(path.encode('utf-8'), 'rb') 666 | passwd = passwd.encode('utf-8') 667 | if gmssl.sm9_enc_master_key_info_decrypt_from_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: 668 | raise NativeError('libgmssl inner error') 669 | libc.fclose(c_void_p(fp)) 670 | self._has_public_key = True 671 | self._has_private_key = True 672 | 673 | def export_encrypted_master_key_info_pem(self, path, passwd): 674 | if self._has_private_key != True: 675 | raise TypeError('has no master key') 676 | libc.fopen.restype = c_void_p 677 | fp = libc.fopen(path.encode('utf-8'), 'wb') 678 | passwd = passwd.encode('utf-8') 679 | if gmssl.sm9_enc_master_key_info_encrypt_to_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: 680 | raise NativeError('libgmssl inner error') 681 | libc.fclose(c_void_p(fp)) 682 | 683 | def export_public_master_key_pem(self, path): 684 | if self._has_public_key != True: 685 | raise TypeError('has no public master key') 686 | libc.fopen.restype = c_void_p 687 | fp = libc.fopen(path.encode('utf-8'), 'wb') 688 | if gmssl.sm9_enc_master_public_key_to_pem(byref(self), c_void_p(fp)) != 1: 689 | raise NativeError('libgmssl inner error') 690 | libc.fclose(c_void_p(fp)) 691 | 692 | def import_public_master_key_pem(self, path): 693 | libc.fopen.restype = c_void_p 694 | fp = libc.fopen(path.encode('utf-8'), 'rb') 695 | if gmssl.sm9_enc_master_public_key_from_pem(byref(self), c_void_p(fp)) != 1: 696 | raise NativeError('libgmssl inner error') 697 | libc.fclose(c_void_p(fp)) 698 | self._has_public_key = True 699 | self._has_private_key = False 700 | 701 | def encrypt(self, plaintext, to): 702 | if self._has_public_key != True: 703 | raise TypeError('has no public master key') 704 | to = to.encode('utf-8') 705 | ciphertext = create_string_buffer(SM9_MAX_CIPHERTEXT_SIZE) 706 | outlen = c_size_t() 707 | if gmssl.sm9_encrypt(byref(self), c_char_p(to), c_size_t(len(to)), 708 | plaintext, c_size_t(len(plaintext)), ciphertext, byref(outlen)) != 1: 709 | raise NativeError('libgmssl inner error') 710 | return ciphertext[0:outlen.value] 711 | 712 | 713 | class Sm9SignKey(Structure): 714 | _fields_ = [ 715 | ("Ppubs", Sm9TwistPoint), 716 | ("ds", Sm9Point) 717 | ] 718 | 719 | def __init__(self, owner_id): 720 | self._id = owner_id.encode('utf-8') 721 | self._has_private_key = False 722 | 723 | def get_id(self): 724 | return self._id; 725 | 726 | def has_private_key(self): 727 | return self._has_private_key 728 | 729 | def import_encrypted_private_key_info_pem(self, path, passwd): 730 | libc.fopen.restype = c_void_p 731 | fp = libc.fopen(path.encode('utf-8'), 'rb') 732 | passwd = passwd.encode('utf-8') 733 | if gmssl.sm9_sign_key_info_decrypt_from_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: 734 | raise NativeError('libgmssl inner error') 735 | libc.fclose(c_void_p(fp)) 736 | self._has_private_key = True 737 | 738 | def export_encrypted_private_key_info_pem(self, path, passwd): 739 | if self._has_private_key == False: 740 | raise TypeError('has no master key') 741 | libc.fopen.restype = c_void_p 742 | fp = libc.fopen(path.encode('utf-8'), 'wb') 743 | passwd = passwd.encode('utf-8') 744 | if gmssl.sm9_sign_key_info_encrypt_to_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: 745 | raise NativeError('libgmssl inner error') 746 | libc.fclose(c_void_p(fp)) 747 | 748 | 749 | class Sm9SignMasterKey(Structure): 750 | _fields_ = [ 751 | ("Ppubs", Sm9TwistPoint), 752 | ("ks", sm9_bn_t) 753 | ] 754 | 755 | def __init__(self): 756 | self._has_public_key = False 757 | self._has_private_key = False 758 | 759 | def generate_master_key(self): 760 | if gmssl.sm9_sign_master_key_generate(byref(self)) != 1: 761 | raise NativeError('libgmssl inner error') 762 | self._has_public_key = True 763 | self._has_private_key = True 764 | 765 | def extract_key(self, identity): 766 | if self._has_private_key != True: 767 | raise TypeError('has no master key') 768 | key = Sm9SignKey(identity) 769 | identity = identity.encode('utf-8') 770 | if gmssl.sm9_sign_master_key_extract_key(byref(self), 771 | c_char_p(identity), c_size_t(len(identity)), byref(key)) != 1: 772 | raise NativeError('libgmssl inner error') 773 | key._has_public_key = True 774 | key._has_private_key = True 775 | return key 776 | 777 | def import_encrypted_master_key_info_pem(self, path, passwd): 778 | libc.fopen.restype = c_void_p 779 | fp = libc.fopen(path.encode('utf-8'), 'rb') 780 | passwd = passwd.encode('utf-8') 781 | if gmssl.sm9_sign_master_key_info_decrypt_from_pem(byref(self), 782 | c_char_p(passwd), c_void_p(fp)) != 1: 783 | raise NativeError('libgmssl inner error') 784 | libc.fclose(c_void_p(fp)) 785 | self._has_public_key = True 786 | self._has_private_key = True 787 | 788 | def export_encrypted_master_key_info_pem(self, path, passwd): 789 | if self._has_private_key != True: 790 | raise TypeError('has no master key') 791 | libc.fopen.restype = c_void_p 792 | fp = libc.fopen(path.encode('utf-8'), 'wb') 793 | passwd = passwd.encode('utf-8') 794 | if gmssl.sm9_sign_master_key_info_encrypt_to_pem(byref(self), 795 | c_char_p(passwd), c_void_p(fp)) != 1: 796 | raise NativeError('libgmssl inner error') 797 | libc.fclose(c_void_p(fp)) 798 | 799 | def export_public_master_key_pem(self, path): 800 | if self._has_public_key != True: 801 | raise TypeError('has no public master key') 802 | libc.fopen.restype = c_void_p 803 | fp = libc.fopen(path.encode('utf-8'), 'wb') 804 | if gmssl.sm9_sign_master_public_key_to_pem(byref(self), c_void_p(fp)) != 1: 805 | raise NativeError('libgmssl inner error') 806 | libc.fclose(c_void_p(fp)) 807 | 808 | def import_public_master_key_pem(self, path): 809 | libc.fopen.restype = c_void_p 810 | fp = libc.fopen(path.encode('utf-8'), 'rb') 811 | if gmssl.sm9_sign_master_public_key_from_pem(byref(self), c_void_p(fp)) != 1: 812 | raise NativeError('libgmssl inner error') 813 | libc.fclose(c_void_p(fp)) 814 | self._has_public_key = True 815 | self._has_private_key = False 816 | 817 | 818 | SM9_SIGNATURE_SIZE = 104 819 | 820 | class Sm9Signature(Structure): 821 | 822 | _fields_ = [ 823 | ("sm3", Sm3) 824 | ] 825 | 826 | def __init__(self, sign = DO_SIGN): 827 | if sign == DO_SIGN: 828 | if gmssl.sm9_sign_init(byref(self)) != 1: 829 | raise NativeError('libgmssl inner error') 830 | else: 831 | if gmssl.sm9_verify_init(byref(self)) != 1: 832 | raise NativeError('libgmssl inner error') 833 | self._sign = sign 834 | self._inited = True 835 | 836 | 837 | def reset(self): 838 | if self._inited != True: 839 | raise StateError('not initialized') 840 | 841 | if self._sign == DO_SIGN: 842 | if gmssl.sm9_sign_init(byref(self)) != 1: 843 | raise NativeError('libgmssl inner error') 844 | else: 845 | if gmssl.sm9_verify_init(byref(self)) != 1: 846 | raise NativeError('libgmssl inner error') 847 | 848 | def update(self, data): 849 | 850 | if self._inited != True: 851 | raise StateError('not initialized') 852 | 853 | if self._sign == DO_SIGN: 854 | if gmssl.sm9_sign_update(byref(self), data, c_size_t(len(data))) != 1: 855 | raise NativeError('libgmssl inner error') 856 | else: 857 | if gmssl.sm9_verify_update(byref(self), data, c_size_t(len(data))) != 1: 858 | raise NativeError('libgmssl inner error') 859 | 860 | 861 | def sign(self, sign_key): 862 | if self._inited != True: 863 | raise StateError('not initialized') 864 | if self._sign != DO_SIGN: 865 | raise StateError('not sign state') 866 | 867 | sig = create_string_buffer(SM9_SIGNATURE_SIZE) 868 | siglen = c_size_t() 869 | if gmssl.sm9_sign_finish(byref(self), byref(sign_key), sig, byref(siglen)) != 1: 870 | raise NativeError('libgmssl inner error') 871 | return sig[:siglen.value] 872 | 873 | def verify(self, signature, public_master_key, signer_id): 874 | if self._inited != True: 875 | raise StateError('not initialized') 876 | if self._sign != DO_VERIFY: 877 | raise StateError('not verify state') 878 | 879 | signer_id = signer_id.encode('utf-8') 880 | 881 | if gmssl.sm9_verify_finish(byref(self), signature, c_size_t(len(signature)), 882 | byref(public_master_key), c_char_p(signer_id), c_size_t(len(signer_id))) != 1: 883 | return False 884 | return True 885 | 886 | 887 | 888 | _ASN1_TAG_IA5String = 22 889 | _ASN1_TAG_SEQUENCE = 0x30 890 | _ASN1_TAG_SET = 0x31 891 | 892 | 893 | 894 | def gmssl_parse_attr_type_and_value(name, d, dlen): 895 | oid = c_int() 896 | tag = c_int() 897 | val = c_void_p() 898 | vlen = c_size_t() 899 | 900 | if gmssl.x509_name_type_from_der(byref(oid), byref(d), byref(dlen)) != 1: 901 | raise NativeError('libgmssl inner error') 902 | gmssl.x509_name_type_name.restype = c_char_p 903 | oid_name = gmssl.x509_name_type_name(oid).decode('ascii') 904 | 905 | if oid_name == 'emailAddress': 906 | if gmssl.asn1_ia5_string_from_der_ex(_ASN1_TAG_IA5String, byref(val), byref(vlen), byref(d), byref(dlen)) != 1: 907 | raise NativeError('libgmssl inner error') 908 | else: 909 | if gmssl.x509_directory_name_from_der(byref(tag), byref(val), byref(vlen), byref(d), byref(dlen)) != 1: 910 | raise NativeError('libgmssl inner error') 911 | 912 | if dlen.value != 0: 913 | raise ValueError('invalid der encoding') 914 | 915 | value = create_string_buffer(vlen.value) 916 | libc.memcpy(value, val, vlen) 917 | 918 | name[oid_name] = value.raw.decode('utf-8') 919 | return True 920 | 921 | def gmssl_parse_rdn(name, d, dlen): 922 | v = c_void_p() 923 | vlen = c_size_t() 924 | 925 | while dlen.value > 0: 926 | if gmssl.asn1_type_from_der(_ASN1_TAG_SEQUENCE, byref(v), byref(vlen), byref(d), byref(dlen)) != 1: 927 | raise NativeError('libgmssl inner error') 928 | 929 | if gmssl_parse_attr_type_and_value(name, v, vlen) != 1: 930 | raise NativeError('libgmssl inner error') 931 | 932 | return True 933 | 934 | # https://stacktuts.com/how-to-correctly-pass-pointer-to-pointer-into-dll-in-python-and-ctypes# 935 | def gmssl_parse_name(name, d, dlen): 936 | v = c_void_p() 937 | vlen = c_size_t() 938 | 939 | while dlen.value > 0: 940 | if gmssl.asn1_nonempty_type_from_der(c_int(_ASN1_TAG_SET), byref(v), byref(vlen), byref(d), byref(dlen)) != 1: 941 | raise NativeError('libgmssl inner error') 942 | gmssl_parse_rdn(name, v, vlen) 943 | return True 944 | 945 | 946 | class Validity: 947 | 948 | def __init__(self, not_before, not_after): 949 | self.not_before = datetime.datetime.fromtimestamp(not_before) 950 | self.not_after = datetime.datetime.fromtimestamp(not_after) 951 | 952 | 953 | class Sm2Certificate: 954 | 955 | def import_pem(self, path): 956 | 957 | cert = c_void_p() 958 | certlen = c_size_t() 959 | if gmssl.x509_cert_new_from_file(byref(cert), byref(certlen), path.encode('utf-8')) != 1: 960 | raise NativeError('libgmssl inner error') 961 | 962 | self._cert = create_string_buffer(certlen.value) 963 | libc.memcpy(self._cert, cert, certlen) 964 | libc.free(cert) 965 | 966 | def get_raw(self): 967 | return self._cert; 968 | 969 | def export_pem(self, path): 970 | libc.fopen.restype = c_void_p 971 | fp = libc.fopen(path.encode('utf-8'), 'wb') 972 | if gmssl.x509_cert_to_pem(self._cert, c_size_t(len(self._cert)), c_void_p(fp)) != 1: 973 | raise NativeError('libgmssl inner error') 974 | 975 | def get_serial_number(self): 976 | 977 | serial_ptr = c_void_p() 978 | serial_len = c_size_t() 979 | 980 | if gmssl.x509_cert_get_issuer_and_serial_number(self._cert, c_size_t(len(self._cert)), 981 | None, None, byref(serial_ptr), byref(serial_len)) != 1: 982 | raise NativeError('libgmssl inner error') 983 | 984 | serial = create_string_buffer(serial_len.value) 985 | libc.memcpy(serial, serial_ptr, serial_len) 986 | return serial.raw 987 | 988 | def get_issuer(self): 989 | issuer_ptr = c_void_p() 990 | issuer_len = c_size_t() 991 | if gmssl.x509_cert_get_issuer(self._cert, c_size_t(len(self._cert)), 992 | byref(issuer_ptr), byref(issuer_len)) != 1: 993 | raise NativeError('libgmssl inner error') 994 | issuer_raw = create_string_buffer(issuer_len.value) 995 | libc.memcpy(issuer_raw, issuer_ptr, issuer_len) 996 | 997 | issuer = { "raw_data" : issuer_raw.raw } 998 | gmssl_parse_name(issuer, issuer_ptr, issuer_len) 999 | return issuer 1000 | 1001 | def get_subject(self): 1002 | subject_ptr = c_void_p() 1003 | subject_len = c_size_t() 1004 | if gmssl.x509_cert_get_subject(self._cert, c_size_t(len(self._cert)), 1005 | byref(subject_ptr), byref(subject_len)) != 1: 1006 | raise NativeError('libgmssl inner error') 1007 | subject_raw = create_string_buffer(subject_len.value) 1008 | libc.memcpy(subject_raw, subject_ptr, subject_len) 1009 | 1010 | subject = { "raw_data" : subject_raw.raw } 1011 | gmssl_parse_name(subject, subject_ptr, subject_len) 1012 | return subject 1013 | 1014 | def get_subject_public_key(self): 1015 | public_key = Sm2Key() 1016 | gmssl.x509_cert_get_subject_public_key(self._cert, c_size_t(len(self._cert)), byref(public_key)) 1017 | public_key._has_private_key = False 1018 | public_key._has_public_key = True 1019 | return public_key 1020 | 1021 | def get_validity(self): 1022 | not_before = c_ulong() 1023 | not_after = c_ulong() 1024 | if gmssl.x509_cert_get_details(self._cert, c_size_t(len(self._cert)), 1025 | None, None, None, None, None, None, 1026 | byref(not_before), byref(not_after), 1027 | None, None, None, None, None, None, None, None, None, None, None, None) != 1: 1028 | raise NativeError('libgmssl inner error') 1029 | return Validity(not_before.value, not_after.value) 1030 | 1031 | def verify_by_ca_certificate(self, cacert, sm2_id): 1032 | 1033 | cacert_raw = cacert.get_raw() 1034 | sm2_id = sm2_id.encode('utf-8') 1035 | 1036 | if gmssl.x509_cert_verify_by_ca_cert(self._cert, c_size_t(len(self._cert)), 1037 | cacert_raw, c_size_t(len(cacert_raw)), c_char_p(sm2_id), c_size_t(len(sm2_id))) != 1: 1038 | return False 1039 | return True 1040 | 1041 | 1042 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=61.0"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | 6 | [project] 7 | name = "gmssl_python" 8 | version = "2.2.2" 9 | authors = [ 10 | { name="Zhi Guan", email="guan@pku.edu.cn" }, 11 | ] 12 | description = "Python binding of the GmSSL library with ctypes" 13 | readme = "README.md" 14 | requires-python = ">=3.7" 15 | classifiers = [ 16 | "Programming Language :: Python :: 3", 17 | "License :: OSI Approved :: Apache Software License", 18 | "Operating System :: OS Independent", 19 | ] 20 | 21 | [project.urls] 22 | "Homepage" = "https://gmssl.github.io/GmSSL-Python/" 23 | "Bug Tracker" = "https://github.com/GmSSL/GmSSL-Python/issues" 24 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2023 The GmSSL Project. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the License); you may 6 | # not use this file except in compliance with the License. 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | 11 | import unittest 12 | from gmssl import * 13 | 14 | class TestGmSSL(unittest.TestCase): 15 | 16 | def test_version(self): 17 | self.assertTrue(gmssl_library_version_num() > 0) 18 | self.assertTrue(len(GMSSL_LIBRARY_VERSION) > 0) 19 | self.assertTrue(len(GMSSL_PYTHON_VERSION) > 0) 20 | 21 | def test_rand(self): 22 | keylen = 20 23 | key = rand_bytes(keylen) 24 | self.assertEqual(len(key), keylen) 25 | 26 | def test_sm3(self): 27 | dgst_hex = '66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0' 28 | sm3 = Sm3() 29 | sm3.update(b'abc') 30 | dgst = sm3.digest() 31 | self.assertEqual(dgst, bytes.fromhex(dgst_hex)) 32 | 33 | def test_sm3_hmac(self): 34 | key = b'1234567812345678' 35 | mac_hex = '0a69401a75c5d471f5166465eec89e6a65198ae885c1fdc061556254d91c1080' 36 | sm3_hmac = Sm3Hmac(key) 37 | sm3_hmac.update(b'abc') 38 | mac = sm3_hmac.generate_mac() 39 | self.assertEqual(mac, bytes.fromhex(mac_hex)) 40 | 41 | def test_sm3_pbkdf2(self): 42 | passwd = 'password' 43 | salt = b'12345678' 44 | iterator = 10000 45 | keylen = 32 46 | keyhex = 'ac5b4a93a130252181434970fa9d8e6f1083badecafc4409aaf0097c813e9fc6' 47 | key = sm3_pbkdf2(passwd, salt, iterator, keylen) 48 | self.assertEqual(key, bytes.fromhex(keyhex)) 49 | 50 | def test_sm4(self): 51 | key = b'1234567812345678' 52 | plaintext = b'block of message' 53 | ciphertext_hex = 'dd99d30fd7baf5af2930335d2554ddb7' 54 | sm4 = Sm4(key, DO_ENCRYPT) 55 | ciphertext = sm4.encrypt(plaintext) 56 | self.assertEqual(ciphertext, bytes.fromhex(ciphertext_hex)) 57 | sm4 = Sm4(key, DO_DECRYPT) 58 | decrypted = sm4.encrypt(ciphertext) 59 | self.assertEqual(decrypted, plaintext) 60 | 61 | def test_sm4_cbc(self): 62 | key = b'1234567812345678' 63 | iv = b'1234567812345678' 64 | plaintext = b'abc' 65 | ciphertext_hex = '532b22f9a096e7e5b8d84a620f0f7078' 66 | sm4_cbc = Sm4Cbc(key, iv, DO_ENCRYPT) 67 | ciphertext = sm4_cbc.update(plaintext) 68 | ciphertext += sm4_cbc.finish() 69 | self.assertEqual(ciphertext, bytes.fromhex(ciphertext_hex)) 70 | sm4_cbc = Sm4Cbc(key, iv, DO_DECRYPT) 71 | decrypted = sm4_cbc.update(ciphertext) 72 | decrypted += sm4_cbc.finish() 73 | self.assertEqual(decrypted, plaintext) 74 | 75 | def test_sm4_ctr(self): 76 | key = b'1234567812345678' 77 | iv = b'1234567812345678' 78 | plaintext = b'abc' 79 | ciphertext_hex = '890106' 80 | sm4_ctr = Sm4Ctr(key, iv) 81 | ciphertext = sm4_ctr.update(plaintext) 82 | ciphertext += sm4_ctr.finish() 83 | self.assertEqual(ciphertext, bytes.fromhex(ciphertext_hex)) 84 | sm4_ctr = Sm4Ctr(key, iv) 85 | decrypted = sm4_ctr.update(ciphertext) 86 | decrypted += sm4_ctr.finish() 87 | self.assertEqual(decrypted, plaintext) 88 | 89 | def test_sm4_gcm(self): 90 | key = b'1234567812345678' 91 | iv = b'0123456789ab' 92 | aad = b'Additional Authenticated Data' 93 | taglen = 16 94 | plaintext = b'abc' 95 | ciphertext_hex = '7d8bd8fdc7ea3b04c15fb61863f2292c15eeaa' 96 | sm4_gcm = Sm4Gcm(key, iv, aad, taglen, DO_ENCRYPT) 97 | ciphertext = sm4_gcm.update(plaintext) 98 | ciphertext += sm4_gcm.finish() 99 | self.assertEqual(ciphertext, bytes.fromhex(ciphertext_hex)) 100 | sm4_gcm = Sm4Gcm(key, iv, aad, taglen, DO_DECRYPT) 101 | decrypted = sm4_gcm.update(ciphertext) 102 | decrypted += sm4_gcm.finish() 103 | self.assertEqual(decrypted, plaintext) 104 | 105 | def test_zuc(self): 106 | key = b'1234567812345678' 107 | iv = b'1234567812345678' 108 | plaintext = b'abc' 109 | ciphertext_hex = '3d144b' 110 | zuc = Zuc(key, iv) 111 | ciphertext = zuc.update(plaintext) 112 | ciphertext += zuc.finish() 113 | self.assertEqual(ciphertext, bytes.fromhex(ciphertext_hex)) 114 | zuc = Zuc(key, iv) 115 | decrypted = zuc.update(ciphertext) 116 | decrypted += zuc.finish() 117 | self.assertEqual(decrypted, plaintext) 118 | 119 | def test_sm2_key(self): 120 | dgst = bytes.fromhex('66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0') 121 | plaintext = b'abc' 122 | sm2 = Sm2Key() 123 | sm2.generate_key() 124 | sm2.export_encrypted_private_key_info_pem('sm2.pem', 'password') 125 | sm2.export_public_key_info_pem('sm2pub.pem') 126 | sm2pri = Sm2Key() 127 | sm2pri.import_encrypted_private_key_info_pem('sm2.pem', 'password') 128 | sm2pub = Sm2Key() 129 | sm2pub.import_public_key_info_pem("sm2pub.pem"); 130 | sig = sm2pri.sign(dgst) 131 | verify_ret = sm2pub.verify(dgst, sig) 132 | self.assertTrue(verify_ret) 133 | ciphertext = sm2pub.encrypt(plaintext) 134 | decrypted = sm2pri.decrypt(ciphertext) 135 | self.assertEqual(decrypted, plaintext) 136 | 137 | def test_sm2_id(self): 138 | pem_txt = '''\ 139 | -----BEGIN PUBLIC KEY----- 140 | MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE+XVF76aof3ZtBVUwXobDQwQn+Sb2 141 | ethykPiYkXDLFdLnTrqr0b9QuA63DPdyrxJS3LZZwp9qzaMSyStai8+nrQ== 142 | -----END PUBLIC KEY-----''' 143 | with open('pub.pem', 'w') as file: 144 | file.write(pem_txt) 145 | file.close() 146 | z_hex = '4e469c92c425960603a315491bb2181c2f25939172775e223e1759b413cfc8ba' 147 | sm2pub = Sm2Key() 148 | sm2pub.import_public_key_info_pem('pub.pem') 149 | z = sm2pub.compute_z(SM2_DEFAULT_ID) 150 | self.assertEqual(z, bytes.fromhex(z_hex)) 151 | 152 | def test_sm2_sig(self): 153 | sm2 = Sm2Key() 154 | sm2.generate_key() 155 | sign = Sm2Signature(sm2, SM2_DEFAULT_ID, DO_SIGN) 156 | sign.update(b'abc') 157 | sig = sign.sign() 158 | verify = Sm2Signature(sm2, SM2_DEFAULT_ID, DO_VERIFY) 159 | verify.update(b'abc') 160 | verify_ret = verify.verify(sig) 161 | self.assertTrue(verify_ret) 162 | 163 | def test_sm9_enc(self): 164 | master_key = Sm9EncMasterKey() 165 | master_key.generate_master_key() 166 | master_key.export_encrypted_master_key_info_pem('enc_msk.pem', 'password') 167 | master_key.export_public_master_key_pem('enc_mpk.pem') 168 | master_pub = Sm9EncMasterKey() 169 | master_pub.import_public_master_key_pem('enc_mpk.pem') 170 | ciphertext = master_pub.encrypt(b'plaintext', 'Alice') 171 | master = Sm9EncMasterKey() 172 | master.import_encrypted_master_key_info_pem('enc_msk.pem', 'password') 173 | key = master.extract_key('Alice') 174 | plaintext = key.decrypt(ciphertext) 175 | self.assertEqual(plaintext, b'plaintext') 176 | 177 | def test_sm9_sign(self): 178 | master_key = Sm9SignMasterKey() 179 | master_key.generate_master_key() 180 | master_key.export_encrypted_master_key_info_pem('sign_msk.pem', 'password') 181 | master_key.export_public_master_key_pem('sign_mpk.pem') 182 | master = Sm9SignMasterKey() 183 | master.import_encrypted_master_key_info_pem('sign_msk.pem', 'password') 184 | key = master.extract_key('Alice') 185 | sign = Sm9Signature(DO_SIGN) 186 | sign.update(b'message') 187 | sig = sign.sign(key) 188 | master_pub = Sm9SignMasterKey() 189 | master_pub.import_public_master_key_pem('sign_mpk.pem') 190 | verify = Sm9Signature(DO_VERIFY) 191 | verify.update(b'message') 192 | ret = verify.verify(sig, master_pub, 'Alice') 193 | self.assertTrue(ret) 194 | 195 | def test_sm2_cert(self): 196 | cert_txt = '''\ 197 | -----BEGIN CERTIFICATE----- 198 | MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG 199 | EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw 200 | MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO 201 | UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE 202 | MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT 203 | V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti 204 | W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ 205 | MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b 206 | 53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI 207 | pDoiVhsLwg== 208 | -----END CERTIFICATE-----''' 209 | with open('ROOTCA.pem', 'w') as file: 210 | file.write(cert_txt) 211 | file.close() 212 | 213 | cert = Sm2Certificate() 214 | cert.import_pem('ROOTCA.pem') 215 | serial = cert.get_serial_number() 216 | self.assertTrue(len(serial) > 0) 217 | validity = cert.get_validity() 218 | self.assertTrue(validity.not_before < validity.not_after) 219 | issuer = cert.get_issuer() 220 | self.assertTrue(len(issuer) > 1) 221 | subject = cert.get_subject() 222 | self.assertTrue(len(subject) > 1) 223 | public_key = cert.get_subject_public_key() 224 | public_key.export_public_key_info_pem('public_key.pem') 225 | public_key.import_public_key_info_pem('public_key.pem') 226 | ret = cert.verify_by_ca_certificate(cert, SM2_DEFAULT_ID) 227 | self.assertTrue(ret) 228 | 229 | 230 | if __name__ == '__main__': 231 | unittest.main() 232 | 233 | --------------------------------------------------------------------------------