├── .github ├── dependabot.yml └── workflows │ ├── basicCI.yml │ └── gradle.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.gradle ├── config └── checkstyle │ └── checkstyle.xml └── src ├── main └── java │ └── twgc │ └── gm │ ├── pool │ ├── SM2EngineFactory.java │ ├── SM2EnginePool.java │ ├── SM2PoolConfig.java │ ├── SM3DigestFactory.java │ ├── SM3DigestPool.java │ ├── SM3PoolConfig.java │ ├── SM4CipherFactory.java │ ├── SM4CipherPool.java │ └── SM4PoolConfig.java │ ├── random │ ├── CertSNAllocator.java │ ├── RandomSNAllocator.java │ └── SecureRandomFactory.java │ ├── sm2 │ ├── SM2Util.java │ └── SM2X509CertFactory.java │ ├── sm2sm3 │ └── SM2SM3Util.java │ ├── sm3 │ └── SM3Util.java │ ├── sm4 │ ├── SM4Cipher.java │ ├── SM4ModeAndPaddingEnum.java │ └── SM4Util.java │ └── utils │ ├── ConfigLoader.java │ └── Const.java └── test ├── java ├── RandomSNAllocatorTest.java ├── SM2SM3UtilThreadTest.java ├── SM2UtilTest.java ├── SM2UtilThreadTest.java ├── SM3UtilTest.java ├── SM3UtilThreadTest.java ├── SM4InterationTest.java └── SM4UtilTest.java └── resources ├── pool-config.yaml └── testdata.yml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: maven 4 | directory: / 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: github-actions 8 | directory: / 9 | schedule: 10 | interval: daily -------------------------------------------------------------------------------- /.github/workflows/basicCI.yml: -------------------------------------------------------------------------------- 1 | name: basicCI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | workflow_dispatch: 7 | schedule: 8 | - cron: '0 0 * * 2' # https://crontab.guru/#0_0_*_*_2 9 | jobs: 10 | java_test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-java@v4 15 | with: 16 | distribution: temurin 17 | java-version: 11 18 | - uses: gradle/gradle-build-action@v3.4.2 19 | with: 20 | gradle-version: 6.5 21 | - run: gradle clean build 22 | 23 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Publish package to GitHub Packages 2 | on: 3 | workflow_dispatch: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: read 12 | packages: write 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-java@v4 16 | with: 17 | java-version: '11' 18 | distribution: 'temurin' 19 | - name: Validate Gradle wrapper 20 | uses: gradle/wrapper-validation-action@88425854a36845f9c881450d9660b5fd46bee142 21 | - name: Publish package 22 | uses: gradle/gradle-build-action@66535aaf56f831b35e3a8481c9c99b665b84dd45 23 | with: 24 | arguments: publish 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | java-gm.iml 2 | .gradle 3 | .idea 4 | .vscode 5 | build 6 | *.pem 7 | gradle/ 8 | gradlew 9 | gradlew.bat 10 | out/ -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # 开发者文档 2 | 3 | ## [checkstyle](https://checkstyle.sourceforge.io/) 4 | 项目使用`checkstyle`作为lint工具。格式参考`./config/checkstyle/checkstyle.xml` 5 | 如果遇到lint错误,可以参考该目录进行修复或者参考命令行提示。 6 | `./java-gm/build/reports/checkstyle/checkstyle.html` 7 | 8 | ## gradle 9 | 项目使用gradle,请通过`gradle clean test`来进行单元测试。目前的流程: 10 | ```shell script 11 | gradle clean test --dry-run 12 | :clean SKIPPED 13 | :compileJava SKIPPED 14 | :processResources SKIPPED 15 | :classes SKIPPED 16 | :compileTestJava SKIPPED 17 | :processTestResources SKIPPED 18 | :testClasses SKIPPED 19 | :test SKIPPED 20 | ``` 21 | 22 | ## 互操作测试 23 | 请大家尽量在提交代码前在本地进行互操作认证,步骤如下: 24 | 25 | - gradle clean build 26 | - cd ${workdir} 27 | - git clone https://github.com/Hyperledger-TWGC/fabric-gm-plugins 28 | - cp -f java-gm/*.pem ${workdir}/fabric-gm-plugins/interop/testdata 29 | - cd ${workdir}/fabric-gm-plugins/interop 30 | - go test tjjavaImport_test.go 31 | - go test tjjaveExport_test.go 32 | - cp -f ${workdir}/fabric-gm-plugins/interop/testdata/*.pem java-gm 33 | - cd java-gm 34 | - gradle clean build 35 | -------------------------------------------------------------------------------- /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 | # java-gm 2 | 3 | 基于BouncyCastle实现国密算法SM2、SM3、SM4的操作类,并验证与其他语言(NodeJS、Go)实现的国密库的互操作性。 4 | 5 | [![Build Status](https://dev.azure.com/Hyperledger/TWGC/_apis/build/status/Hyperledger-TWGC.java-gm?branchName=master)](https://dev.azure.com/Hyperledger/TWGC/_build/latest?definitionId=129&branchName=master) 6 | 7 | ## Feature 功能支持列表 8 | 9 | | SM2功能 | 支持范围 | 10 | | ---- | ---- | 11 | | Generate KeyPair | `是` | 12 | | Derive public key from private key | `是` | 13 | | Sign | `是` | 14 | | Verify | `是` | 15 | | PEM格式导出 | `私钥/公钥/CSR`| 16 | | PEM文件加密 | RFC5958 | 17 | | PEM格式导入 | `私钥/公钥/CSR` | 18 | 19 | 备注: 20 | 21 | C1C3C2和SM2SM3作为默认的加密和Hash算法,同时接口层面保留C1C2C3和其他Hash方式的支持。 22 | 23 | | SM4功能 | 支持范围 | 24 | | ---- | ---- | 25 | | Generate Key | | 26 | | Encrypt, Decrypt | `是` | 27 | | PEM格式导出 | | 28 | | PEM文件加密 | | 29 | | 分组模式 | ECB/CBC/CFB/OFB/CTR | 30 | 31 | 32 | | SM3功能 | 支持范围 | 33 | | ---- | ---- | 34 | | 当前语言Hash接口兼容 | `是` | 35 | 36 | ## ObjectPoolSupport 37 | 我们采用了可配置的对象池的方案来提供相对便利的多线程支持。 38 | 如: 39 | ``` 40 | static SM2EnginePool sm2EnginePool = new SM2EnginePool(SM2Engine.Mode.C1C3C2); 41 | SM2Engine sm2Engine = sm2EnginePool.borrowObject(); 42 | byte[] encrypted = instance.encrypt(sm2Engine, this.pubKey, message); 43 | byte[] rs = instance.decrypt(sm2Engine, this.privKey, encrypted); 44 | Assert.assertEquals(new String(message), new String(rs)); 45 | sm2EnginePool.returnObject(sm2Engine); 46 | ``` 47 | 具体对象池的配置参考如下文件: 48 | `pool-config.yaml`, 49 | 我们目前尚未使用统一的对象池,而是根据不同密码学算法使用不同的对象池。 50 | 这里是考虑到不同算法的执行效率并不相同,因此例如sm2+sm3的操作: 51 | 我们可以考虑用一个数量更小的sm3对象池,来对接一个sm2对象池,来实现摘要(SM3)签名(SM2)这一密码学实现。 52 | 53 | ## Terminology 术语 54 | - SM2: 国密椭圆曲线算法库 55 | - SM3: 国密hash算法库 56 | - SM4: 国密分组密码算法库 57 | 58 | ## How to Contribute 贡献须知 59 | We welcome contributions to Hyperledger in many forms, and there's always plenty to do! 60 | 61 | Please visit the [contributors guide](CONTRIBUTING.md) in the 62 | docs to learn how to make contributions to this exciting project. 63 | 64 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 65 | 66 | ## License 许可证 67 | Hyperledger Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the [LICENSE](LICENSE) file. 68 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'checkstyle' 4 | id 'maven-publish' 5 | id 'jacoco' 6 | } 7 | 8 | group 'twgc' 9 | version '0.1.4' 10 | 11 | sourceCompatibility = 1.8 12 | targetCompatibility = 1.8 13 | 14 | allprojects { 15 | repositories { 16 | maven { 17 | url 'https://maven.aliyun.com/repository/public/' 18 | } 19 | mavenLocal() 20 | mavenCentral() 21 | } 22 | } 23 | 24 | tasks.withType(JavaCompile) { 25 | options.encoding = "UTF-8" 26 | } 27 | 28 | tasks.withType(Test) { 29 | systemProperty "file.encoding", "UTF-8" 30 | } 31 | 32 | checkstyle { 33 | toolVersion '6.11.1' 34 | showViolations true 35 | } 36 | 37 | check.dependsOn 'checkstyle' 38 | 39 | jacoco { 40 | toolVersion = "0.8.5" 41 | reportsDir = file("$buildDir/customJacocoReportDir") 42 | } 43 | 44 | jacocoTestReport { 45 | reports { 46 | xml.enabled false 47 | csv.enabled false 48 | html.destination file("${buildDir}/jacocoHtml") 49 | } 50 | } 51 | 52 | jacocoTestCoverageVerification { 53 | violationRules { 54 | rule { 55 | limit { 56 | minimum = 0.85 57 | } 58 | } 59 | 60 | rule { 61 | enabled = false 62 | element = 'CLASS' 63 | includes = ['twgc.gm.*'] 64 | 65 | limit { 66 | counter = 'LINE' 67 | value = 'TOTALCOUNT' 68 | maximum = 0.90 69 | } 70 | } 71 | } 72 | } 73 | 74 | task checkstyle(type: Checkstyle) { 75 | //configFile file("${project.projectDir}/config/checkstyle/checkstyle.xml") 76 | // Where my checkstyle config is... 77 | //configProperties.checkstyleSuppressionsPath = file("${project.projectDirr}/config/quality/suppressions.xml").absolutePath // Where is my suppressions file for checkstyle is... 78 | source 'src' 79 | include '**/*.java' 80 | exclude "**/test/**" 81 | classpath = files() 82 | } 83 | 84 | jacocoTestCoverageVerification.dependsOn 'jacocoTestReport' 85 | build.dependsOn 'jacocoTestCoverageVerification' 86 | 87 | publishing { 88 | repositories { 89 | maven { 90 | name = "GitHubPackages" 91 | url = uri("https://maven.pkg.github.com/Hyperledger-TWGC/java-gm") 92 | credentials { 93 | username = "davidkhala" 94 | password = System.getProperty("GITHUB_TOKEN_DAVIDKHALA") 95 | } 96 | } 97 | } 98 | publications { 99 | gpr(MavenPublication) { 100 | artifactId = 'java-gm' 101 | from(components.java) 102 | } 103 | } 104 | } 105 | 106 | dependencies { 107 | implementation group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.67' 108 | implementation group: 'org.apache.commons', name: 'commons-pool2', version: '2.9.0' 109 | implementation group: 'org.yaml', name: 'snakeyaml', version: '1.29' 110 | testImplementation group: 'junit', name: 'junit', version: '4.12' 111 | testImplementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.11' 112 | } 113 | -------------------------------------------------------------------------------- /config/checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 12 | 27 | 28 | 29 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/pool/SM2EngineFactory.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.pool; 2 | 3 | import org.apache.commons.pool2.BasePooledObjectFactory; 4 | import org.apache.commons.pool2.PooledObject; 5 | import org.apache.commons.pool2.impl.DefaultPooledObject; 6 | import org.bouncycastle.crypto.engines.SM2Engine; 7 | 8 | /** 9 | * @author Sean 10 | * @Description: SM2Engine工厂 11 | * @date 2021/6/12 12 | */ 13 | public class SM2EngineFactory extends BasePooledObjectFactory { 14 | 15 | private SM2Engine.Mode mode; 16 | 17 | public SM2EngineFactory(SM2Engine.Mode mode) { 18 | this.mode = mode; 19 | } 20 | 21 | @Override 22 | public SM2Engine create() { 23 | return new SM2Engine(mode); 24 | } 25 | 26 | @Override 27 | public PooledObject wrap(SM2Engine obj) { 28 | return new DefaultPooledObject<>(obj); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/pool/SM2EnginePool.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.pool; 2 | 3 | import java.io.IOException; 4 | 5 | import org.apache.commons.pool2.impl.GenericObjectPool; 6 | import org.bouncycastle.crypto.engines.SM2Engine; 7 | import twgc.gm.utils.Const; 8 | 9 | /** 10 | * @author Sean 11 | * @Description: SM2Engine对象池 12 | * @date 2021/6/12 13 | */ 14 | public class SM2EnginePool extends GenericObjectPool { 15 | 16 | public SM2EnginePool(int max, SM2Engine.Mode mode) { 17 | this(1, max, mode); 18 | } 19 | 20 | public SM2EnginePool(int init, int max, SM2Engine.Mode mode) { 21 | super(new SM2EngineFactory(mode)); 22 | setMaxTotal(max); 23 | setMinIdle(init); 24 | } 25 | 26 | public SM2EnginePool(SM2Engine.Mode mode, SM2PoolConfig config) { 27 | super(new SM2EngineFactory(mode), config); 28 | } 29 | 30 | public SM2EnginePool(SM2Engine.Mode mode) throws IOException { 31 | super(new SM2EngineFactory(mode), new SM2PoolConfig(Const.POOL_CONFIG)); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/pool/SM2PoolConfig.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.pool; 2 | 3 | import java.io.IOException; 4 | import java.util.Map; 5 | 6 | import org.apache.commons.pool2.impl.GenericObjectPoolConfig; 7 | import twgc.gm.utils.ConfigLoader; 8 | import twgc.gm.utils.Const; 9 | 10 | public class SM2PoolConfig extends GenericObjectPoolConfig { 11 | 12 | public SM2PoolConfig() { 13 | } 14 | 15 | public SM2PoolConfig(String file) throws IOException { 16 | this.setProperties(ConfigLoader.loadConfig(this.getClass().getResourceAsStream(file), Const.SM2)); 17 | } 18 | 19 | private void setProperties(Map map) { 20 | this.setMaxTotal(Integer.valueOf(map.get("maxTotal").toString())); 21 | this.setMaxIdle(Integer.valueOf(map.get("maxIdle").toString())); 22 | this.setMinIdle(Integer.valueOf(map.get("minIdle").toString())); 23 | this.setMaxWaitMillis(Integer.valueOf(map.get("maxWaitMillis").toString())); 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/twgc/gm/pool/SM3DigestFactory.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.pool; 2 | 3 | import org.apache.commons.pool2.BasePooledObjectFactory; 4 | import org.apache.commons.pool2.PooledObject; 5 | import org.apache.commons.pool2.impl.DefaultPooledObject; 6 | import org.bouncycastle.crypto.digests.SM3Digest; 7 | 8 | /** 9 | * @author Sean 10 | * @Description: SM3Digest工厂 11 | * @date 2021/1/21 12 | */ 13 | public class SM3DigestFactory extends BasePooledObjectFactory { 14 | @Override 15 | public SM3Digest create() { 16 | return new SM3Digest(); 17 | } 18 | 19 | @Override 20 | public PooledObject wrap(SM3Digest obj) { 21 | return new DefaultPooledObject<>(obj); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/pool/SM3DigestPool.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.pool; 2 | 3 | import java.io.IOException; 4 | 5 | import org.apache.commons.pool2.impl.GenericObjectPool; 6 | import org.bouncycastle.crypto.digests.SM3Digest; 7 | import twgc.gm.utils.Const; 8 | /** 9 | * @author Sean 10 | * @Description: SM3Digest对象池 11 | * @date 2021/1/21 12 | */ 13 | public class SM3DigestPool extends GenericObjectPool { 14 | 15 | private static final SM3DigestFactory sm3DigestFactory = new SM3DigestFactory(); 16 | 17 | public SM3DigestPool(int max) { 18 | this(1, max); 19 | } 20 | 21 | public SM3DigestPool(int init, int max) { 22 | super(sm3DigestFactory); 23 | setMaxTotal(max); 24 | setMinIdle(init); 25 | } 26 | 27 | public SM3DigestPool(SM3PoolConfig config) { 28 | super(sm3DigestFactory, config); 29 | } 30 | 31 | public SM3DigestPool() throws IOException { 32 | super(sm3DigestFactory, new SM3PoolConfig(Const.POOL_CONFIG)); 33 | } 34 | 35 | @Override 36 | public void returnObject(SM3Digest obj) { 37 | if (obj != null) { 38 | obj.reset(); 39 | } 40 | super.returnObject(obj); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/pool/SM3PoolConfig.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.pool; 2 | 3 | import java.io.IOException; 4 | import java.util.Map; 5 | 6 | import org.apache.commons.pool2.impl.GenericObjectPoolConfig; 7 | import twgc.gm.utils.ConfigLoader; 8 | import twgc.gm.utils.Const; 9 | 10 | public class SM3PoolConfig extends GenericObjectPoolConfig { 11 | 12 | public SM3PoolConfig() { 13 | } 14 | 15 | public SM3PoolConfig(String file) throws IOException { 16 | this.setProperties(ConfigLoader.loadConfig(this.getClass().getResourceAsStream(file), Const.SM3)); 17 | } 18 | 19 | private void setProperties(Map map) { 20 | this.setMaxTotal(Integer.valueOf(map.get("maxTotal").toString())); 21 | this.setMaxIdle(Integer.valueOf(map.get("maxIdle").toString())); 22 | this.setMinIdle(Integer.valueOf(map.get("minIdle").toString())); 23 | this.setMaxWaitMillis(Integer.valueOf(map.get("maxWaitMillis").toString())); 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/twgc/gm/pool/SM4CipherFactory.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.pool; 2 | 3 | import org.apache.commons.pool2.BasePooledObjectFactory; 4 | import org.apache.commons.pool2.PooledObject; 5 | import org.apache.commons.pool2.impl.DefaultPooledObject; 6 | import twgc.gm.sm4.SM4Cipher; 7 | 8 | /** 9 | * @author Sean 10 | * @Description: SM4Cipher工厂 11 | * @date 2021/1/21 12 | */ 13 | public class SM4CipherFactory extends BasePooledObjectFactory { 14 | @Override 15 | public SM4Cipher create() throws Exception { 16 | return new SM4Cipher(); 17 | } 18 | 19 | @Override 20 | public PooledObject wrap(SM4Cipher obj) { 21 | return new DefaultPooledObject<>(obj); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/pool/SM4CipherPool.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.pool; 2 | 3 | import java.io.IOException; 4 | 5 | import org.apache.commons.pool2.impl.GenericObjectPool; 6 | import twgc.gm.sm4.SM4Cipher; 7 | import twgc.gm.utils.Const; 8 | 9 | 10 | /** 11 | * @author Sean 12 | * @Description: SM4Cipher对象池 13 | * @date 2021/1/21 14 | */ 15 | public class SM4CipherPool extends GenericObjectPool { 16 | 17 | private static final SM4CipherFactory sm4CipherFactory = new SM4CipherFactory(); 18 | 19 | public SM4CipherPool(int max) { 20 | this(1, max); 21 | } 22 | 23 | public SM4CipherPool(int init, int max) { 24 | super(sm4CipherFactory); 25 | setMaxTotal(max); 26 | setMinIdle(init); 27 | } 28 | 29 | public SM4CipherPool(SM4PoolConfig config) { 30 | super(sm4CipherFactory, config); 31 | } 32 | 33 | public SM4CipherPool() throws IOException { 34 | super(sm4CipherFactory, new SM4PoolConfig(Const.POOL_CONFIG)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/pool/SM4PoolConfig.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.pool; 2 | 3 | import java.io.IOException; 4 | import java.util.Map; 5 | 6 | import org.apache.commons.pool2.impl.GenericObjectPoolConfig; 7 | import twgc.gm.utils.ConfigLoader; 8 | import twgc.gm.utils.Const; 9 | 10 | public class SM4PoolConfig extends GenericObjectPoolConfig { 11 | 12 | public SM4PoolConfig() { 13 | } 14 | 15 | public SM4PoolConfig(String file) throws IOException { 16 | this.setProperties(ConfigLoader.loadConfig(this.getClass().getResourceAsStream(file), Const.SM4)); 17 | } 18 | 19 | private void setProperties(Map map) { 20 | this.setMaxTotal(Integer.valueOf(map.get("maxTotal").toString())); 21 | this.setMaxIdle(Integer.valueOf(map.get("maxIdle").toString())); 22 | this.setMinIdle(Integer.valueOf(map.get("minIdle").toString())); 23 | this.setMaxWaitMillis(Integer.valueOf(map.get("maxWaitMillis").toString())); 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/twgc/gm/random/CertSNAllocator.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.random; 2 | 3 | import java.math.BigInteger; 4 | 5 | /** 6 | * @author liqs 7 | * @version 1.0 8 | * @date 2021/1/19 14:27 9 | * ref:https://github.com/ZZMarquis/gmhelper 10 | */ 11 | public interface CertSNAllocator { 12 | BigInteger nextSerialNumber(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/random/RandomSNAllocator.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.random; 2 | 3 | import java.math.BigInteger; 4 | import java.security.SecureRandom; 5 | 6 | /** 7 | * @author liqs 8 | * @version 1.0 9 | * @date 2021/1/19 14:31 10 | * ref:https://github.com/ZZMarquis/gmhelper 11 | */ 12 | public class RandomSNAllocator implements CertSNAllocator { 13 | /** 14 | * The highest bit is always set to 1, so the effective bit length is bitLen - 1. To ensure that 15 | * at least 64 bit entropy, bitLen must be at least 65. 16 | */ 17 | private static final int MIN_SERIALNUMBER_SIZE = 65; 18 | 19 | /** 20 | * Since serial number should be positive and maximal 20 bytes, the maximal value of bitLen is 21 | * 159. 22 | */ 23 | private static final int MAX_SERIALNUMBER_SIZE = 159; 24 | 25 | private static int[] andMasks = new int[] {0xFF, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F}; 26 | 27 | private static int[] orMasks = new int[] {0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40}; 28 | 29 | private final SecureRandom random; 30 | 31 | private final int bitLen; 32 | 33 | /** 34 | * Constructor with the bitLen = 65. 35 | */ 36 | public RandomSNAllocator() { 37 | this(MIN_SERIALNUMBER_SIZE); 38 | } 39 | 40 | /** 41 | * Constructor with the specification of bitLen. 42 | * @param bitLen bit length of the serial number. The highest bit is always set to 1, so the 43 | * effective bit length is bitLen - 1. Valid value is [65, 159]. 44 | */ 45 | public RandomSNAllocator(int bitLen) { 46 | if (bitLen < MIN_SERIALNUMBER_SIZE || bitLen > MAX_SERIALNUMBER_SIZE) { 47 | throw new IllegalArgumentException(String.format( 48 | "%s may not be out of the range [%d, %d]: %d", 49 | "bitLen", MIN_SERIALNUMBER_SIZE, MAX_SERIALNUMBER_SIZE, bitLen)); 50 | } 51 | 52 | this.random = new SecureRandom(); 53 | this.bitLen = bitLen; 54 | } 55 | 56 | @Override 57 | public BigInteger nextSerialNumber() { 58 | final byte[] rdnBytes = new byte[(bitLen + 7) / 8]; 59 | final int ci = bitLen % 8; 60 | 61 | random.nextBytes(rdnBytes); 62 | if (ci != 0) { 63 | rdnBytes[0] = (byte) (rdnBytes[0] & andMasks[ci]); 64 | } 65 | rdnBytes[0] = (byte) (rdnBytes[0] | orMasks[ci]); 66 | 67 | return new BigInteger(1, rdnBytes); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/random/SecureRandomFactory.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.random; 2 | 3 | import java.security.SecureRandom; 4 | 5 | /** 6 | * @author Sean 7 | * @Description: SecureRandomHolder 8 | * @date 2021/6/13 9 | */ 10 | public class SecureRandomFactory { 11 | 12 | private SecureRandomFactory() { } 13 | 14 | public static SecureRandom getSecureRandom() { 15 | return SecureRandomFactory.CachedSecureRandomHolder.instance; 16 | } 17 | 18 | private static class CachedSecureRandomHolder { 19 | public static SecureRandom instance = new SecureRandom(); 20 | 21 | private CachedSecureRandomHolder() { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/sm2/SM2Util.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.sm2; 2 | 3 | import java.io.*; 4 | import java.math.BigInteger; 5 | import java.security.*; 6 | import java.security.cert.CertificateEncodingException; 7 | import java.security.cert.CertificateException; 8 | import java.security.cert.CertificateFactory; 9 | import java.security.cert.X509Certificate; 10 | import java.security.spec.ECGenParameterSpec; 11 | import java.security.spec.InvalidKeySpecException; 12 | import java.security.spec.X509EncodedKeySpec; 13 | import java.util.function.Supplier; 14 | import javax.security.auth.x500.X500Principal; 15 | 16 | import org.bouncycastle.asn1.gm.GMNamedCurves; 17 | import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; 18 | import org.bouncycastle.asn1.x9.X9ECParameters; 19 | import org.bouncycastle.crypto.InvalidCipherTextException; 20 | import org.bouncycastle.crypto.engines.SM2Engine; 21 | import org.bouncycastle.crypto.params.ECDomainParameters; 22 | import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 23 | import org.bouncycastle.crypto.params.ECPublicKeyParameters; 24 | import org.bouncycastle.crypto.params.ParametersWithRandom; 25 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; 26 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; 27 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 28 | import org.bouncycastle.jce.spec.ECParameterSpec; 29 | import org.bouncycastle.jce.spec.ECPublicKeySpec; 30 | import org.bouncycastle.math.ec.ECPoint; 31 | import org.bouncycastle.math.ec.FixedPointCombMultiplier; 32 | import org.bouncycastle.openssl.PEMParser; 33 | import org.bouncycastle.openssl.PKCS8Generator; 34 | import org.bouncycastle.openssl.jcajce.*; 35 | import org.bouncycastle.operator.ContentSigner; 36 | import org.bouncycastle.operator.InputDecryptorProvider; 37 | import org.bouncycastle.operator.OperatorCreationException; 38 | import org.bouncycastle.operator.OutputEncryptor; 39 | import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; 40 | import org.bouncycastle.pkcs.PKCS10CertificationRequest; 41 | import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; 42 | import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; 43 | import org.bouncycastle.pkcs.PKCSException; 44 | import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; 45 | import org.bouncycastle.util.io.pem.PemObject; 46 | import org.bouncycastle.util.io.pem.PemReader; 47 | import org.bouncycastle.util.io.pem.PemWriter; 48 | import twgc.gm.random.SecureRandomFactory; 49 | import twgc.gm.utils.Const; 50 | 51 | /** 52 | * @author SamYuan; 吴仙杰 53 | * @Description 国密SM2工具类, 算法提供者 Bouncy Castle 54 | * @date 2020/10 55 | * ref: 56 | * https://tools.ietf.org/html/draft-shen-sm2-ecdsa-02 57 | * http://gmssl.org/docs/oid.html 58 | * http://www.jonllen.com/jonllen/work/164.aspx 59 | * https://blog.csdn.net/Vincent2014Linux/article/details/108668186 60 | * https://www.pixelstech.net/article/1464167276-Generating-CSR-using-Java 61 | * http://senthadev.com/generating-csr-using-java-and-bouncycastle-api.html 62 | * https://github.com/Trisia/alg-sm2-demo 63 | */ 64 | public class SM2Util { 65 | 66 | public SM2Util() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { 67 | if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { 68 | Security.addProvider(new BouncyCastleProvider()); 69 | } 70 | signature = Signature.getInstance(Const.SM3SM2_VALUE, BouncyCastleProvider.PROVIDER_NAME); 71 | generator = KeyPairGenerator.getInstance(Const.EC_VALUE, BouncyCastleProvider.PROVIDER_NAME); 72 | generator.initialize(new ECGenParameterSpec(Const.CURVE_NAME)); 73 | } 74 | 75 | public Signature getSignature() { 76 | return signature; 77 | } 78 | 79 | public void setSignature(Signature signature) { 80 | this.signature = signature; 81 | } 82 | 83 | private Signature signature; 84 | private static final X9ECParameters X_9_EC_PARAMETERS = GMNamedCurves.getByName(Const.CURVE_NAME); 85 | private static final ECDomainParameters EC_DOMAIN_PARAMETERS = new ECDomainParameters(X_9_EC_PARAMETERS.getCurve(), X_9_EC_PARAMETERS.getG(), X_9_EC_PARAMETERS.getN()); 86 | private static final ECParameterSpec PARAMETER_SPEC = new ECParameterSpec(X_9_EC_PARAMETERS.getCurve(), X_9_EC_PARAMETERS.getG(), X_9_EC_PARAMETERS.getN()); 87 | private static KeyPairGenerator generator; 88 | private static final JcaPEMKeyConverter CONVERTER = new JcaPEMKeyConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME); 89 | 90 | 91 | /** 92 | * 生成 PKCS#10 证书请求 93 | * 94 | * @return RSA P10 证书请求 Base64 字符串 95 | */ 96 | public KeyPair generatekeyPair() { 97 | return generator.generateKeyPair(); 98 | } 99 | 100 | public byte[] encrypt(SM2Engine sm2Engine, PublicKey publicKey, byte[] message) throws InvalidCipherTextException { 101 | BCECPublicKey localECPublicKey = (BCECPublicKey) publicKey; 102 | ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), EC_DOMAIN_PARAMETERS); 103 | sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, SecureRandomFactory.getSecureRandom())); 104 | return sm2Engine.processBlock(message, 0, message.length); 105 | } 106 | 107 | public byte[] decrypt(SM2Engine sm2Engine, PrivateKey privateKey, byte[] message) throws InvalidCipherTextException { 108 | BCECPrivateKey localECPrivateKey = (BCECPrivateKey) privateKey; 109 | ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(localECPrivateKey.getD(), EC_DOMAIN_PARAMETERS); 110 | sm2Engine.init(false, ecPrivateKeyParameters); 111 | return sm2Engine.processBlock(message, 0, message.length); 112 | } 113 | 114 | public byte[] sign(PrivateKey privateKey, byte[] message) throws SignatureException, InvalidKeyException { 115 | synchronized (this) { 116 | signature.initSign(privateKey, SecureRandomFactory.getSecureRandom()); 117 | signature.update(message); 118 | return signature.sign(); 119 | } 120 | } 121 | 122 | public boolean verify(PublicKey publicKey, byte[] message, byte[] sigBytes) throws InvalidKeyException, SignatureException { 123 | synchronized (this) { 124 | signature.initVerify(publicKey); 125 | signature.update(message); 126 | return signature.verify(sigBytes); 127 | } 128 | } 129 | 130 | public static PKCS10CertificationRequest generateCSR(KeyPair keyPair, X500Principal subject) throws OperatorCreationException { 131 | ContentSigner signer = new JcaContentSignerBuilder("SM3withSM2").build(keyPair.getPrivate()); 132 | PKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(subject, keyPair.getPublic()); 133 | return builder.build(signer); 134 | } 135 | 136 | public static String pemFrom(PrivateKey privateKey, String password) throws OperatorCreationException, IOException { 137 | StringWriter sw = new StringWriter(); 138 | try (JcaPEMWriter pemWriter = new JcaPEMWriter(sw)) { 139 | OutputEncryptor encryptor = null; 140 | if (password != null && password.length() > 0) { 141 | encryptor = new JceOpenSSLPKCS8EncryptorBuilder(PKCS8Generator.AES_256_CBC) 142 | .setProvider(BouncyCastleProvider.PROVIDER_NAME) 143 | .setRandom(SecureRandomFactory.getSecureRandom()) 144 | .setPasssword(password.toCharArray()) 145 | .build(); 146 | } 147 | PKCS8Generator generator = new JcaPKCS8Generator(privateKey, encryptor); 148 | pemWriter.writeObject(generator); 149 | } 150 | return sw.toString(); 151 | } 152 | 153 | public static String pemFrom(PublicKey publicKey) throws IOException { 154 | StringWriter sw = new StringWriter(); 155 | try (PemWriter pemWriter = new PemWriter(sw)) { 156 | PemObject pem = new PemObject("PUBLIC KEY", publicKey.getEncoded()); 157 | pemWriter.writeObject(pem); 158 | } 159 | return sw.toString(); 160 | } 161 | 162 | /** 163 | * 打印 OpenSSL PEM 格式文件字符串的 SSL 证书请求 CSR 文件内容 164 | * 165 | * @param csr 证书请求对象 166 | */ 167 | public static String pemFrom(PKCS10CertificationRequest csr) throws IOException { 168 | StringWriter sw = new StringWriter(); 169 | try (PemWriter pemWriter = new PemWriter(sw)) { 170 | PemObject pem = new PemObject("CERTIFICATE REQUEST", csr.getEncoded()); 171 | pemWriter.writeObject(pem); 172 | } 173 | return sw.toString(); 174 | } 175 | 176 | public static String pemFrom(X509Certificate x509Certificate) throws IOException, CertificateEncodingException { 177 | StringWriter sw = new StringWriter(); 178 | try (PemWriter pemWriter = new PemWriter(sw)) { 179 | PemObject pem = new PemObject("CERTIFICATE", x509Certificate.getEncoded()); 180 | pemWriter.writeObject(pem); 181 | } 182 | return sw.toString(); 183 | } 184 | 185 | public static PrivateKey loadPrivFromFile(String filename, String password) throws IOException, OperatorCreationException, PKCSException { 186 | return loadPriv(password, () -> { 187 | try { 188 | return new FileReader(filename); 189 | } catch (FileNotFoundException e) { 190 | throw new RuntimeException("Private key \"" + filename + "\" not found", e); 191 | } 192 | }); 193 | } 194 | 195 | public static PublicKey loadPublicFromFile(String filename) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException { 196 | return loadPublic(() -> { 197 | try { 198 | return new FileReader(filename); 199 | } catch (FileNotFoundException e) { 200 | throw new RuntimeException("Public key \"" + filename + "\" not found", e); 201 | } 202 | }); 203 | } 204 | 205 | public static X509Certificate loadX509CertificateFromFile(String filename) throws IOException, CertificateException, 206 | NoSuchProviderException { 207 | try (FileInputStream in = new FileInputStream(filename)) { 208 | CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); 209 | return (X509Certificate) cf.generateCertificate(in); 210 | } 211 | } 212 | 213 | /** 214 | * 从字符串加载私钥 215 | * 216 | * @param privateKey 字符串字私钥 217 | * @param password 密码 218 | * @return {@link PrivateKey} 私钥对象 219 | * @throws IOException 220 | * @throws OperatorCreationException 221 | * @throws PKCSException 222 | */ 223 | public static PrivateKey loadPrivFromString(String privateKey, String password) throws IOException, OperatorCreationException, PKCSException { 224 | return loadPriv(password, () -> new StringReader(privateKey)); 225 | } 226 | 227 | /** 228 | * 从字符串加载公钥 229 | * 230 | * @param publicKey 字符串公钥 231 | * @return {@link PublicKey} 公钥对象 232 | * @throws IOException 233 | * @throws NoSuchProviderException 234 | * @throws NoSuchAlgorithmException 235 | * @throws InvalidKeySpecException 236 | */ 237 | public static PublicKey loadPublicFromString(String publicKey) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException { 238 | return loadPublic(() -> new StringReader(publicKey)); 239 | } 240 | 241 | /** 242 | * 从字符串加载证书 243 | * 244 | * @param cert 字符串证书 245 | * @return {@link X509Certificate} 证书对象 246 | * @throws IOException 247 | * @throws CertificateException 248 | * @throws NoSuchProviderException 249 | */ 250 | public static X509Certificate loadX509CertificateFromString(String cert) throws IOException, CertificateException, NoSuchProviderException { 251 | try (InputStream in = new ByteArrayInputStream(cert.getBytes())) { 252 | CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); 253 | return (X509Certificate) cf.generateCertificate(in); 254 | } 255 | } 256 | 257 | public static PublicKey derivePublicFromPrivate(PrivateKey privateKey) { 258 | BCECPrivateKey localECPrivateKey = (BCECPrivateKey) privateKey; 259 | BigInteger d = localECPrivateKey.getD(); 260 | ECPoint ecpoint = new FixedPointCombMultiplier().multiply(GMNamedCurves.getByName(Const.CURVE_NAME).getG(), d); 261 | ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(ecpoint, PARAMETER_SPEC); 262 | return new BCECPublicKey(privateKey.getAlgorithm(), pubKeySpec, 263 | BouncyCastleProvider.CONFIGURATION); 264 | } 265 | 266 | /** 267 | * 加载私钥 268 | * 269 | * @param password 密码 270 | * @param fx {@link Reader} 回调函数 271 | * @return {@link PrivateKey} 272 | * @throws IOException 273 | * @throws OperatorCreationException 274 | * @throws PKCSException 275 | */ 276 | public static PrivateKey loadPriv(String password, Supplier fx) throws IOException, OperatorCreationException, PKCSException { 277 | PrivateKey priv = null; 278 | try (PEMParser pemParser = new PEMParser(fx.get())) { 279 | Object obj = pemParser.readObject(); 280 | if (password != null && password.length() > 0) { 281 | if (obj instanceof PKCS8EncryptedPrivateKeyInfo) { 282 | PKCS8EncryptedPrivateKeyInfo epkInfo = (PKCS8EncryptedPrivateKeyInfo) obj; 283 | InputDecryptorProvider decryptor = new JceOpenSSLPKCS8DecryptorProviderBuilder() 284 | .setProvider(BouncyCastleProvider.PROVIDER_NAME) 285 | .build(password.toCharArray()); 286 | PrivateKeyInfo pkInfo = epkInfo.decryptPrivateKeyInfo(decryptor); 287 | priv = CONVERTER.getPrivateKey(pkInfo); 288 | } 289 | } else { 290 | priv = CONVERTER.getPrivateKey((PrivateKeyInfo) obj); 291 | } 292 | } 293 | return priv; 294 | } 295 | 296 | /** 297 | * 加载公钥 298 | * 299 | * @param fx {@link Reader} 回调函数 300 | * @return {@link PublicKey} 301 | * @throws IOException 302 | * @throws NoSuchProviderException 303 | * @throws NoSuchAlgorithmException 304 | * @throws InvalidKeySpecException 305 | */ 306 | public static PublicKey loadPublic(Supplier fx) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException { 307 | try (PemReader pemReader = new PemReader(fx.get())) { 308 | PemObject spki = pemReader.readPemObject(); 309 | Security.getProvider(BouncyCastleProvider.PROVIDER_NAME); 310 | return KeyFactory.getInstance(Const.EC_VALUE, BouncyCastleProvider.PROVIDER_NAME).generatePublic(new X509EncodedKeySpec(spki.getContent())); 311 | } 312 | } 313 | } -------------------------------------------------------------------------------- /src/main/java/twgc/gm/sm2/SM2X509CertFactory.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.sm2; 2 | 3 | import java.io.IOException; 4 | import java.math.BigInteger; 5 | import java.security.*; 6 | import java.security.cert.CertificateException; 7 | import java.security.cert.X509Certificate; 8 | import java.util.ArrayList; 9 | import java.util.Date; 10 | import java.util.LinkedList; 11 | import java.util.List; 12 | 13 | import org.bouncycastle.asn1.ASN1ObjectIdentifier; 14 | import org.bouncycastle.asn1.DERIA5String; 15 | import org.bouncycastle.asn1.x500.AttributeTypeAndValue; 16 | import org.bouncycastle.asn1.x500.RDN; 17 | import org.bouncycastle.asn1.x500.X500Name; 18 | import org.bouncycastle.asn1.x500.style.BCStyle; 19 | import org.bouncycastle.asn1.x500.style.IETFUtils; 20 | import org.bouncycastle.asn1.x509.*; 21 | import org.bouncycastle.cert.X509v3CertificateBuilder; 22 | import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; 23 | import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; 24 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 25 | import org.bouncycastle.operator.OperatorCreationException; 26 | import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; 27 | import org.bouncycastle.pkcs.PKCS10CertificationRequest; 28 | import twgc.gm.utils.Const; 29 | 30 | /** 31 | * @author liqs 32 | * @version 1.0 33 | * @date 2021/1/19 14:26 34 | * ref:https://github.com/ZZMarquis/gmhelper 35 | */ 36 | public class SM2X509CertFactory { 37 | 38 | private enum CertLevel { 39 | RootCA, 40 | SubCA 41 | //EndEntity 42 | } // class CertLevel 43 | 44 | private X500Name issuerDN; 45 | private KeyPair issuerKeyPair; 46 | private String commonName; 47 | private List subjectAltNames = new LinkedList<>(); 48 | private boolean selfSignedEECert; 49 | private JcaX509ExtensionUtils extUtils; 50 | /** 51 | * @param issuerKeyPair 证书颁发者的密钥对。 52 | * @param issuer 证书颁发者信息 53 | */ 54 | public SM2X509CertFactory(KeyPair issuerKeyPair, X500Name issuer) throws NoSuchAlgorithmException { 55 | if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { 56 | Security.addProvider(new BouncyCastleProvider()); 57 | } 58 | this.issuerKeyPair = issuerKeyPair; 59 | this.issuerDN = issuer; 60 | this.extUtils = new JcaX509ExtensionUtils(); 61 | } 62 | 63 | /** 64 | * 生成RootCA证书 65 | * @param csr 66 | * @param mail 67 | * @throws Exception 68 | */ 69 | public X509Certificate rootCACert(byte[] csr, String mail, 70 | BigInteger serial, 71 | Date notBefore, 72 | Date notAfter) throws OperatorCreationException, InvalidKeyException, NoSuchAlgorithmException, IOException, SignatureException, NoSuchProviderException, CertificateException { 73 | KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign); 74 | PKCS10CertificationRequest request = new PKCS10CertificationRequest(csr); 75 | X500Name subject = request.getSubject(); 76 | if (!issuerDN.equals(subject)) { 77 | throw new IllegalArgumentException("subject != issuer for certLevel " + CertLevel.RootCA); 78 | } 79 | X509v3CertificateBuilder v3CertGen = genX509v3CertificateBuilder(CertLevel.RootCA, request, mail, serial, notBefore, notAfter); 80 | if (!selfSignedEECert) { 81 | v3CertGen.addExtension(Extension.authorityKeyIdentifier, false, 82 | extUtils.createAuthorityKeyIdentifier(SubjectPublicKeyInfo.getInstance(issuerKeyPair.getPublic().getEncoded()))); 83 | } 84 | BasicConstraints basicConstraints = new BasicConstraints(true); 85 | return certificate(CertLevel.RootCA, usage, basicConstraints, request, v3CertGen); 86 | } 87 | 88 | /** 89 | * 生成SubCA证书 90 | * 91 | * @param csr CSR 92 | * @param mail 93 | * @throws Exception 如果错误发生 94 | */ 95 | public X509Certificate subCACert(byte[] csr, String mail, 96 | BigInteger serial, 97 | Date notBefore, 98 | Date notAfter) throws OperatorCreationException, InvalidKeyException, NoSuchAlgorithmException, IOException, SignatureException, NoSuchProviderException, CertificateException { 99 | KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign); 100 | BasicConstraints basicConstraints = new BasicConstraints(0); 101 | PKCS10CertificationRequest request = new PKCS10CertificationRequest(csr); 102 | X500Name subject = request.getSubject(); 103 | if (issuerDN.equals(subject)) { 104 | throw new IllegalArgumentException( 105 | "subject MUST not equals issuer for certLevel " + CertLevel.SubCA); 106 | } 107 | X509v3CertificateBuilder v3CertGen = genX509v3CertificateBuilder(CertLevel.SubCA, request, mail, serial, notBefore, notAfter); 108 | return certificate(CertLevel.SubCA, usage, basicConstraints, request, v3CertGen); 109 | } 110 | 111 | private X509Certificate certificate(CertLevel certLevel, 112 | KeyUsage keyUsage, //KeyPurposeId[] extendedKeyUsages 113 | BasicConstraints basicConstraints, 114 | PKCS10CertificationRequest request, 115 | X509v3CertificateBuilder v3CertGen) throws IOException, OperatorCreationException, CertificateException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { 116 | /*if (certLevel == CertLevel.EndEntity) { 117 | if (keyUsage.hasUsages(KeyUsage.keyCertSign)) { 118 | throw new IllegalArgumentException("key usage keyCertSign is not allowed in EndEntity Certificate"); 119 | } 120 | }*/ 121 | X509Certificate cert = null; 122 | SubjectPublicKeyInfo subPub = request.getSubjectPublicKeyInfo(); 123 | v3CertGen.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(subPub)); 124 | v3CertGen.addExtension(Extension.basicConstraints, true, basicConstraints); 125 | v3CertGen.addExtension(Extension.keyUsage, true, keyUsage); 126 | 127 | /* 128 | comments so far as no invoked and used code branch 129 | if (extendedKeyUsages != null) { 130 | ExtendedKeyUsage xku = new ExtendedKeyUsage(extendedKeyUsages); 131 | v3CertGen.addExtension(Extension.extendedKeyUsage, false, xku); 132 | boolean forSSLServer = false; 133 | for (KeyPurposeId purposeId : extendedKeyUsages) { 134 | if (KeyPurposeId.id_kp_serverAuth.equals(purposeId)) { 135 | forSSLServer = true; 136 | break; 137 | } 138 | } 139 | if (forSSLServer) { 140 | if (commonName == null) { 141 | throw new IllegalArgumentException("commonName must not be null"); 142 | } 143 | GeneralName name = new GeneralName(GeneralName.dNSName, new DERIA5String(commonName, true)); 144 | subjectAltNames.add(name); 145 | } 146 | } */ 147 | 148 | if (!subjectAltNames.isEmpty()) { 149 | v3CertGen.addExtension(Extension.subjectAlternativeName, false, 150 | new GeneralNames(subjectAltNames.toArray(new GeneralName[0]))); 151 | } 152 | 153 | JcaContentSignerBuilder contentSignerBuilder = makeContentSignerBuilder(issuerKeyPair.getPublic()); 154 | if (contentSignerBuilder != null) { 155 | cert = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME) 156 | .getCertificate(v3CertGen.build(contentSignerBuilder.build(issuerKeyPair.getPrivate()))); 157 | cert.verify(issuerKeyPair.getPublic()); 158 | } 159 | return cert; 160 | } 161 | 162 | private JcaContentSignerBuilder makeContentSignerBuilder(PublicKey issPub) { 163 | JcaContentSignerBuilder contentSignerBuilder = null; 164 | if (issPub.getAlgorithm().equals(Const.EC_VALUE)) { 165 | contentSignerBuilder = new JcaContentSignerBuilder(Const.SM3SM2_VALUE); 166 | contentSignerBuilder.setProvider(BouncyCastleProvider.PROVIDER_NAME); 167 | } 168 | return contentSignerBuilder; 169 | } 170 | 171 | private X509v3CertificateBuilder genX509v3CertificateBuilder(CertLevel certLevel, 172 | PKCS10CertificationRequest request, 173 | String email, 174 | BigInteger serial, 175 | Date notBefore, 176 | Date notAfter) { 177 | 178 | SubjectPublicKeyInfo subPub = request.getSubjectPublicKeyInfo(); 179 | X500Name subject = request.getSubject(); 180 | /* 181 | * RFC 5280 §4.2.1.6 Subject 182 | * Conforming implementations generating new certificates with 183 | * electronic mail addresses MUST use the rfc822Name in the subject 184 | * alternative name extension (Section 4.2.1.6) to describe such 185 | * identities. Simultaneous inclusion of the emailAddress attribute in 186 | * the subject distinguished name to support legacy implementations is 187 | * deprecated but permitted. 188 | */ 189 | RDN[] rdns = subject.getRDNs(); 190 | List newRdns = new ArrayList<>(rdns.length); 191 | for (int i = 0; i < rdns.length; i++) { 192 | RDN rdn = rdns[i]; 193 | 194 | AttributeTypeAndValue atv = rdn.getFirst(); 195 | ASN1ObjectIdentifier type = atv.getType(); 196 | if (BCStyle.EmailAddress.equals(type)) { 197 | email = IETFUtils.valueToString(atv.getValue()); 198 | } else { 199 | if (BCStyle.CN.equals(type)) { 200 | commonName = IETFUtils.valueToString(atv.getValue()); 201 | } 202 | newRdns.add(rdn); 203 | } 204 | } 205 | 206 | if (email != null) { 207 | subject = new X500Name(newRdns.toArray(new RDN[0])); 208 | subjectAltNames.add( 209 | new GeneralName(GeneralName.rfc822Name, 210 | new DERIA5String(email, true))); 211 | } 212 | 213 | if (issuerDN.equals(subject)) { 214 | selfSignedEECert = true; 215 | } 216 | return new X509v3CertificateBuilder( 217 | issuerDN, serial, 218 | notBefore, notAfter, 219 | subject, subPub); 220 | } 221 | 222 | } 223 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/sm2sm3/SM2SM3Util.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.sm2sm3; 2 | 3 | import java.security.*; 4 | 5 | import org.bouncycastle.crypto.digests.SM3Digest; 6 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 7 | import twgc.gm.random.SecureRandomFactory; 8 | import twgc.gm.sm3.SM3Util; 9 | import twgc.gm.utils.Const; 10 | 11 | public class SM2SM3Util { 12 | private Signature signature; 13 | 14 | public SM2SM3Util() throws NoSuchProviderException, NoSuchAlgorithmException { 15 | if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { 16 | Security.addProvider(new BouncyCastleProvider()); 17 | } 18 | signature = Signature.getInstance(Const.SM3SM2_VALUE, BouncyCastleProvider.PROVIDER_NAME); 19 | } 20 | // Digest and Sign 21 | public byte[] digestAndSign(SM3Digest sm3Digest, PrivateKey privateKey, byte[] message) throws SignatureException, InvalidKeyException { 22 | byte[] hashVal = SM3Util.hash(sm3Digest, message); 23 | synchronized (this) { 24 | signature.initSign(privateKey, SecureRandomFactory.getSecureRandom()); 25 | signature.update(hashVal); 26 | return signature.sign(); 27 | } 28 | } 29 | // Verify Signature and Digest 30 | public boolean verifySignatureAndDigest(SM3Digest sm3Digest, PublicKey publicKey, byte[] message, byte[] sigBytes) throws InvalidKeyException, SignatureException { 31 | byte[] hashVal = SM3Util.hash(sm3Digest, message); 32 | synchronized (this) { 33 | signature.initVerify(publicKey); 34 | signature.update(hashVal); 35 | return signature.verify(sigBytes); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/sm3/SM3Util.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.sm3; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.bouncycastle.crypto.digests.SM3Digest; 6 | 7 | /** 8 | * @author Sean 9 | * @Description: 国密SM3工具类 10 | * @date 2020/9/18 11 | */ 12 | public class SM3Util { 13 | 14 | private SM3Util() { 15 | 16 | } 17 | 18 | /** 19 | * 计算SM3摘要值 20 | * 21 | * @param srcData 原文 22 | * @return 摘要值 23 | */ 24 | public static byte[] hash(SM3Digest digest, byte[] srcData) { 25 | digest.update(srcData, 0, srcData.length); 26 | byte[] hashVal = new byte[digest.getDigestSize()]; 27 | digest.doFinal(hashVal, 0); 28 | return hashVal; 29 | } 30 | 31 | /** 32 | * SM3摘要值验证 33 | * 34 | * @param srcData 原文 35 | * @param sm3HashVal 原文对应的摘要值 36 | * @return 返回true标识验证成功,false标识验证失败 37 | */ 38 | public static boolean verify(SM3Digest digest, byte[] srcData, byte[] sm3HashVal) { 39 | byte[] hashVal = hash(digest, srcData); 40 | return Arrays.equals(hashVal, sm3HashVal); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/sm4/SM4Cipher.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.sm4; 2 | 3 | import java.security.NoSuchAlgorithmException; 4 | import java.security.NoSuchProviderException; 5 | import java.security.Security; 6 | import java.util.EnumMap; 7 | import java.util.Map; 8 | import javax.crypto.Cipher; 9 | import javax.crypto.NoSuchPaddingException; 10 | 11 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 12 | 13 | 14 | /** 15 | * @author Sean 16 | * @Description: SM4Cipher 17 | * @date 2021/2/12 18 | */ 19 | public class SM4Cipher { 20 | 21 | private final Map cipherMap = new EnumMap<>(SM4ModeAndPaddingEnum.class); 22 | 23 | public SM4Cipher() throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException { 24 | if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { 25 | Security.addProvider(new BouncyCastleProvider()); 26 | } 27 | for (SM4ModeAndPaddingEnum mode : SM4ModeAndPaddingEnum.values()) { 28 | Cipher cipher = Cipher.getInstance(mode.getName(), BouncyCastleProvider.PROVIDER_NAME); 29 | cipherMap.put(mode, cipher); 30 | } 31 | } 32 | 33 | public Cipher getCipher(SM4ModeAndPaddingEnum sm4ModeAndPaddingEnum) { 34 | return cipherMap.get(sm4ModeAndPaddingEnum); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/sm4/SM4ModeAndPaddingEnum.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.sm4; 2 | 3 | /** 4 | * @author Sean 5 | * @Description: SM4ModeAndPaddingEnum 6 | * @date 2021/1/21 7 | */ 8 | public enum SM4ModeAndPaddingEnum { 9 | SM4_ECB_NoPadding("SM4/ECB/NoPadding"), 10 | SM4_ECB_PKCS5Padding("SM4/ECB/PKCS5Padding"), 11 | SM4_ECB_PKCS7Padding("SM4/ECB/PKCS7Padding"), 12 | SM4_CBC_NoPadding("SM4/CBC/NoPadding"), 13 | SM4_CBC_PKCS5Padding("SM4/CBC/PKCS5Padding"), 14 | SM4_CBC_PKCS7Padding("SM4/CBC/PKCS7Padding"), 15 | // CFB,OFB,CTR三种模式无需填充(padding) 16 | SM4_CFB_NoPadding("SM4/CFB/NoPadding"), 17 | SM4_OFB_NoPadding("SM4/OFB/NoPadding"), 18 | SM4_CTR_NoPadding("SM4/CTR/NoPadding"); 19 | 20 | private final String name; 21 | 22 | SM4ModeAndPaddingEnum(String name) { 23 | this.name = name; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/sm4/SM4Util.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.sm4; 2 | 3 | import java.security.*; 4 | import javax.crypto.*; 5 | import javax.crypto.spec.IvParameterSpec; 6 | import javax.crypto.spec.SecretKeySpec; 7 | 8 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 9 | 10 | /** 11 | * @author Sean 12 | * @Description: 国密SM4工具类 13 | * @date 2020/9/18 14 | */ 15 | public class SM4Util { 16 | 17 | private static final String ALGORITHM_NAME = "SM4"; 18 | private static KeyGenerator kg; 19 | 20 | public SM4Util() throws NoSuchProviderException, NoSuchAlgorithmException { 21 | if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { 22 | Security.addProvider(new BouncyCastleProvider()); 23 | } 24 | kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME); 25 | } 26 | 27 | /** 28 | * SM4加密 29 | * 30 | * @param cipher cipher 31 | * @param input 明文数据 32 | * @param sm4Key SecretKeySpec 33 | * @param iv 初始向量(ECB模式下传NULL), IV must be 16 bytes long 34 | * @return byte[] 35 | * @throws IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException 36 | */ 37 | public byte[] encrypt(Cipher cipher, byte[] input, SecretKeySpec sm4Key, byte[] iv) throws IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException { 38 | IvParameterSpec ivParameterSpec = null; 39 | if (iv != null) { 40 | ivParameterSpec = new IvParameterSpec(iv); 41 | } 42 | return sm4(input, sm4Key, cipher, ivParameterSpec, Cipher.ENCRYPT_MODE); 43 | } 44 | 45 | /** 46 | * SM4解密 47 | * 48 | * @param cipher cipher 49 | * @param input 密文数据 50 | * @param sm4Key SecretKeySpec 51 | * @param iv 初始向量(ECB模式下传NULL), IV must be 16 bytes long 52 | * @return byte[] 53 | * @throws IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException 54 | */ 55 | public byte[] decrypt(Cipher cipher, byte[] input, SecretKeySpec sm4Key, byte[] iv) throws IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException { 56 | IvParameterSpec ivParameterSpec = null; 57 | if (iv != null) { 58 | ivParameterSpec = new IvParameterSpec(iv); 59 | } 60 | return sm4(input, sm4Key, cipher, ivParameterSpec, Cipher.DECRYPT_MODE); 61 | } 62 | 63 | /** 64 | * 执行sm4加解密 65 | * 66 | * @param input 明文或密文,与参数mode有关 67 | * @param sm4Key 密钥 68 | * @param cipher chipher 69 | * @param ivParameterSpec 初始向量(ECB模式下传NULL) 70 | * @param mode 1-加密;2-解密 71 | * @return byte[] 72 | * @throws InvalidKeyException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException 73 | */ 74 | private static byte[] sm4(byte[] input, SecretKeySpec sm4Key, Cipher cipher, IvParameterSpec ivParameterSpec, int mode) throws InvalidKeyException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException { 75 | if (ivParameterSpec == null) { 76 | cipher.init(mode, sm4Key); 77 | } else { 78 | cipher.init(mode, sm4Key, ivParameterSpec); 79 | } 80 | return cipher.doFinal(input); 81 | } 82 | 83 | /** 84 | * SM4算法目前只支持128位(即密钥16字节) 85 | */ 86 | public static final int DEFAULT_KEY_SIZE = 128; 87 | 88 | public byte[] generateKey() { 89 | kg.init(DEFAULT_KEY_SIZE, new SecureRandom()); 90 | return kg.generateKey().getEncoded(); 91 | } 92 | } -------------------------------------------------------------------------------- /src/main/java/twgc/gm/utils/ConfigLoader.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.utils; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.Map; 6 | 7 | import org.yaml.snakeyaml.Yaml; 8 | /** 9 | * @author Sean 10 | * @Description: 配置文件加载工具类 11 | * @date 2021/9/25 12 | */ 13 | public class ConfigLoader { 14 | 15 | private ConfigLoader() { 16 | 17 | } 18 | 19 | private static final Yaml yaml = new Yaml(); 20 | 21 | public static Map> loadConfig(InputStream in) throws IOException { 22 | Map> ret = yaml.load(in); 23 | in.close(); 24 | return ret; 25 | } 26 | 27 | public static Map loadConfig(InputStream in, String algorithm) throws IOException { 28 | return loadConfig(in).get(algorithm); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/twgc/gm/utils/Const.java: -------------------------------------------------------------------------------- 1 | package twgc.gm.utils; 2 | 3 | public class Const { 4 | private Const() { 5 | 6 | } 7 | 8 | public static final String EC_VALUE = "EC"; 9 | public static final String SM3SM2_VALUE = "SM3WITHSM2"; 10 | public static final String CURVE_NAME = "sm2p256v1"; 11 | public static final String POOL_CONFIG = "/pool-config.yaml"; 12 | public static final String SM2 = "sm2"; 13 | public static final String SM3 = "sm3"; 14 | public static final String SM4 = "sm4"; 15 | } -------------------------------------------------------------------------------- /src/test/java/RandomSNAllocatorTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | import twgc.gm.random.RandomSNAllocator; 3 | 4 | import static org.junit.Assert.*; 5 | 6 | public class RandomSNAllocatorTest { 7 | @Test(expected = IllegalArgumentException.class) 8 | public void testInvalidData() { 9 | new RandomSNAllocator(64); 10 | // should throw IllegalArgumentException 11 | } 12 | } -------------------------------------------------------------------------------- /src/test/java/SM2SM3UtilThreadTest.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | import java.security.KeyPair; 3 | import java.security.PrivateKey; 4 | import java.security.PublicKey; 5 | import java.util.Queue; 6 | import java.util.concurrent.ConcurrentLinkedQueue; 7 | 8 | import org.apache.commons.lang3.RandomStringUtils; 9 | import org.bouncycastle.crypto.digests.SM3Digest; 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | import twgc.gm.pool.SM3DigestPool; 13 | import twgc.gm.sm2.SM2Util; 14 | import twgc.gm.sm2sm3.SM2SM3Util; 15 | import twgc.gm.sm3.SM3Util; 16 | 17 | import static org.junit.Assert.*; 18 | 19 | public class SM2SM3UtilThreadTest { 20 | 21 | static int randomData = 128; 22 | static byte[] message = RandomStringUtils.random(randomData).getBytes(); 23 | 24 | static SM3DigestPool sm3DigestPool; 25 | 26 | PublicKey pubKey; 27 | PrivateKey privKey; 28 | KeyPair keyPair; 29 | 30 | static { 31 | try { 32 | sm3DigestPool = new SM3DigestPool(); 33 | } catch (IOException e) { 34 | e.printStackTrace(); 35 | } 36 | } 37 | 38 | static String exceptionHappened = "Exception happened"; 39 | 40 | @Test 41 | public void threadSafe() throws Exception { 42 | Queue results = new ConcurrentLinkedQueue<>(); 43 | Queue ex = new ConcurrentLinkedQueue<>(); 44 | SM3Digest sm3Digest = null; 45 | 46 | SM2Util sm2instance = new SM2Util(); 47 | this.keyPair = sm2instance.generatekeyPair(); 48 | this.pubKey = keyPair.getPublic(); 49 | this.privKey = keyPair.getPrivate(); 50 | 51 | SM2SM3Util instance = new SM2SM3Util(); 52 | byte[] signresult = null; 53 | try { 54 | sm3Digest = sm3DigestPool.borrowObject(); 55 | signresult = instance.digestAndSign(sm3Digest, this.privKey, message); 56 | Assert.assertTrue(instance.verifySignatureAndDigest(sm3Digest, this.pubKey, message, signresult)); 57 | } finally { 58 | if (sm3Digest != null) { 59 | sm3DigestPool.returnObject(sm3Digest); 60 | } 61 | } 62 | 63 | for (int i = 0; i < 300; i++) { 64 | new Thread(() -> { 65 | SM3Digest sm3DigestInThead = null; 66 | try { 67 | sm3DigestInThead = sm3DigestPool.borrowObject(); 68 | byte[] result = instance.digestAndSign(sm3DigestInThead, this.privKey, message); 69 | results.add(instance.verifySignatureAndDigest(sm3DigestInThead, this.pubKey, message, result)); 70 | } catch (Exception e) { 71 | ex.add(e); 72 | } finally { 73 | if (sm3DigestInThead != null) { 74 | sm3DigestPool.returnObject(sm3DigestInThead); 75 | } 76 | } 77 | }).start(); 78 | } 79 | while (!ex.isEmpty()) { 80 | Exception e = ex.poll(); 81 | e.printStackTrace(); 82 | Assert.fail(exceptionHappened); 83 | } 84 | Thread.sleep(5000); 85 | Assert.assertEquals(300, results.size()); 86 | while (!results.isEmpty()) { 87 | Assert.assertTrue(results.poll()); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/test/java/SM2UtilTest.java: -------------------------------------------------------------------------------- 1 | import java.io.File; 2 | import java.io.IOException; 3 | import java.io.InputStream; 4 | import java.nio.file.Files; 5 | import java.nio.file.Paths; 6 | import java.security.*; 7 | import java.security.cert.X509Certificate; 8 | import java.util.Date; 9 | import java.util.Map; 10 | import javax.security.auth.x500.X500Principal; 11 | 12 | import org.apache.commons.lang3.RandomStringUtils; 13 | import org.bouncycastle.asn1.x500.X500Name; 14 | import org.bouncycastle.asn1.x500.X500NameBuilder; 15 | import org.bouncycastle.asn1.x500.style.BCStyle; 16 | import org.bouncycastle.crypto.engines.SM2Engine; 17 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 18 | import org.bouncycastle.operator.OperatorCreationException; 19 | import org.bouncycastle.pkcs.PKCS10CertificationRequest; 20 | import org.junit.Assert; 21 | import org.junit.Before; 22 | import org.junit.FixMethodOrder; 23 | import org.junit.Test; 24 | import org.junit.runners.MethodSorters; 25 | import twgc.gm.pool.SM2EnginePool; 26 | import twgc.gm.random.CertSNAllocator; 27 | import twgc.gm.random.RandomSNAllocator; 28 | import twgc.gm.sm2.SM2Util; 29 | import twgc.gm.sm2.SM2X509CertFactory; 30 | import twgc.gm.utils.ConfigLoader; 31 | 32 | 33 | @FixMethodOrder(MethodSorters.JVM) 34 | public class SM2UtilTest { 35 | 36 | static String pubFileName = "pub.pem"; 37 | static String privFileName = "priv.pem"; 38 | static String encryptedprivFileName = "encryptedpriv.pem"; 39 | static String reqFileName = "req.pem"; 40 | static String certFileName = "cert.pem"; 41 | static String exceptionHappened = "Exception happened"; 42 | static String keyEqualHint = "key should be equal"; 43 | static String passwd = RandomStringUtils.random(18); 44 | static int randomData = 128; 45 | static byte[] message = RandomStringUtils.random(randomData).getBytes(); 46 | PublicKey pubKey; 47 | PrivateKey privKey; 48 | X509Certificate x509Certificate; 49 | KeyPair keyPair; 50 | 51 | Map> configMap; 52 | 53 | public static void saveCSRInPem(PKCS10CertificationRequest csr, String csrFile) throws IOException, OperatorCreationException { 54 | String csrPem = SM2Util.pemFrom(csr); 55 | Files.write(Paths.get(csrFile), csrPem.getBytes()); 56 | } 57 | 58 | public static void saveCertificateInPem(X509Certificate x509Certificate, String certFileName) throws Exception { 59 | String certStr = SM2Util.pemFrom(x509Certificate); 60 | Files.write(Paths.get(certFileName), certStr.getBytes()); 61 | } 62 | 63 | public static X509Certificate genCertificate(KeyPair keyPair, PKCS10CertificationRequest csr, X500Name x500Name) throws Exception { 64 | long certExpire = 20L * 365 * 24 * 60 * 60 * 1000; 65 | CertSNAllocator snAllocator = new RandomSNAllocator(); 66 | SM2X509CertFactory rootCertFactory = new SM2X509CertFactory(keyPair, x500Name); 67 | Date now = new Date(); 68 | return rootCertFactory.rootCACert(csr.getEncoded(), 69 | "test@twgc.com", 70 | snAllocator.nextSerialNumber(), 71 | now, 72 | new Date(now.getDate() + certExpire)); 73 | } 74 | 75 | public static void savePemFormatKeyFile(PrivateKey privateKey, String filename) throws IOException, OperatorCreationException { 76 | String privateKeyPem = SM2Util.pemFrom(privateKey, ""); 77 | Files.write(Paths.get(filename), privateKeyPem.getBytes()); 78 | } 79 | 80 | public static void savePemFormatPubKeyFile(PublicKey publicKey, String filename) throws IOException { 81 | String pubKeyPem = SM2Util.pemFrom(publicKey); 82 | Files.write(Paths.get(filename), pubKeyPem.getBytes()); 83 | } 84 | 85 | public static void saveKeyPairInPem(KeyPair keyPair, String pubFileName, String privFileName) throws IOException, OperatorCreationException { 86 | savePemFormatKeyFile(keyPair.getPrivate(), privFileName); 87 | savePemFormatPubKeyFile(keyPair.getPublic(), pubFileName); 88 | } 89 | 90 | @Before 91 | @Test 92 | public void generateFile() { 93 | File pubFile = new File(pubFileName); 94 | File privFile = new File(privFileName); 95 | File reqFile = new File(reqFileName); 96 | File certFile = new File(certFileName); 97 | try { 98 | if (!pubFile.exists()) { 99 | SM2Util instance = new SM2Util(); 100 | this.keyPair = instance.generatekeyPair(); 101 | saveKeyPairInPem(this.keyPair, pubFileName, privFileName); 102 | 103 | X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE); 104 | X500Name x500Name = builder.addRDN(BCStyle.CN, "Root CA").build(); 105 | // gen csr && save 106 | PKCS10CertificationRequest csr = SM2Util.generateCSR(keyPair, new X500Principal(String.valueOf(x500Name))); 107 | saveCSRInPem(csr, reqFileName); 108 | // gen cert && save 109 | X509Certificate x509Certificate = genCertificate(keyPair, csr, x500Name); 110 | saveCertificateInPem(x509Certificate, certFileName); 111 | } else { 112 | System.out.println("Skip file generation deal to interact testing."); 113 | } 114 | this.pubKey = SM2Util.loadPublicFromFile(pubFileName); 115 | Assert.assertNotNull(this.pubKey); 116 | this.privKey = SM2Util.loadPrivFromFile(privFileName, ""); 117 | Assert.assertNotNull(this.privKey); 118 | this.x509Certificate = SM2Util.loadX509CertificateFromFile(certFileName); 119 | Assert.assertNotNull(this.x509Certificate); 120 | Assert.assertEquals("SM3WITHSM2", this.x509Certificate.getSigAlgName()); 121 | if (!pubFile.exists()) { 122 | Assert.assertEquals(keyEqualHint, this.keyPair.getPublic(), this.pubKey); 123 | Assert.assertEquals(keyEqualHint, this.keyPair.getPrivate(), this.privKey); 124 | } 125 | } catch (Exception e) { 126 | e.printStackTrace(); 127 | Assert.fail(exceptionHappened); 128 | } 129 | Assert.assertTrue(pubFile.exists()); 130 | Assert.assertTrue(privFile.exists()); 131 | Assert.assertTrue(reqFile.exists()); 132 | Assert.assertTrue(certFile.exists()); 133 | 134 | } 135 | 136 | @Before 137 | public void loadTestDataConfigMap() { 138 | try { 139 | InputStream in = SM2UtilTest.class.getResourceAsStream("testdata.yml"); 140 | this.configMap = ConfigLoader.loadConfig(in); 141 | 142 | Assert.assertNotNull(this.configMap); 143 | } catch (Exception e) { 144 | Assert.fail(exceptionHappened); 145 | } 146 | } 147 | 148 | //encrypt and decrypt 149 | @Test 150 | public void encryptAndDecryptC1C3C2() { 151 | SM2EnginePool sm2EnginePool = new SM2EnginePool(1, SM2Engine.Mode.C1C3C2); 152 | SM2Engine sm2Engine = null; 153 | try { 154 | SM2Util instance = new SM2Util(); 155 | sm2Engine = sm2EnginePool.borrowObject(); 156 | byte[] encrypted = instance.encrypt(sm2Engine, this.pubKey, message); 157 | byte[] rs = instance.decrypt(sm2Engine, this.privKey, encrypted); 158 | Assert.assertEquals(new String(message), new String(rs)); 159 | byte[] encrypted2 = instance.encrypt(sm2Engine, this.pubKey, "msg".getBytes()); 160 | rs = instance.decrypt(sm2Engine, this.privKey, encrypted2); 161 | Assert.assertNotEquals(new String(message), new String(rs)); 162 | } catch (Exception e) { 163 | e.printStackTrace(); 164 | Assert.fail(exceptionHappened); 165 | } finally { 166 | if (sm2Engine != null) { 167 | sm2EnginePool.returnObject(sm2Engine); 168 | } 169 | } 170 | } 171 | 172 | //encrypt and decrypt 173 | @Test 174 | public void encryptAndDecryptC1C2C3() { 175 | SM2EnginePool sm2EnginePool = new SM2EnginePool(1, SM2Engine.Mode.C1C2C3); 176 | SM2Engine sm2Engine = null; 177 | try { 178 | SM2Util instance = new SM2Util(); 179 | sm2Engine = sm2EnginePool.borrowObject(); 180 | byte[] encrypted = instance.encrypt(sm2Engine, this.pubKey, message); 181 | byte[] rs = instance.decrypt(sm2Engine, this.privKey, encrypted); 182 | Assert.assertEquals(new String(message), new String(rs)); 183 | byte[] encrypted2 = instance.encrypt(sm2Engine, this.pubKey, "msg".getBytes()); 184 | rs = instance.decrypt(sm2Engine, this.privKey, encrypted2); 185 | Assert.assertNotEquals(new String(message), new String(rs)); 186 | } catch (Exception e) { 187 | e.printStackTrace(); 188 | Assert.fail(exceptionHappened); 189 | } finally { 190 | if (sm2Engine != null) { 191 | sm2EnginePool.returnObject(sm2Engine); 192 | } 193 | } 194 | } 195 | 196 | //sign and verify 197 | @Test 198 | public void signAndverify() { 199 | try { 200 | SM2Util instance = new SM2Util(); 201 | byte[] signbyte = instance.sign(this.privKey, message); 202 | boolean rs = instance.verify(this.pubKey, message, signbyte); 203 | Assert.assertTrue(rs); 204 | rs = instance.verify(this.pubKey, message, message); 205 | Assert.assertFalse(rs); 206 | } catch (Exception e) { 207 | e.printStackTrace(); 208 | Assert.fail(exceptionHappened); 209 | } 210 | } 211 | 212 | //sign and verify 213 | //ref https://github.com/bcgit/bc-java/blob/r1rv67/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/GM.java 214 | @Test 215 | public void signAndverifyHash256Sample() { 216 | try { 217 | if (Security.getProvider("BC") == null) { 218 | Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 219 | } 220 | Signature signature = Signature.getInstance("SHA256WITHSM2", "BC"); 221 | SM2Util instance = new SM2Util(); 222 | instance.setSignature(signature); 223 | byte[] signbyte = instance.sign(this.privKey, message); 224 | boolean rs = instance.verify(this.pubKey, message, signbyte); 225 | Assert.assertTrue(rs); 226 | rs = instance.verify(this.pubKey, message, message); 227 | Assert.assertFalse(rs); 228 | } catch (Exception e) { 229 | e.printStackTrace(); 230 | Assert.fail(exceptionHappened); 231 | } 232 | } 233 | 234 | //private key Derive from private key 235 | @Test 236 | public void derivePublicFromPrivate() { 237 | PublicKey deriveKey = SM2Util.derivePublicFromPrivate(this.privKey); 238 | Assert.assertEquals(keyEqualHint, this.pubKey, deriveKey); 239 | } 240 | 241 | //key with password 242 | @Test 243 | public void keyPairWithPasswd() { 244 | try { 245 | SM2Util instance = new SM2Util(); 246 | KeyPair keyPair = instance.generatekeyPair(); 247 | String privateKeyPem = SM2Util.pemFrom(keyPair.getPrivate(), passwd); 248 | Files.write(Paths.get(encryptedprivFileName), privateKeyPem.getBytes()); 249 | PrivateKey key = SM2Util.loadPrivFromFile(encryptedprivFileName, passwd); 250 | Assert.assertNotNull(key); 251 | Assert.assertEquals(keyEqualHint, keyPair.getPrivate(), key); 252 | } catch (Exception e) { 253 | e.printStackTrace(); 254 | Assert.fail(exceptionHappened); 255 | } 256 | } 257 | 258 | @Test 259 | public void issueCertificate() throws Exception { 260 | 261 | SM2Util sm2Util = new SM2Util(); 262 | // 证书颁发时长 263 | long certExpire = 20L * 365 * 24 * 60 * 60 * 1000; 264 | CertSNAllocator snAllocator = new RandomSNAllocator(); 265 | 266 | // one 模拟根 CA 自签名生成根证书 rootCACert 267 | KeyPair rootKeyPair = sm2Util.generatekeyPair(); 268 | X500Name rootX500Name = new X500NameBuilder(BCStyle.INSTANCE).addRDN(BCStyle.CN, "Root CA").build(); 269 | SM2X509CertFactory rootCertMaker = new SM2X509CertFactory(rootKeyPair, rootX500Name); 270 | PublicKey rootKeyPairPublic = rootKeyPair.getPublic(); 271 | byte[] rootcsr = SM2Util.generateCSR(rootKeyPair, new X500Principal(String.valueOf(rootX500Name))).getEncoded(); 272 | Date now = new Date(); 273 | X509Certificate rootCACert = rootCertMaker.rootCACert(rootcsr, 274 | "test@twgc.com", 275 | snAllocator.nextSerialNumber(), 276 | now, 277 | new Date(now.getDate() + certExpire)); 278 | 279 | // two 模拟根 CA 生成中间证书 280 | KeyPair midKeyPair = sm2Util.generatekeyPair(); 281 | PublicKey midKeyPairPublic = midKeyPair.getPublic(); 282 | X500Name midX500Name = new X500NameBuilder(BCStyle.INSTANCE).addRDN(BCStyle.CN, "Intermediate CA").build(); 283 | byte[] midcsr = SM2Util.generateCSR(midKeyPair, new X500Principal(String.valueOf(midX500Name))).getEncoded(); 284 | X509Certificate midCACert = rootCertMaker.subCACert(midcsr, 285 | "test1@twgc.com", 286 | snAllocator.nextSerialNumber(), 287 | now, 288 | new Date(now.getDate() + certExpire) 289 | ); 290 | 291 | // three 模拟中间 CA 生成用户证书 292 | SM2X509CertFactory midCertMaker = new SM2X509CertFactory(midKeyPair, midX500Name); 293 | KeyPair userKeyPair = sm2Util.generatekeyPair(); 294 | X500Name userX500Name = new X500NameBuilder(BCStyle.INSTANCE).addRDN(BCStyle.CN, "User CA").build(); 295 | byte[] usercsr = SM2Util.generateCSR(userKeyPair, new X500Principal(String.valueOf(userX500Name))).getEncoded(); 296 | X509Certificate userCACert = midCertMaker.subCACert(usercsr, 297 | "test2@twgc.com", 298 | snAllocator.nextSerialNumber(), 299 | now, 300 | new Date(now.getDate() + certExpire)); 301 | 302 | // 根证书自签名,用自己的公钥验证 303 | rootCACert.verify(rootKeyPairPublic, BouncyCastleProvider.PROVIDER_NAME); 304 | // 中间证书可用根证书的公钥验证 305 | midCACert.verify(rootKeyPairPublic, BouncyCastleProvider.PROVIDER_NAME); 306 | // 用户证书可用中间证书的公钥验证 307 | userCACert.verify(midKeyPairPublic, BouncyCastleProvider.PROVIDER_NAME); 308 | 309 | } 310 | 311 | /** 312 | * 测试从 `testdata.yml` 加载配置文件 313 | * 314 | * @throws IOException 315 | */ 316 | @Test 317 | public void testLoadConfigMap() { 318 | Map javagm = this.configMap.get("javagm"); 319 | Assert.assertNotNull(javagm); 320 | 321 | Object testdata = javagm.get("testdata"); 322 | 323 | Assert.assertNotNull(testdata); 324 | String publicKey = (String) ((Map) testdata).get("public-key"); 325 | String privateKey = (String) ((Map) testdata).get("private-key"); 326 | String cert = (String) ((Map) testdata).get("cert"); 327 | 328 | Assert.assertNotNull(publicKey); 329 | Assert.assertNotNull(privateKey); 330 | Assert.assertNotNull(cert); 331 | } 332 | 333 | /** 334 | * 测试从从字符串加载私钥对象 335 | * 336 | *
337 |      * javagm:
338 |      *   testdata:
339 |      *     private-key: |
340 |      *       -----BEGIN PRIVATE KEY-----
341 |      *       MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgc0UCgfELjC0V+xUm
342 |      *       ELYFmy0J0cee42ZpKyQ4FRTBlJSgCgYIKoEcz1UBgi2hRANCAATJbIFbxcAaDxMk
343 |      *       7XExTRU/bBnGEu6YfaleJxnLZS40NDNjZV+ztveWfLZk2+oWieykM3/yZ/6IieJk
344 |      *       5uuohUjD
345 |      *       -----END PRIVATE KEY-----
346 |      *     public-key: |
347 |      *       -----BEGIN PUBLIC KEY-----
348 |      *       MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEyWyBW8XAGg8TJO1xMU0VP2wZxhLu
349 |      *       mH2pXicZy2UuNDQzY2Vfs7b3lny2ZNvqFonspDN/8mf+iIniZObrqIVIww==
350 |      *       -----END PUBLIC KEY-----
351 |      *     cert: |
352 |      *       -----BEGIN CERTIFICATE-----
353 |      *       MIIBdzCCAR2gAwIBAgIJAfA3Qnph7CieMAoGCCqBHM9VAYN1MBIxEDAOBgNVBAMM
354 |      *       B1Jvb3QgQ0EwHhcNMjMxMjAyMTQzMjMxWhcNODkxMjI3MDAwMDAwWjASMRAwDgYD
355 |      *       VQQDEwdSb290IENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEyWyBW8XAGg8T
356 |      *       JO1xMU0VP2wZxhLumH2pXicZy2UuNDQzY2Vfs7b3lny2ZNvqFonspDN/8mf+iIni
357 |      *       ZObrqIVIw6NcMFowHQYDVR0OBBYEFOMvj2LPGlkOw1M1Pj34klVi8SFgMA8GA1Ud
358 |      *       EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMBgGA1UdEQQRMA+BDXRlc3RAdHdn
359 |      *       Yy5jb20wCgYIKoEcz1UBg3UDSAAwRQIgTeoLjt+eP3kwQg17G+l12wj4MQNed1hW
360 |      *       aZZkJe43rkICIQCdI3WhnrvzhbEijsTXL1woIwnFgY9MIci7BmKLMpMM6w==
361 |      *       -----END CERTIFICATE-----
362 |      * 
363 | */ 364 | @Test 365 | public void testLoadPrivFromString() throws Exception { 366 | Map javagm = this.configMap.get("javagm"); 367 | Object testdata = javagm.get("testdata"); 368 | String privateKey = (String) ((Map) testdata).get("private-key"); 369 | 370 | PrivateKey privKey = SM2Util.loadPrivFromString(privateKey, ""); 371 | Assert.assertNotNull(privKey); 372 | } 373 | 374 | /** 375 | * 测试从从字符串加载公钥对象 376 | * 377 | * @throws Exception 378 | */ 379 | @Test 380 | public void testLoadPublicFromString() throws Exception { 381 | Map javagm = this.configMap.get("javagm"); 382 | Object testdata = javagm.get("testdata"); 383 | 384 | String publicKey = (String) ((Map) testdata).get("public-key"); 385 | PublicKey pubKey = SM2Util.loadPublicFromString(publicKey); 386 | Assert.assertNotNull(pubKey); 387 | } 388 | 389 | /** 390 | * 测试从字符串加载密钥对并测试加解密 391 | * 392 | * @throws Exception 393 | */ 394 | @Test 395 | public void testLoadPublicAndPrivFromString() throws Exception { 396 | Map javagm = this.configMap.get("javagm"); 397 | Object testdata = javagm.get("testdata"); 398 | 399 | String publicKey = (String) ((Map) testdata).get("public-key"); 400 | String privateKey = (String) ((Map) testdata).get("private-key"); 401 | 402 | PublicKey pubKey = SM2Util.loadPublicFromString(publicKey); 403 | PrivateKey privKey = SM2Util.loadPrivFromString(privateKey, ""); 404 | 405 | Assert.assertNotNull(pubKey); 406 | Assert.assertNotNull(privKey); 407 | 408 | SM2EnginePool sm2EnginePool = new SM2EnginePool(1, SM2Engine.Mode.C1C3C2); 409 | SM2Engine sm2Engine = null; 410 | 411 | try { 412 | SM2Util instance = new SM2Util(); 413 | sm2Engine = sm2EnginePool.borrowObject(); 414 | byte[] encrypted = instance.encrypt(sm2Engine, pubKey, message); 415 | byte[] rs = instance.decrypt(sm2Engine, privKey, encrypted); 416 | Assert.assertEquals(new String(message), new String(rs)); 417 | 418 | byte[] encrypted2 = instance.encrypt(sm2Engine, pubKey, "msg".getBytes()); 419 | rs = instance.decrypt(sm2Engine, privKey, encrypted2); 420 | Assert.assertNotEquals(new String(message), new String(rs)); 421 | } catch (Exception e) { 422 | e.printStackTrace(); 423 | Assert.fail(exceptionHappened); 424 | } finally { 425 | if (sm2Engine != null) { 426 | sm2EnginePool.returnObject(sm2Engine); 427 | } 428 | } 429 | } 430 | 431 | /** 432 | * 测试从字符串加载证书对象 433 | * 434 | * @throws Exception 435 | */ 436 | @Test 437 | public void testLoadX509CertificateFromString() throws Exception { 438 | Map javagm = this.configMap.get("javagm"); 439 | Object testdata = javagm.get("testdata"); 440 | 441 | String cert = (String) ((Map) testdata).get("cert"); 442 | Assert.assertNotNull(cert); 443 | 444 | X509Certificate certificate = SM2Util.loadX509CertificateFromString(cert); 445 | Assert.assertNotNull(certificate); 446 | Assert.assertEquals("SM3WITHSM2", certificate.getSigAlgName()); 447 | } 448 | 449 | static { 450 | try { 451 | Security.addProvider(new BouncyCastleProvider()); 452 | } catch (Exception e) { 453 | e.printStackTrace(); 454 | } 455 | } 456 | } -------------------------------------------------------------------------------- /src/test/java/SM2UtilThreadTest.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | import java.security.KeyPair; 3 | import java.security.PrivateKey; 4 | import java.security.PublicKey; 5 | import java.time.Duration; 6 | import java.time.Instant; 7 | import java.util.Queue; 8 | import java.util.concurrent.ConcurrentLinkedQueue; 9 | import java.util.concurrent.CountDownLatch; 10 | 11 | import org.apache.commons.lang3.RandomStringUtils; 12 | import org.bouncycastle.crypto.engines.SM2Engine; 13 | import org.junit.Assert; 14 | import org.junit.FixMethodOrder; 15 | import org.junit.Test; 16 | import org.junit.runners.MethodSorters; 17 | import twgc.gm.pool.SM2EnginePool; 18 | import twgc.gm.sm2.SM2Util; 19 | 20 | 21 | /** 22 | * @author Sam 23 | * @description 国密算法SM2工具类线程安全测试 24 | * @date 2021/1/10 25 | */ 26 | @FixMethodOrder(MethodSorters.JVM) 27 | public class SM2UtilThreadTest { 28 | 29 | static String exceptionHappened = "Exception happened"; 30 | static int randomData = 128; 31 | static byte[] message = RandomStringUtils.random(randomData).getBytes(); 32 | static SM2EnginePool sm2EnginePool; 33 | 34 | static { 35 | try { 36 | sm2EnginePool = new SM2EnginePool(SM2Engine.Mode.C1C3C2); 37 | } catch (IOException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | 42 | PublicKey pubKey; 43 | PrivateKey privKey; 44 | KeyPair keyPair; 45 | 46 | @Test 47 | public void threadSafeEncryptDecrypt() { 48 | try { 49 | Queue results = new ConcurrentLinkedQueue<>(); 50 | Queue ex = new ConcurrentLinkedQueue<>(); 51 | SM2Util instance = new SM2Util(); 52 | this.keyPair = instance.generatekeyPair(); 53 | this.pubKey = keyPair.getPublic(); 54 | this.privKey = keyPair.getPrivate(); 55 | 56 | SM2Engine sm2Engine = sm2EnginePool.borrowObject(); 57 | byte[] encrypted = instance.encrypt(sm2Engine, this.pubKey, message); 58 | byte[] rs = instance.decrypt(sm2Engine, this.privKey, encrypted); 59 | Assert.assertEquals(new String(message), new String(rs)); 60 | sm2EnginePool.returnObject(sm2Engine); 61 | 62 | int threadNum = 300; 63 | final CountDownLatch startGate = new CountDownLatch(1); 64 | final CountDownLatch endGate = new CountDownLatch(threadNum); 65 | for (int i = 0; i < threadNum; i++) { 66 | new Thread(() -> { 67 | try { 68 | startGate.await(); 69 | SM2Engine sm2Engine1 = null; 70 | try { 71 | sm2Engine1 = sm2EnginePool.borrowObject(); 72 | results.add(instance.decrypt(sm2Engine1, this.privKey, encrypted)); 73 | } catch (Exception e) { 74 | ex.add(e); 75 | } finally { 76 | if (sm2Engine1 != null) { 77 | sm2EnginePool.returnObject(sm2Engine1); 78 | } 79 | endGate.countDown(); 80 | } 81 | } catch (InterruptedException e) { 82 | e.printStackTrace(); 83 | } 84 | }).start(); 85 | } 86 | 87 | long start = System.currentTimeMillis(); 88 | startGate.countDown(); 89 | try { 90 | endGate.await(); 91 | } catch (InterruptedException e) { 92 | e.printStackTrace(); 93 | } 94 | long end = System.currentTimeMillis(); 95 | System.out.println("cost times :" + (end - start)); 96 | 97 | while (!ex.isEmpty()) { 98 | Exception e = ex.poll(); 99 | e.printStackTrace(); 100 | Assert.fail(exceptionHappened); 101 | } 102 | Assert.assertEquals(threadNum, results.size()); 103 | while (!results.isEmpty()) { 104 | rs = results.poll(); 105 | Assert.assertEquals(new String(message), new String(rs)); 106 | } 107 | } catch (Exception e) { 108 | e.printStackTrace(); 109 | Assert.fail(exceptionHappened); 110 | } 111 | } 112 | 113 | @Test 114 | public void threadSafeSignVerify() { 115 | try { 116 | Queue results = new ConcurrentLinkedQueue<>(); 117 | Queue ex = new ConcurrentLinkedQueue<>(); 118 | SM2Util instance = new SM2Util(); 119 | this.keyPair = instance.generatekeyPair(); 120 | this.pubKey = keyPair.getPublic(); 121 | this.privKey = keyPair.getPrivate(); 122 | byte[] signbyte = instance.sign(this.privKey, message); 123 | boolean rs = instance.verify(this.pubKey, message, signbyte); 124 | Assert.assertTrue(rs); 125 | for (int i = 0; i < 300; i++) { 126 | new Thread(() -> { 127 | try { 128 | results.add(instance.verify(this.pubKey, message, signbyte)); 129 | } catch (Exception e) { 130 | ex.add(e); 131 | } 132 | }).start(); 133 | } 134 | Thread.sleep(5000); 135 | while (!ex.isEmpty()) { 136 | Exception e = ex.poll(); 137 | e.printStackTrace(); 138 | Assert.fail(exceptionHappened); 139 | } 140 | Assert.assertEquals(300, results.size()); 141 | while (!results.isEmpty()) { 142 | Assert.assertTrue(results.poll()); 143 | } 144 | } catch (Exception e) { 145 | e.printStackTrace(); 146 | Assert.fail(exceptionHappened); 147 | } 148 | } 149 | 150 | @Test 151 | public void testKeyGen() { 152 | try { 153 | SM2Util instance = new SM2Util(); 154 | Instant start = Instant.now(); 155 | for (int i = 0; i < 1000; i++) { 156 | instance.generatekeyPair(); 157 | } 158 | System.out.println("cost ms: " + Duration.between(start, Instant.now()).toMillis()); 159 | } catch (Exception e) { 160 | e.printStackTrace(); 161 | } 162 | } 163 | } -------------------------------------------------------------------------------- /src/test/java/SM3UtilTest.java: -------------------------------------------------------------------------------- 1 | import org.apache.commons.lang3.RandomStringUtils; 2 | import org.bouncycastle.crypto.digests.SM3Digest; 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import twgc.gm.pool.SM3DigestPool; 6 | import twgc.gm.sm3.SM3Util; 7 | 8 | /** 9 | * @author Sean 10 | * @Description: 国密算法SM3工具类测试 11 | * @date 2020/9/18 12 | */ 13 | public class SM3UtilTest { 14 | 15 | static int randomData = 128; 16 | static byte[] message = RandomStringUtils.random(randomData).getBytes(); 17 | 18 | static SM3DigestPool sm3DigestPool = new SM3DigestPool(1); 19 | 20 | @Test 21 | public void hashAndVerify() throws Exception { 22 | SM3Digest sm3Digest = null; 23 | try { 24 | sm3Digest = sm3DigestPool.borrowObject(); 25 | byte[] hashVal = SM3Util.hash(sm3Digest, message); 26 | Assert.assertTrue(SM3Util.verify(sm3Digest, message, hashVal)); 27 | } finally { 28 | if (sm3Digest != null) { 29 | sm3DigestPool.returnObject(sm3Digest); 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/test/java/SM3UtilThreadTest.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | import java.util.Queue; 3 | import java.util.concurrent.ConcurrentLinkedQueue; 4 | 5 | import org.apache.commons.lang3.RandomStringUtils; 6 | import org.bouncycastle.crypto.digests.SM3Digest; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | import twgc.gm.pool.SM3DigestPool; 10 | import twgc.gm.sm3.SM3Util; 11 | 12 | 13 | /** 14 | * @author Sam 15 | * @Description: 国密算法SM3工具类线程安全测试 16 | * @date 2021/1/10 17 | */ 18 | public class SM3UtilThreadTest { 19 | 20 | static int randomData = 128; 21 | static byte[] message = RandomStringUtils.random(randomData).getBytes(); 22 | 23 | static SM3DigestPool sm3DigestPool; 24 | 25 | static { 26 | try { 27 | sm3DigestPool = new SM3DigestPool(); 28 | } catch (IOException e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | 33 | static String exceptionHappened = "Exception happened"; 34 | 35 | @Test 36 | public void hashAndVerify() throws Exception { 37 | Queue results = new ConcurrentLinkedQueue<>(); 38 | Queue ex = new ConcurrentLinkedQueue<>(); 39 | SM3Digest sm3Digest = null; 40 | try { 41 | sm3Digest = sm3DigestPool.borrowObject(); 42 | byte[] hashVal = SM3Util.hash(sm3Digest, message); 43 | Assert.assertTrue(SM3Util.verify(sm3Digest, message, hashVal)); 44 | } finally { 45 | if (sm3Digest != null) { 46 | sm3DigestPool.returnObject(sm3Digest); 47 | } 48 | } 49 | 50 | for (int i = 0; i < 300; i++) { 51 | new Thread(() -> { 52 | SM3Digest sm3DigestInThead = null; 53 | try { 54 | sm3DigestInThead = sm3DigestPool.borrowObject(); 55 | byte[] hashValInThread = SM3Util.hash(sm3DigestInThead, message); 56 | results.add(SM3Util.verify(sm3DigestInThead, message, hashValInThread)); 57 | } catch (Exception e) { 58 | ex.add(e); 59 | } finally { 60 | if (sm3DigestInThead != null) { 61 | sm3DigestPool.returnObject(sm3DigestInThead); 62 | } 63 | } 64 | }).start(); 65 | } 66 | while (!ex.isEmpty()) { 67 | Exception e = ex.poll(); 68 | e.printStackTrace(); 69 | Assert.fail(exceptionHappened); 70 | } 71 | Thread.sleep(5000); 72 | Assert.assertEquals(300, results.size()); 73 | while (!results.isEmpty()) { 74 | Assert.assertTrue(results.poll()); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /src/test/java/SM4InterationTest.java: -------------------------------------------------------------------------------- 1 | import javax.crypto.Cipher; 2 | import javax.crypto.spec.SecretKeySpec; 3 | 4 | import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import twgc.gm.pool.SM4CipherPool; 9 | import twgc.gm.sm4.SM4Cipher; 10 | import twgc.gm.sm4.SM4ModeAndPaddingEnum; 11 | import twgc.gm.sm4.SM4Util; 12 | 13 | 14 | public class SM4InterationTest { 15 | 16 | @Test 17 | public void sm4Interaction() throws Exception { 18 | SM4CipherPool sm4CipherPool = new SM4CipherPool(10); 19 | SM4Cipher sm4Cipher = null; 20 | try { 21 | sm4Cipher = sm4CipherPool.borrowObject(); 22 | Cipher cipher = sm4Cipher.getCipher(SM4ModeAndPaddingEnum.SM4_CBC_PKCS7Padding); 23 | 24 | byte[] key = "1234567890abcdef".getBytes(); 25 | SecretKeySpec sm4Key = new SecretKeySpec(key, SM4ModeAndPaddingEnum.SM4_CBC_PKCS7Padding.getName()); 26 | byte[] iv = "ilovegolangjava.".getBytes(); 27 | SM4Util instance = new SM4Util(); 28 | //解密校验 29 | String cryptText = "8781d981f7ffd6c1a780f8b213f596aa535c8bb6389923f8329f79a1707966e2"; 30 | byte[] b = instance.decrypt(cipher, ByteUtils.fromHexString(cryptText), sm4Key, iv); 31 | Assert.assertEquals("I am encrypted by golang SM4.", new String(b)); 32 | 33 | //加密校验,SM4加密以下明文以供Go SM4进行解密验证 34 | byte[] msg = "I am encrypted by java SM4.".getBytes(); 35 | byte[] cryptData = instance.encrypt(cipher, msg, sm4Key, iv); 36 | String cryptStr = ByteUtils.toHexString(cryptData); 37 | System.out.println(cryptStr); 38 | } finally { 39 | if (sm4Cipher != null) { 40 | sm4CipherPool.returnObject(sm4Cipher); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/SM4UtilTest.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | import java.nio.charset.StandardCharsets; 3 | import java.security.NoSuchAlgorithmException; 4 | import java.security.NoSuchProviderException; 5 | import java.util.Arrays; 6 | import java.util.Collection; 7 | import java.util.Queue; 8 | import java.util.concurrent.ConcurrentLinkedQueue; 9 | import javax.crypto.Cipher; 10 | import javax.crypto.spec.SecretKeySpec; 11 | 12 | import org.apache.commons.lang3.RandomStringUtils; 13 | import org.junit.Assert; 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | import org.junit.runners.Parameterized; 17 | import org.junit.runners.Parameterized.Parameters; 18 | import twgc.gm.pool.SM4CipherPool; 19 | import twgc.gm.sm4.SM4Cipher; 20 | import twgc.gm.sm4.SM4ModeAndPaddingEnum; 21 | import twgc.gm.sm4.SM4Util; 22 | 23 | /** 24 | * @author Sean 25 | * @Description: 国密算法SM4工具类测试 26 | * @date 2020/9/18 27 | */ 28 | 29 | @RunWith(Parameterized.class) 30 | public class SM4UtilTest { 31 | private static byte[] content = null; 32 | private static final byte[] content16 = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}; 33 | private static byte[] iv = null; 34 | private static SM4ModeAndPaddingEnum type; 35 | static int randomData = 128; 36 | static String message = RandomStringUtils.random(randomData); 37 | static String exceptionHappened = "Exception happened"; 38 | SM4CipherPool sm4CipherPool; 39 | { 40 | try { 41 | sm4CipherPool = new SM4CipherPool(); 42 | } catch (IOException e) { 43 | e.printStackTrace(); 44 | } 45 | } 46 | 47 | @SuppressWarnings("rawtypes") 48 | @Parameters(name = "{index}: sm4({1})") 49 | public static Collection prepareData() { 50 | Object[][] object = { 51 | {content16, SM4ModeAndPaddingEnum.SM4_ECB_NoPadding, false}, 52 | {message.getBytes(StandardCharsets.UTF_8), SM4ModeAndPaddingEnum.SM4_ECB_PKCS5Padding, false}, 53 | {message.getBytes(StandardCharsets.UTF_8), SM4ModeAndPaddingEnum.SM4_ECB_PKCS7Padding, false}, 54 | {content16, SM4ModeAndPaddingEnum.SM4_CBC_NoPadding, true}, 55 | {message.getBytes(StandardCharsets.UTF_8), SM4ModeAndPaddingEnum.SM4_CBC_PKCS5Padding, true}, 56 | {message.getBytes(StandardCharsets.UTF_8), SM4ModeAndPaddingEnum.SM4_CBC_PKCS7Padding, true}, 57 | {message.getBytes(StandardCharsets.UTF_8), SM4ModeAndPaddingEnum.SM4_CFB_NoPadding, true}, 58 | {message.getBytes(StandardCharsets.UTF_8), SM4ModeAndPaddingEnum.SM4_OFB_NoPadding, true}, 59 | {message.getBytes(StandardCharsets.UTF_8), SM4ModeAndPaddingEnum.SM4_CTR_NoPadding, true} 60 | }; 61 | return Arrays.asList(object); 62 | } 63 | 64 | public SM4UtilTest(byte[] content, SM4ModeAndPaddingEnum type, boolean flag) throws NoSuchProviderException, NoSuchAlgorithmException { 65 | SM4UtilTest.content = content; 66 | SM4UtilTest.type = type; 67 | if (flag) { 68 | SM4Util instance = new SM4Util(); 69 | iv = instance.generateKey(); 70 | } 71 | } 72 | 73 | @Test 74 | public void testingSM4() throws Exception { 75 | SM4Cipher sm4Cipher = null; 76 | try { 77 | sm4Cipher = sm4CipherPool.borrowObject(); 78 | Cipher cipher = sm4Cipher.getCipher(type); 79 | SM4Util instance = new SM4Util(); 80 | byte[] key = instance.generateKey(); 81 | SecretKeySpec sm4Key = new SecretKeySpec(key, type.getName()); 82 | System.out.println("===== " + type + " ====="); 83 | // 加密 84 | byte[] v = instance.encrypt(cipher, content, sm4Key, iv); 85 | // 解密 86 | byte[] c = instance.decrypt(cipher, v, sm4Key, iv); 87 | 88 | System.out.println("解密内容:" + new String(c)); 89 | Assert.assertArrayEquals(c, content); 90 | } finally { 91 | if (sm4Cipher != null) { 92 | sm4CipherPool.returnObject(sm4Cipher); 93 | } 94 | } 95 | } 96 | 97 | @Test 98 | public void threadsafe() throws Exception { 99 | SM4Cipher sm4Cipher; 100 | sm4Cipher = sm4CipherPool.borrowObject(); 101 | Queue results = new ConcurrentLinkedQueue<>(); 102 | Queue ex = new ConcurrentLinkedQueue<>(); 103 | SM4Util instance = new SM4Util(); 104 | byte[] key = instance.generateKey(); 105 | SecretKeySpec sm4Key = new SecretKeySpec(key, type.getName()); 106 | byte[] v; 107 | try { 108 | Cipher cipher = sm4Cipher.getCipher(type); 109 | System.out.println("===== " + type + " ====="); 110 | // 加密 111 | v = instance.encrypt(cipher, content, sm4Key, iv); 112 | // 解密 113 | byte[] c = instance.decrypt(cipher, v, sm4Key, iv); 114 | 115 | System.out.println("解密内容:" + new String(c)); 116 | Assert.assertArrayEquals(c, content); 117 | } finally { 118 | if (sm4Cipher != null) { 119 | sm4CipherPool.returnObject(sm4Cipher); 120 | } 121 | } 122 | for (int i = 0; i < 300; i++) { 123 | new Thread(() -> { 124 | SM4Cipher sm4Ciphertest = null; 125 | try { 126 | sm4Ciphertest = sm4CipherPool.borrowObject(); 127 | Cipher ciphertest = sm4Ciphertest.getCipher(type); 128 | results.add(instance.decrypt(ciphertest, v, sm4Key, iv)); 129 | } catch (Exception e) { 130 | ex.add(e); 131 | } finally { 132 | if (sm4Ciphertest != null) { 133 | sm4CipherPool.returnObject(sm4Ciphertest); 134 | } 135 | } 136 | }).start(); 137 | } 138 | Thread.sleep(5000); 139 | while (!ex.isEmpty()) { 140 | Exception e = ex.poll(); 141 | e.printStackTrace(); 142 | Assert.fail(exceptionHappened); 143 | } 144 | Assert.assertEquals(300, results.size()); 145 | while (!results.isEmpty()) { 146 | Assert.assertArrayEquals(results.poll(), content); 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/test/resources/pool-config.yaml: -------------------------------------------------------------------------------- 1 | default: &default 2 | maxTotal: 10 3 | maxIdle: 8 4 | minIdle: 2 5 | maxWaitMillis: 1000 6 | 7 | sm2: 8 | <<: *default 9 | maxTotal: 30 10 | 11 | sm3: 12 | <<: *default 13 | maxTotal: 30 14 | 15 | sm4: 16 | <<: *default 17 | minIdle: 4 -------------------------------------------------------------------------------- /src/test/resources/testdata.yml: -------------------------------------------------------------------------------- 1 | javagm: 2 | testdata: 3 | private-key: | 4 | -----BEGIN PRIVATE KEY----- 5 | MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgc0UCgfELjC0V+xUm 6 | ELYFmy0J0cee42ZpKyQ4FRTBlJSgCgYIKoEcz1UBgi2hRANCAATJbIFbxcAaDxMk 7 | 7XExTRU/bBnGEu6YfaleJxnLZS40NDNjZV+ztveWfLZk2+oWieykM3/yZ/6IieJk 8 | 5uuohUjD 9 | -----END PRIVATE KEY----- 10 | public-key: | 11 | -----BEGIN PUBLIC KEY----- 12 | MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEyWyBW8XAGg8TJO1xMU0VP2wZxhLu 13 | mH2pXicZy2UuNDQzY2Vfs7b3lny2ZNvqFonspDN/8mf+iIniZObrqIVIww== 14 | -----END PUBLIC KEY----- 15 | cert: | 16 | -----BEGIN CERTIFICATE----- 17 | MIIBdzCCAR2gAwIBAgIJAfA3Qnph7CieMAoGCCqBHM9VAYN1MBIxEDAOBgNVBAMM 18 | B1Jvb3QgQ0EwHhcNMjMxMjAyMTQzMjMxWhcNODkxMjI3MDAwMDAwWjASMRAwDgYD 19 | VQQDEwdSb290IENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEyWyBW8XAGg8T 20 | JO1xMU0VP2wZxhLumH2pXicZy2UuNDQzY2Vfs7b3lny2ZNvqFonspDN/8mf+iIni 21 | ZObrqIVIw6NcMFowHQYDVR0OBBYEFOMvj2LPGlkOw1M1Pj34klVi8SFgMA8GA1Ud 22 | EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMBgGA1UdEQQRMA+BDXRlc3RAdHdn 23 | Yy5jb20wCgYIKoEcz1UBg3UDSAAwRQIgTeoLjt+eP3kwQg17G+l12wj4MQNed1hW 24 | aZZkJe43rkICIQCdI3WhnrvzhbEijsTXL1woIwnFgY9MIci7BmKLMpMM6w== 25 | -----END CERTIFICATE----- --------------------------------------------------------------------------------