├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── ci.yml │ └── oss-deploy.yml ├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── LICENSE ├── README.md ├── mvnw ├── mvnw.cmd ├── oss-release.sh ├── ossrh-settings.xml ├── pom.xml └── src ├── main ├── java │ └── org │ │ └── zz │ │ └── gmhelper │ │ ├── BCECUtil.java │ │ ├── GMBaseUtil.java │ │ ├── SM2Cipher.java │ │ ├── SM2KeyExchangeUtil.java │ │ ├── SM2PreprocessSigner.java │ │ ├── SM2Util.java │ │ ├── SM3Util.java │ │ ├── SM4Util.java │ │ └── cert │ │ ├── CertSNAllocator.java │ │ ├── CommonUtil.java │ │ ├── FileSNAllocator.java │ │ ├── RandomSNAllocator.java │ │ ├── SM2CertUtil.java │ │ ├── SM2PfxMaker.java │ │ ├── SM2Pkcs12Maker.java │ │ ├── SM2PrivateKey.java │ │ ├── SM2PublicKey.java │ │ ├── SM2X509CertMaker.java │ │ └── exception │ │ └── InvalidX500NameException.java └── resources │ └── sn.dat └── test └── java └── org └── zz └── gmhelper ├── cert └── test │ ├── FileSNAllocatorTest.java │ ├── SM2CertUtilTest.java │ ├── SM2PfxMakerTest.java │ ├── SM2Pkcs12MakerTest.java │ ├── SM2PrivateKeyTest.java │ └── SM2X509CertMakerTest.java └── test ├── AllTest.java ├── BCECUtilTest.java ├── GMBaseTest.java ├── SM2KeyExchangeUtilTest.java ├── SM2PreprocessSignerTest.java ├── SM2UtilTest.java ├── SM3UtilTest.java ├── SM4UtilTest.java └── util └── FileUtil.java /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: https://weibo.com/5978668016/KDzUy5mOq 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: maven 4 | directory: "/" 5 | target-branch: "dev" 6 | schedule: 7 | interval: daily 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Java CI with Maven 5 | on: 6 | push: 7 | branches: 8 | - master 9 | - dev 10 | pull_request: 11 | types: [opened, synchronize, reopened] 12 | jobs: 13 | build: 14 | name: Build 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 20 | 21 | - name: Set up JDK 17 22 | uses: actions/setup-java@v3 23 | with: 24 | distribution: 'adopt' 25 | java-version: '17' 26 | 27 | - name: Cache local Maven repository 28 | uses: actions/cache@v3.3.1 29 | env: 30 | cache-name: cache-mvn 31 | with: 32 | path: ~/.m2/repository 33 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/pom.xml') }} 34 | restore-keys: | 35 | ${{ runner.os }}-build-${{ env.cache-name }}- 36 | ${{ runner.os }}-build- 37 | ${{ runner.os }}- 38 | 39 | - name: Compile 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any 42 | run: mvn -B clean compile --file pom.xml -U 43 | -------------------------------------------------------------------------------- /.github/workflows/oss-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Publish to the Maven Central 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - snapshot 7 | tags: 8 | - '*' 9 | 10 | jobs: 11 | oss-deploy: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | 17 | - name: Set up JDK 17 18 | uses: actions/setup-java@v3 19 | with: 20 | distribution: 'adopt' 21 | java-version: '17' 22 | 23 | - name: Cache local Maven repository 24 | uses: actions/cache@v3.3.1 25 | env: 26 | cache-name: cache-mvn 27 | with: 28 | path: ~/.m2/repository 29 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/pom.xml') }} 30 | restore-keys: | 31 | ${{ runner.os }}-build-${{ env.cache-name }}- 32 | ${{ runner.os }}-build- 33 | ${{ runner.os }}- 34 | 35 | - name: Import GPG 36 | uses: crazy-max/ghaction-import-gpg@v4.4.0 37 | with: 38 | gpg_private_key: ${{ secrets.MAVEN_GPG_KEY }} 39 | passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} 40 | 41 | - name: Publish to Maven Central 42 | run: ./mvnw --settings ./ossrh-settings.xml clean deploy -Dgpg.passphrase=${MAVEN_GPG_PASSPHRASE} -P 'oss-release' 43 | env: 44 | MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} 45 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} 46 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### gradle ### 2 | .gradle 3 | /build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | 6 | ### STS ### 7 | .settings/ 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | bin/ 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | !.idea/copyright/*.xml 19 | *.iws 20 | *.iml 21 | *.ipr 22 | *.lock 23 | rebel.xml 24 | 25 | ### NetBeans ### 26 | nbproject/private/ 27 | build/ 28 | nbbuild/ 29 | nbdist/ 30 | .nb-gradle/ 31 | 32 | ### maven ### 33 | target/ 34 | *.war 35 | *.ear 36 | *.zip 37 | *.tar 38 | *.tar.gz 39 | !**/src/main/** 40 | !**/src/test/** 41 | !/.mvn/wrapper/maven-wrapper.jar 42 | 43 | # flattened pom 44 | .flattened-pom.xml 45 | 46 | ### logs #### 47 | /logs/ 48 | *.log 49 | 50 | ### temp ignore ### 51 | *.cache 52 | *.diff 53 | *.patch 54 | *.tmp 55 | *.java~ 56 | *.properties~ 57 | *.xml~ 58 | 59 | ### system ignore ### 60 | .DS_Store 61 | Thumbs.db 62 | Servers 63 | .metadata 64 | upload 65 | gen_code 66 | 67 | # BlueJ files 68 | *.ctxt 69 | 70 | ### node ### 71 | node_modules 72 | !yarn.lock 73 | !package.lock 74 | 75 | ###################################################################### 76 | # Others 77 | *.xml.versionsBackup 78 | *.xml.releaseBackup 79 | pom.xml.next 80 | release.properties 81 | .metadata/ 82 | settings-ossrh.xml 83 | 84 | # Mobile Tools for Java (J2ME) 85 | .mtj.tmp/ 86 | 87 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 88 | hs_err_pid* 89 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZZMarquis/gmhelper/3ccaf2215b572d14af90cb7c7af6ab709ce01d4a/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /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 | # gmhelper 2 | 3 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.zz/gmhelper/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.zz/gmhelper) 4 | 5 | BC库从1.59版本开始已经基本实现了国密算法(SM2、SM3、SM4),本项目是基于BC库做的一些功能的简单封装,也可以当成一个sample看,目前主要实现了以下几块功能: 6 | 1. SM2/SM3/SM4算法的简单封装 7 | 2. SM2 X509v3证书的签发 8 | 3. SM2 pfx证书的签发 9 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Maven Start Up Batch script 4 | # 5 | # Required ENV vars: 6 | # ------------------ 7 | # JAVA_HOME - location of a JDK home dir 8 | # 9 | # Optional ENV vars 10 | # ----------------- 11 | # M2_HOME - location of maven2's installed home dir 12 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 13 | # e.g. to debug Maven itself, use 14 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 15 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 16 | # ---------------------------------------------------------------------------- 17 | 18 | if [ -z "$MAVEN_SKIP_RC" ] ; then 19 | 20 | if [ -f /etc/mavenrc ] ; then 21 | . /etc/mavenrc 22 | fi 23 | 24 | if [ -f "$HOME/.mavenrc" ] ; then 25 | . "$HOME/.mavenrc" 26 | fi 27 | 28 | fi 29 | 30 | # OS specific support. $var _must_ be set to either true or false. 31 | cygwin=false; 32 | darwin=false; 33 | mingw=false 34 | case "`uname`" in 35 | CYGWIN*) cygwin=true ;; 36 | MINGW*) mingw=true;; 37 | Darwin*) darwin=true 38 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 39 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 40 | if [ -z "$JAVA_HOME" ]; then 41 | if [ -x "/usr/libexec/java_home" ]; then 42 | export JAVA_HOME="`/usr/libexec/java_home`" 43 | else 44 | export JAVA_HOME="/Library/Java/Home" 45 | fi 46 | fi 47 | ;; 48 | esac 49 | 50 | if [ -z "$JAVA_HOME" ] ; then 51 | if [ -r /etc/gentoo-release ] ; then 52 | JAVA_HOME=`java-config --jre-home` 53 | fi 54 | fi 55 | 56 | if [ -z "$M2_HOME" ] ; then 57 | ## resolve links - $0 may be a link to maven's home 58 | PRG="$0" 59 | 60 | # need this for relative symlinks 61 | while [ -h "$PRG" ] ; do 62 | ls=`ls -ld "$PRG"` 63 | link=`expr "$ls" : '.*-> \(.*\)$'` 64 | if expr "$link" : '/.*' > /dev/null; then 65 | PRG="$link" 66 | else 67 | PRG="`dirname "$PRG"`/$link" 68 | fi 69 | done 70 | 71 | saveddir=`pwd` 72 | 73 | M2_HOME=`dirname "$PRG"`/.. 74 | 75 | # make it fully qualified 76 | M2_HOME=`cd "$M2_HOME" && pwd` 77 | 78 | cd "$saveddir" 79 | # echo Using m2 at $M2_HOME 80 | fi 81 | 82 | # For Cygwin, ensure paths are in UNIX format before anything is touched 83 | if $cygwin ; then 84 | [ -n "$M2_HOME" ] && 85 | M2_HOME=`cygpath --unix "$M2_HOME"` 86 | [ -n "$JAVA_HOME" ] && 87 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 88 | [ -n "$CLASSPATH" ] && 89 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 90 | fi 91 | 92 | # For Mingw, ensure paths are in UNIX format before anything is touched 93 | if $mingw ; then 94 | [ -n "$M2_HOME" ] && 95 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 96 | [ -n "$JAVA_HOME" ] && 97 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 98 | fi 99 | 100 | if [ -z "$JAVA_HOME" ]; then 101 | javaExecutable="`which javac`" 102 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 103 | # readlink(1) is not available as standard on Solaris 10. 104 | readLink=`which readlink` 105 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 106 | if $darwin ; then 107 | javaHome="`dirname \"$javaExecutable\"`" 108 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 109 | else 110 | javaExecutable="`readlink -f \"$javaExecutable\"`" 111 | fi 112 | javaHome="`dirname \"$javaExecutable\"`" 113 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 114 | JAVA_HOME="$javaHome" 115 | export JAVA_HOME 116 | fi 117 | fi 118 | fi 119 | 120 | if [ -z "$JAVACMD" ] ; then 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD="$JAVA_HOME/jre/sh/java" 125 | else 126 | JAVACMD="$JAVA_HOME/bin/java" 127 | fi 128 | else 129 | JAVACMD="`which java`" 130 | fi 131 | fi 132 | 133 | if [ ! -x "$JAVACMD" ] ; then 134 | echo "Error: JAVA_HOME is not defined correctly." >&2 135 | echo " We cannot execute $JAVACMD" >&2 136 | exit 1 137 | fi 138 | 139 | if [ -z "$JAVA_HOME" ] ; then 140 | echo "Warning: JAVA_HOME environment variable is not set." 141 | fi 142 | 143 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 144 | 145 | # traverses directory structure from process work directory to filesystem root 146 | # first directory with .mvn subdirectory is considered project base directory 147 | find_maven_basedir() { 148 | 149 | if [ -z "$1" ] 150 | then 151 | echo "Path not specified to find_maven_basedir" 152 | return 1 153 | fi 154 | 155 | basedir="$1" 156 | wdir="$1" 157 | while [ "$wdir" != '/' ] ; do 158 | if [ -d "$wdir"/.mvn ] ; then 159 | basedir=$wdir 160 | break 161 | fi 162 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 163 | if [ -d "${wdir}" ]; then 164 | wdir=`cd "$wdir/.."; pwd` 165 | fi 166 | # end of workaround 167 | done 168 | echo "${basedir}" 169 | } 170 | 171 | # concatenates all lines of a file 172 | concat_lines() { 173 | if [ -f "$1" ]; then 174 | echo "$(tr -s '\n' ' ' < "$1")" 175 | fi 176 | } 177 | 178 | BASE_DIR=`find_maven_basedir "$(pwd)"` 179 | if [ -z "$BASE_DIR" ]; then 180 | exit 1; 181 | fi 182 | 183 | ########################################################################################## 184 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 185 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 186 | ########################################################################################## 187 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 188 | if [ "$MVNW_VERBOSE" = true ]; then 189 | echo "Found .mvn/wrapper/maven-wrapper.jar" 190 | fi 191 | else 192 | if [ "$MVNW_VERBOSE" = true ]; then 193 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 194 | fi 195 | if [ -n "$MVNW_REPOURL" ]; then 196 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 197 | else 198 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 199 | fi 200 | while IFS="=" read key value; do 201 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 202 | esac 203 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 204 | if [ "$MVNW_VERBOSE" = true ]; then 205 | echo "Downloading from: $jarUrl" 206 | fi 207 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 208 | if $cygwin; then 209 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 210 | fi 211 | 212 | if command -v wget > /dev/null; then 213 | if [ "$MVNW_VERBOSE" = true ]; then 214 | echo "Found wget ... using wget" 215 | fi 216 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 217 | wget "$jarUrl" -O "$wrapperJarPath" 218 | else 219 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 220 | fi 221 | elif command -v curl > /dev/null; then 222 | if [ "$MVNW_VERBOSE" = true ]; then 223 | echo "Found curl ... using curl" 224 | fi 225 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 226 | curl -o "$wrapperJarPath" "$jarUrl" -f 227 | else 228 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 229 | fi 230 | 231 | else 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Falling back to using Java to download" 234 | fi 235 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 236 | # For Cygwin, switch paths to Windows format before running javac 237 | if $cygwin; then 238 | javaClass=`cygpath --path --windows "$javaClass"` 239 | fi 240 | if [ -e "$javaClass" ]; then 241 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 242 | if [ "$MVNW_VERBOSE" = true ]; then 243 | echo " - Compiling MavenWrapperDownloader.java ..." 244 | fi 245 | # Compiling the Java class 246 | ("$JAVA_HOME/bin/javac" "$javaClass") 247 | fi 248 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 249 | # Running the downloader 250 | if [ "$MVNW_VERBOSE" = true ]; then 251 | echo " - Running MavenWrapperDownloader.java ..." 252 | fi 253 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 254 | fi 255 | fi 256 | fi 257 | fi 258 | ########################################################################################## 259 | # End of extension 260 | ########################################################################################## 261 | 262 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 263 | if [ "$MVNW_VERBOSE" = true ]; then 264 | echo $MAVEN_PROJECTBASEDIR 265 | fi 266 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 267 | 268 | # For Cygwin, switch paths to Windows format before running java 269 | if $cygwin; then 270 | [ -n "$M2_HOME" ] && 271 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 272 | [ -n "$JAVA_HOME" ] && 273 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 274 | [ -n "$CLASSPATH" ] && 275 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 276 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 277 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 278 | fi 279 | 280 | # Provide a "standardized" way to retrieve the CLI args that will 281 | # work with both Windows and non-Windows executions. 282 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 283 | export MAVEN_CMD_LINE_ARGS 284 | 285 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 286 | 287 | exec "$JAVACMD" \ 288 | $MAVEN_OPTS \ 289 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 290 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 291 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 292 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /oss-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./mvnw clean deploy -Dgpg.passphrase=${GPG_PWD} -DskipTests=true -P 'oss-release' 4 | -------------------------------------------------------------------------------- /ossrh-settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | ossrh 8 | ${env.OSSRH_USERNAME} 9 | ${env.OSSRH_PASSWORD} 10 | 11 | 12 | 13 | 14 | 15 | aliyun-repo 16 | 17 | 18 | aliyun-plugin 19 | https://maven.aliyun.com/repository/public 20 | 21 | true 22 | 23 | 24 | true 25 | 26 | 27 | 28 | 29 | 30 | aliyun 31 | aliyun 32 | https://maven.aliyun.com/repository/public 33 | 34 | true 35 | 36 | 37 | true 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.zz 6 | gmhelper 7 | ${revision} 8 | jar 9 | 10 | gmhelper 11 | https://github.com/ZZMarquis/gmhelper 12 | 基于BC库:国密SM2/SM3/SM4算法简单封装;实现SM2 X509v3证书的签发;实现SM2 pfx证书的签发 13 | 14 | 15 | 16 | APACHE LICENSE, VERSION 2.0 17 | https://www.apache.org/licenses/LICENSE-2.0 18 | repo 19 | 20 | 21 | 22 | 23 | ZZMarquis 24 | https://github.com/ZZMarquis 25 | 26 | 27 | 28 | https://github.com/ZZMarquis/gmhelper 29 | scm:git:git://github.com/ZZMarquis/gmhelper.git 30 | scm:git:https://github.com/ZZMarquis/gmhelper.git 31 | HEAD 32 | 33 | 34 | 35 | 36 | ZZMarquis 37 | 445336534@qq.com 38 | 39 | 40 | 41 | 42 | 0.0.1-SNAPSHOT 43 | UTF-8 44 | UTF-8 45 | 1.7 46 | 1.7 47 | 48 | 0.0.38 49 | 1.2.7 50 | 3.3.1 51 | 3.0.1 52 | 2.22.2 53 | 2.5.3 54 | 3.2.1 55 | 3.0.1 56 | 0.8.8 57 | 58 | 59 | 60 | 61 | org.bouncycastle 62 | bcprov-jdk15on 63 | 1.67 64 | 65 | 66 | org.bouncycastle 67 | bcpkix-jdk15on 68 | 1.67 69 | 70 | 71 | 72 | junit 73 | junit 74 | 4.13.1 75 | test 76 | 77 | 78 | 79 | 80 | ${project.artifactId} 81 | 82 | 83 | org.apache.maven.plugins 84 | maven-resources-plugin 85 | ${maven-resources-plugin.version} 86 | 87 | 88 | pl.project13.maven 89 | git-commit-id-plugin 90 | ${git-commit-id-plugin.version} 91 | 92 | 98 | 99 | org.codehaus.mojo 100 | flatten-maven-plugin 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-surefire-plugin 105 | 106 | 107 | org.apache.maven.plugins 108 | maven-release-plugin 109 | 110 | 111 | org.apache.maven.plugins 112 | maven-source-plugin 113 | 114 | 115 | org.jacoco 116 | jacoco-maven-plugin 117 | 118 | 119 | 120 | 121 | 122 | 123 | io.spring.javaformat 124 | spring-javaformat-maven-plugin 125 | ${spring-javaformat.plugin.version} 126 | 127 | 128 | validate 129 | true 130 | 131 | validate 132 | 133 | 134 | 135 | 136 | 137 | org.codehaus.mojo 138 | flatten-maven-plugin 139 | ${flatten-maven-plugin.version} 140 | 141 | true 142 | oss 143 | 144 | 145 | 146 | flatten 147 | process-resources 148 | 149 | flatten 150 | 151 | 152 | 153 | flatten.clean 154 | clean 155 | 156 | clean 157 | 158 | 159 | 160 | 161 | 162 | org.apache.maven.plugins 163 | maven-surefire-plugin 164 | ${maven-surefire-plugin.version} 165 | 166 | false 167 | 168 | false 169 | 170 | 171 | 172 | org.apache.maven.plugins 173 | maven-release-plugin 174 | ${maven-release-plugin.version} 175 | 176 | true 177 | chore(maven release):  178 | @{project.version} 179 | deploy 180 | 181 | 182 | 183 | org.apache.maven.plugins 184 | maven-source-plugin 185 | ${maven-source-plugin.version} 186 | 187 | 188 | attach-sources 189 | verify 190 | 191 | jar-no-fork 192 | 193 | 194 | 195 | 196 | 197 | 198 | org.jacoco 199 | jacoco-maven-plugin 200 | ${jacoco-maven-plugin.version} 201 | 202 | 203 | 204 | prepare-agent 205 | 206 | 207 | 208 | report 209 | test 210 | 211 | report 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | oss-release 223 | 224 | true 225 | 226 | 227 | 228 | 229 | org.apache.maven.plugins 230 | maven-javadoc-plugin 231 | 3.5.0 232 | 233 | UTF-8 234 | UTF-8 235 | UTF-8 236 | 237 | 238 | 239 | attach-javadocs 240 | 241 | jar 242 | 243 | 244 | -Xdoclint:none 245 | 246 | 247 | 248 | 249 | 250 | org.apache.maven.plugins 251 | maven-gpg-plugin 252 | ${maven-gpg-plugin.version} 253 | 254 | 255 | sign-artifacts 256 | verify 257 | 258 | sign 259 | 260 | 261 | 262 | 263 | 264 | org.sonatype.plugins 265 | nexus-staging-maven-plugin 266 | 1.6.8 267 | true 268 | 269 | ossrh 270 | https://oss.sonatype.org/ 271 | true 272 | 273 | 274 | 275 | 276 | 277 | 278 | ossrh 279 | https://oss.sonatype.org/content/repositories/snapshots 280 | 281 | 282 | ossrh 283 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 284 | 285 | 286 | 287 | 288 | oss-snapshots 289 | 290 | true 291 | 292 | 293 | 294 | snapshots-repo 295 | https://oss.sonatype.org/content/repositories/snapshots 296 | 297 | false 298 | 299 | 300 | true 301 | 302 | 303 | 304 | 305 | 306 | 307 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/BCECUtil.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper; 2 | 3 | import org.bouncycastle.asn1.ASN1Encodable; 4 | import org.bouncycastle.asn1.ASN1EncodableVector; 5 | import org.bouncycastle.asn1.ASN1Encoding; 6 | import org.bouncycastle.asn1.ASN1Integer; 7 | import org.bouncycastle.asn1.ASN1ObjectIdentifier; 8 | import org.bouncycastle.asn1.ASN1OctetString; 9 | import org.bouncycastle.asn1.ASN1Primitive; 10 | import org.bouncycastle.asn1.DERNull; 11 | import org.bouncycastle.asn1.DEROctetString; 12 | import org.bouncycastle.asn1.DERSequence; 13 | import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; 14 | import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 15 | import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 16 | import org.bouncycastle.asn1.x9.X962Parameters; 17 | import org.bouncycastle.asn1.x9.X9ECParameters; 18 | import org.bouncycastle.asn1.x9.X9ECPoint; 19 | import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; 20 | import org.bouncycastle.crypto.AsymmetricCipherKeyPair; 21 | import org.bouncycastle.crypto.generators.ECKeyPairGenerator; 22 | import org.bouncycastle.crypto.params.ECDomainParameters; 23 | import org.bouncycastle.crypto.params.ECKeyGenerationParameters; 24 | import org.bouncycastle.crypto.params.ECKeyParameters; 25 | import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 26 | import org.bouncycastle.crypto.params.ECPublicKeyParameters; 27 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; 28 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; 29 | import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; 30 | import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; 31 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 32 | import org.bouncycastle.jce.spec.ECNamedCurveSpec; 33 | import org.bouncycastle.jce.spec.ECParameterSpec; 34 | import org.bouncycastle.math.ec.ECCurve; 35 | import org.bouncycastle.math.ec.ECPoint; 36 | import org.bouncycastle.math.ec.FixedPointCombMultiplier; 37 | import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; 38 | import org.bouncycastle.util.io.pem.PemObject; 39 | import org.bouncycastle.util.io.pem.PemReader; 40 | import org.bouncycastle.util.io.pem.PemWriter; 41 | 42 | import java.io.ByteArrayInputStream; 43 | import java.io.ByteArrayOutputStream; 44 | import java.io.IOException; 45 | import java.io.InputStreamReader; 46 | import java.io.OutputStreamWriter; 47 | import java.math.BigInteger; 48 | import java.security.InvalidAlgorithmParameterException; 49 | import java.security.KeyFactory; 50 | import java.security.KeyPair; 51 | import java.security.KeyPairGenerator; 52 | import java.security.NoSuchAlgorithmException; 53 | import java.security.NoSuchProviderException; 54 | import java.security.SecureRandom; 55 | import java.security.spec.ECGenParameterSpec; 56 | import java.security.spec.InvalidKeySpecException; 57 | import java.security.spec.PKCS8EncodedKeySpec; 58 | import java.security.spec.X509EncodedKeySpec; 59 | 60 | /** 61 | * 这个工具类的方法,也适用于其他基于BC库的ECC算法 62 | */ 63 | public class BCECUtil { 64 | private static final String ALGO_NAME_EC = "EC"; 65 | private static final String PEM_STRING_PUBLIC = "PUBLIC KEY"; 66 | private static final String PEM_STRING_ECPRIVATEKEY = "EC PRIVATE KEY"; 67 | 68 | /** 69 | * 生成ECC密钥对 70 | * 71 | * @return ECC密钥对 72 | */ 73 | public static AsymmetricCipherKeyPair generateKeyPairParameter( 74 | ECDomainParameters domainParameters, SecureRandom random) { 75 | ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParameters, 76 | random); 77 | ECKeyPairGenerator keyGen = new ECKeyPairGenerator(); 78 | keyGen.init(keyGenerationParams); 79 | return keyGen.generateKeyPair(); 80 | } 81 | 82 | public static KeyPair generateKeyPair(ECDomainParameters domainParameters, SecureRandom random) 83 | throws NoSuchProviderException, NoSuchAlgorithmException, 84 | InvalidAlgorithmParameterException { 85 | KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME); 86 | ECParameterSpec parameterSpec = new ECParameterSpec(domainParameters.getCurve(), domainParameters.getG(), 87 | domainParameters.getN(), domainParameters.getH()); 88 | kpg.initialize(parameterSpec, random); 89 | return kpg.generateKeyPair(); 90 | } 91 | 92 | public static int getCurveLength(ECKeyParameters ecKey) { 93 | return getCurveLength(ecKey.getParameters()); 94 | } 95 | 96 | public static int getCurveLength(ECDomainParameters domainParams) { 97 | return (domainParams.getCurve().getFieldSize() + 7) / 8; 98 | } 99 | 100 | public static byte[] fixToCurveLengthBytes(int curveLength, byte[] src) { 101 | if (src.length == curveLength) { 102 | return src; 103 | } 104 | 105 | byte[] result = new byte[curveLength]; 106 | if (src.length > curveLength) { 107 | System.arraycopy(src, src.length - result.length, result, 0, result.length); 108 | } else { 109 | System.arraycopy(src, 0, result, result.length - src.length, src.length); 110 | } 111 | return result; 112 | } 113 | 114 | /** 115 | * @param dHex 十六进制字符串形式的私钥d值,如果是SM2算法,Hex字符串长度应该是64(即32字节) 116 | * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS} 117 | * @return 118 | */ 119 | public static ECPrivateKeyParameters createECPrivateKeyParameters( 120 | String dHex, ECDomainParameters domainParameters) { 121 | return createECPrivateKeyParameters(ByteUtils.fromHexString(dHex), domainParameters); 122 | } 123 | 124 | /** 125 | * @param dBytes 字节数组形式的私钥d值,如果是SM2算法,应该是32字节 126 | * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS} 127 | * @return 128 | */ 129 | public static ECPrivateKeyParameters createECPrivateKeyParameters( 130 | byte[] dBytes, ECDomainParameters domainParameters) { 131 | return createECPrivateKeyParameters(new BigInteger(1, dBytes), domainParameters); 132 | } 133 | 134 | /** 135 | * @param d 大数形式的私钥d值 136 | * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS} 137 | * @return 138 | */ 139 | public static ECPrivateKeyParameters createECPrivateKeyParameters( 140 | BigInteger d, ECDomainParameters domainParameters) { 141 | return new ECPrivateKeyParameters(d, domainParameters); 142 | } 143 | 144 | /** 145 | * 根据EC私钥构造EC公钥 146 | * 147 | * @param priKey ECC私钥参数对象 148 | * @return 149 | */ 150 | public static ECPublicKeyParameters buildECPublicKeyByPrivateKey(ECPrivateKeyParameters priKey) { 151 | ECDomainParameters domainParameters = priKey.getParameters(); 152 | ECPoint q = new FixedPointCombMultiplier().multiply(domainParameters.getG(), priKey.getD()); 153 | return new ECPublicKeyParameters(q, domainParameters); 154 | } 155 | 156 | /** 157 | * @param x 大数形式的公钥x分量 158 | * @param y 大数形式的公钥y分量 159 | * @param curve EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#CURVE} 160 | * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS} 161 | * @return 162 | */ 163 | public static ECPublicKeyParameters createECPublicKeyParameters( 164 | BigInteger x, BigInteger y, ECCurve curve, ECDomainParameters domainParameters) { 165 | return createECPublicKeyParameters(x.toByteArray(), y.toByteArray(), curve, domainParameters); 166 | } 167 | 168 | /** 169 | * @param xHex 十六进制形式的公钥x分量,如果是SM2算法,Hex字符串长度应该是64(即32字节) 170 | * @param yHex 十六进制形式的公钥y分量,如果是SM2算法,Hex字符串长度应该是64(即32字节) 171 | * @param curve EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#CURVE} 172 | * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS} 173 | * @return 174 | */ 175 | public static ECPublicKeyParameters createECPublicKeyParameters( 176 | String xHex, String yHex, ECCurve curve, ECDomainParameters domainParameters) { 177 | return createECPublicKeyParameters(ByteUtils.fromHexString(xHex), ByteUtils.fromHexString(yHex), 178 | curve, domainParameters); 179 | } 180 | 181 | /** 182 | * @param xBytes 十六进制形式的公钥x分量,如果是SM2算法,应该是32字节 183 | * @param yBytes 十六进制形式的公钥y分量,如果是SM2算法,应该是32字节 184 | * @param curve EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#CURVE} 185 | * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS} 186 | * @return 187 | */ 188 | public static ECPublicKeyParameters createECPublicKeyParameters( 189 | byte[] xBytes, byte[] yBytes, ECCurve curve, ECDomainParameters domainParameters) { 190 | final byte uncompressedFlag = 0x04; 191 | int curveLength = getCurveLength(domainParameters); 192 | xBytes = fixToCurveLengthBytes(curveLength, xBytes); 193 | yBytes = fixToCurveLengthBytes(curveLength, yBytes); 194 | byte[] encodedPubKey = new byte[1 + xBytes.length + yBytes.length]; 195 | encodedPubKey[0] = uncompressedFlag; 196 | System.arraycopy(xBytes, 0, encodedPubKey, 1, xBytes.length); 197 | System.arraycopy(yBytes, 0, encodedPubKey, 1 + xBytes.length, yBytes.length); 198 | return new ECPublicKeyParameters(curve.decodePoint(encodedPubKey), domainParameters); 199 | } 200 | 201 | public static ECPrivateKeyParameters convertPrivateKeyToParameters(BCECPrivateKey ecPriKey) { 202 | ECParameterSpec parameterSpec = ecPriKey.getParameters(); 203 | ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(), 204 | parameterSpec.getN(), parameterSpec.getH()); 205 | return new ECPrivateKeyParameters(ecPriKey.getD(), domainParameters); 206 | } 207 | 208 | public static ECPublicKeyParameters convertPublicKeyToParameters(BCECPublicKey ecPubKey) { 209 | ECParameterSpec parameterSpec = ecPubKey.getParameters(); 210 | ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(), 211 | parameterSpec.getN(), parameterSpec.getH()); 212 | return new ECPublicKeyParameters(ecPubKey.getQ(), domainParameters); 213 | } 214 | 215 | public static BCECPublicKey createPublicKeyFromSubjectPublicKeyInfo(SubjectPublicKeyInfo subPubInfo) 216 | throws NoSuchProviderException, 217 | NoSuchAlgorithmException, InvalidKeySpecException, IOException { 218 | return BCECUtil.convertX509ToECPublicKey(subPubInfo.toASN1Primitive().getEncoded(ASN1Encoding.DER)); 219 | } 220 | 221 | /** 222 | * 将ECC私钥转换为PKCS8标准的字节流 223 | * 224 | * @param priKey 225 | * @param pubKey 可以为空,但是如果为空的话得到的结果OpenSSL可能解析不了 226 | * @return 227 | */ 228 | public static byte[] convertECPrivateKeyToPKCS8( 229 | ECPrivateKeyParameters priKey, ECPublicKeyParameters pubKey) { 230 | ECDomainParameters domainParams = priKey.getParameters(); 231 | ECParameterSpec spec = new ECParameterSpec(domainParams.getCurve(), domainParams.getG(), 232 | domainParams.getN(), domainParams.getH()); 233 | BCECPublicKey publicKey = null; 234 | if (pubKey != null) { 235 | publicKey = new BCECPublicKey(ALGO_NAME_EC, pubKey, spec, 236 | BouncyCastleProvider.CONFIGURATION); 237 | } 238 | BCECPrivateKey privateKey = new BCECPrivateKey(ALGO_NAME_EC, priKey, publicKey, 239 | spec, BouncyCastleProvider.CONFIGURATION); 240 | return privateKey.getEncoded(); 241 | } 242 | 243 | /** 244 | * 将PKCS8标准的私钥字节流转换为私钥对象 245 | * 246 | * @param pkcs8Key 247 | * @return 248 | * @throws NoSuchAlgorithmException 249 | * @throws NoSuchProviderException 250 | * @throws InvalidKeySpecException 251 | */ 252 | public static BCECPrivateKey convertPKCS8ToECPrivateKey(byte[] pkcs8Key) 253 | throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException { 254 | PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(pkcs8Key); 255 | KeyFactory kf = KeyFactory.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME); 256 | return (BCECPrivateKey) kf.generatePrivate(peks); 257 | } 258 | 259 | /** 260 | * 将PKCS8标准的私钥字节流转换为PEM 261 | * 262 | * @param encodedKey 263 | * @return 264 | * @throws IOException 265 | */ 266 | public static String convertECPrivateKeyPKCS8ToPEM(byte[] encodedKey) throws IOException { 267 | return convertEncodedDataToPEM(PEM_STRING_ECPRIVATEKEY, encodedKey); 268 | } 269 | 270 | /** 271 | * 将PEM格式的私钥转换为PKCS8标准字节流 272 | * 273 | * @param pemString 274 | * @return 275 | * @throws IOException 276 | */ 277 | public static byte[] convertECPrivateKeyPEMToPKCS8(String pemString) throws IOException { 278 | return convertPEMToEncodedData(pemString); 279 | } 280 | 281 | /** 282 | * 将ECC私钥转换为SEC1标准的字节流 283 | * openssl d2i_ECPrivateKey函数要求的DER编码的私钥也是SEC1标准的, 284 | * 这个工具函数的主要目的就是为了能生成一个openssl可以直接“识别”的ECC私钥. 285 | * 相对RSA私钥的PKCS1标准,ECC私钥的标准为SEC1 286 | * 287 | * @param priKey 288 | * @param pubKey 289 | * @return 290 | * @throws IOException 291 | */ 292 | public static byte[] convertECPrivateKeyToSEC1( 293 | ECPrivateKeyParameters priKey, ECPublicKeyParameters pubKey) throws IOException { 294 | byte[] pkcs8Bytes = convertECPrivateKeyToPKCS8(priKey, pubKey); 295 | PrivateKeyInfo pki = PrivateKeyInfo.getInstance(pkcs8Bytes); 296 | ASN1Encodable encodable = pki.parsePrivateKey(); 297 | ASN1Primitive primitive = encodable.toASN1Primitive(); 298 | byte[] sec1Bytes = primitive.getEncoded(); 299 | return sec1Bytes; 300 | } 301 | 302 | /** 303 | * 将SEC1标准的私钥字节流恢复为PKCS8标准的字节流 304 | * 305 | * @param sec1Key 306 | * @return 307 | * @throws IOException 308 | */ 309 | public static byte[] convertECPrivateKeySEC1ToPKCS8(byte[] sec1Key) throws IOException { 310 | /** 311 | * 参考org.bouncycastle.asn1.pkcs.PrivateKeyInfo和 312 | * org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey,逆向拼装 313 | */ 314 | X962Parameters params = getDomainParametersFromName(SM2Util.JDK_EC_SPEC, false); 315 | ASN1OctetString privKey = new DEROctetString(sec1Key); 316 | ASN1EncodableVector v = new ASN1EncodableVector(); 317 | v.add(new ASN1Integer(0)); //版本号 318 | v.add(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params)); //算法标识 319 | v.add(privKey); 320 | DERSequence ds = new DERSequence(v); 321 | return ds.getEncoded(ASN1Encoding.DER); 322 | } 323 | 324 | /** 325 | * 将SEC1标准的私钥字节流转为BCECPrivateKey对象 326 | * 327 | * @param sec1Key 328 | * @return 329 | * @throws NoSuchAlgorithmException 330 | * @throws NoSuchProviderException 331 | * @throws InvalidKeySpecException 332 | * @throws IOException 333 | */ 334 | public static BCECPrivateKey convertSEC1ToBCECPrivateKey(byte[] sec1Key) 335 | throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, IOException { 336 | PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(convertECPrivateKeySEC1ToPKCS8(sec1Key)); 337 | KeyFactory kf = KeyFactory.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME); 338 | return (BCECPrivateKey) kf.generatePrivate(peks); 339 | } 340 | 341 | /** 342 | * 将SEC1标准的私钥字节流转为ECPrivateKeyParameters对象 343 | * openssl i2d_ECPrivateKey函数生成的DER编码的ecc私钥是:SEC1标准的、带有EC_GROUP、带有公钥的, 344 | * 这个工具函数的主要目的就是为了使Java程序能够“识别”openssl生成的ECC私钥 345 | * 346 | * @param sec1Key 347 | * @return 348 | * @throws NoSuchAlgorithmException 349 | * @throws NoSuchProviderException 350 | * @throws InvalidKeySpecException 351 | */ 352 | public static ECPrivateKeyParameters convertSEC1ToECPrivateKey(byte[] sec1Key) 353 | throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, IOException { 354 | BCECPrivateKey privateKey = convertSEC1ToBCECPrivateKey(sec1Key); 355 | return convertPrivateKeyToParameters(privateKey); 356 | } 357 | 358 | /** 359 | * 将ECC公钥对象转换为X509标准的字节流 360 | * 361 | * @param pubKey 362 | * @return 363 | */ 364 | public static byte[] convertECPublicKeyToX509(ECPublicKeyParameters pubKey) { 365 | ECDomainParameters domainParams = pubKey.getParameters(); 366 | ECParameterSpec spec = new ECParameterSpec(domainParams.getCurve(), domainParams.getG(), 367 | domainParams.getN(), domainParams.getH()); 368 | BCECPublicKey publicKey = new BCECPublicKey(ALGO_NAME_EC, pubKey, spec, 369 | BouncyCastleProvider.CONFIGURATION); 370 | return publicKey.getEncoded(); 371 | } 372 | 373 | /** 374 | * 将X509标准的公钥字节流转为公钥对象 375 | * 376 | * @param x509Bytes 377 | * @return 378 | * @throws NoSuchProviderException 379 | * @throws NoSuchAlgorithmException 380 | * @throws InvalidKeySpecException 381 | */ 382 | public static BCECPublicKey convertX509ToECPublicKey(byte[] x509Bytes) throws NoSuchProviderException, 383 | NoSuchAlgorithmException, InvalidKeySpecException { 384 | X509EncodedKeySpec eks = new X509EncodedKeySpec(x509Bytes); 385 | KeyFactory kf = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME); 386 | return (BCECPublicKey) kf.generatePublic(eks); 387 | } 388 | 389 | /** 390 | * 将X509标准的公钥字节流转为PEM 391 | * 392 | * @param encodedKey 393 | * @return 394 | * @throws IOException 395 | */ 396 | public static String convertECPublicKeyX509ToPEM(byte[] encodedKey) throws IOException { 397 | return convertEncodedDataToPEM(PEM_STRING_PUBLIC, encodedKey); 398 | } 399 | 400 | /** 401 | * 将PEM格式的公钥转为X509标准的字节流 402 | * 403 | * @param pemString 404 | * @return 405 | * @throws IOException 406 | */ 407 | public static byte[] convertECPublicKeyPEMToX509(String pemString) throws IOException { 408 | return convertPEMToEncodedData(pemString); 409 | } 410 | 411 | /** 412 | * copy from BC 413 | * 414 | * @param genSpec 415 | * @return 416 | */ 417 | public static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec) { 418 | return getDomainParametersFromName(genSpec.getName()); 419 | } 420 | 421 | /** 422 | * copy from BC 423 | * 424 | * @param curveName 425 | * @return 426 | */ 427 | public static X9ECParameters getDomainParametersFromName(String curveName) { 428 | X9ECParameters domainParameters; 429 | try { 430 | if (curveName.charAt(0) >= '0' && curveName.charAt(0) <= '2') { 431 | ASN1ObjectIdentifier oidID = new ASN1ObjectIdentifier(curveName); 432 | domainParameters = ECUtil.getNamedCurveByOid(oidID); 433 | } else { 434 | if (curveName.indexOf(' ') > 0) { 435 | curveName = curveName.substring(curveName.indexOf(' ') + 1); 436 | domainParameters = ECUtil.getNamedCurveByName(curveName); 437 | } else { 438 | domainParameters = ECUtil.getNamedCurveByName(curveName); 439 | } 440 | } 441 | } catch (IllegalArgumentException ex) { 442 | domainParameters = ECUtil.getNamedCurveByName(curveName); 443 | } 444 | return domainParameters; 445 | } 446 | 447 | /** 448 | * copy from BC 449 | * 450 | * @param ecSpec 451 | * @param withCompression 452 | * @return 453 | */ 454 | public static X962Parameters getDomainParametersFromName( 455 | java.security.spec.ECParameterSpec ecSpec, boolean withCompression) { 456 | X962Parameters params; 457 | 458 | if (ecSpec instanceof ECNamedCurveSpec) { 459 | ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec) ecSpec).getName()); 460 | if (curveOid == null) { 461 | curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec) ecSpec).getName()); 462 | } 463 | params = new X962Parameters(curveOid); 464 | } else if (ecSpec == null) { 465 | params = new X962Parameters(DERNull.INSTANCE); 466 | } else { 467 | ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); 468 | 469 | X9ECParameters ecP = new X9ECParameters( 470 | curve, 471 | new X9ECPoint(EC5Util.convertPoint(curve, ecSpec.getGenerator()), withCompression), 472 | ecSpec.getOrder(), 473 | BigInteger.valueOf(ecSpec.getCofactor()), 474 | ecSpec.getCurve().getSeed()); 475 | 476 | //// 如果是1.62或更低版本的bcprov-jdk15on应该使用以下这段代码,因为高版本的EC5Util.convertPoint没有向下兼容 477 | /* 478 | X9ECParameters ecP = new X9ECParameters( 479 | curve, 480 | EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), 481 | ecSpec.getOrder(), 482 | BigInteger.valueOf(ecSpec.getCofactor()), 483 | ecSpec.getCurve().getSeed()); 484 | */ 485 | 486 | params = new X962Parameters(ecP); 487 | } 488 | 489 | return params; 490 | } 491 | 492 | private static String convertEncodedDataToPEM(String type, byte[] encodedData) throws IOException { 493 | ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 494 | PemWriter pWrt = new PemWriter(new OutputStreamWriter(bOut)); 495 | try { 496 | PemObject pemObj = new PemObject(type, encodedData); 497 | pWrt.writeObject(pemObj); 498 | } finally { 499 | pWrt.close(); 500 | } 501 | return new String(bOut.toByteArray()); 502 | } 503 | 504 | private static byte[] convertPEMToEncodedData(String pemString) throws IOException { 505 | ByteArrayInputStream bIn = new ByteArrayInputStream(pemString.getBytes()); 506 | PemReader pRdr = new PemReader(new InputStreamReader(bIn)); 507 | try { 508 | PemObject pemObject = pRdr.readPemObject(); 509 | return pemObject.getContent(); 510 | } finally { 511 | pRdr.close(); 512 | } 513 | } 514 | } 515 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/GMBaseUtil.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper; 2 | 3 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 4 | 5 | import java.security.Security; 6 | 7 | public class GMBaseUtil { 8 | static { 9 | Security.addProvider(new BouncyCastleProvider()); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/SM2Cipher.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper; 2 | 3 | public class SM2Cipher { 4 | /** 5 | * ECC密钥 6 | */ 7 | private byte[] c1; 8 | 9 | /** 10 | * 真正的密文 11 | */ 12 | private byte[] c2; 13 | 14 | /** 15 | * 对(c1+c2)的SM3-HASH值 16 | */ 17 | private byte[] c3; 18 | 19 | /** 20 | * SM2标准的密文,即(c1+c2+c3) 21 | */ 22 | private byte[] cipherText; 23 | 24 | public byte[] getC1() { 25 | return c1; 26 | } 27 | 28 | public void setC1(byte[] c1) { 29 | this.c1 = c1; 30 | } 31 | 32 | public byte[] getC2() { 33 | return c2; 34 | } 35 | 36 | public void setC2(byte[] c2) { 37 | this.c2 = c2; 38 | } 39 | 40 | public byte[] getC3() { 41 | return c3; 42 | } 43 | 44 | public void setC3(byte[] c3) { 45 | this.c3 = c3; 46 | } 47 | 48 | public byte[] getCipherText() { 49 | return cipherText; 50 | } 51 | 52 | public void setCipherText(byte[] cipherText) { 53 | this.cipherText = cipherText; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/SM2KeyExchangeUtil.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper; 2 | 3 | import org.bouncycastle.crypto.agreement.SM2KeyExchange; 4 | import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 5 | import org.bouncycastle.crypto.params.ECPublicKeyParameters; 6 | import org.bouncycastle.crypto.params.ParametersWithID; 7 | import org.bouncycastle.crypto.params.SM2KeyExchangePrivateParameters; 8 | import org.bouncycastle.crypto.params.SM2KeyExchangePublicParameters; 9 | 10 | import java.util.Arrays; 11 | 12 | public class SM2KeyExchangeUtil { 13 | /** 14 | * @param initiator true表示发起方,false表示响应方 15 | * @param keyBits 生成的密钥长度 16 | * @param selfStaticPriv 己方固定私钥 17 | * @param selfEphemeralPriv 己方临时私钥 18 | * @param selfId 己方ID 19 | * @param otherStaticPub 对方固定公钥 20 | * @param otherEphemeralPub 对方临时公钥 21 | * @param otherId 对方ID 22 | * @return 返回协商出的密钥,但是这个密钥是没有经过确认的 23 | */ 24 | public static byte[] calculateKey(boolean initiator, int keyBits, 25 | ECPrivateKeyParameters selfStaticPriv, ECPrivateKeyParameters selfEphemeralPriv, byte[] selfId, 26 | ECPublicKeyParameters otherStaticPub, ECPublicKeyParameters otherEphemeralPub, byte[] otherId) { 27 | SM2KeyExchange exch = new SM2KeyExchange(); 28 | exch.init(new ParametersWithID( 29 | new SM2KeyExchangePrivateParameters(initiator, selfStaticPriv, selfEphemeralPriv), 30 | selfId)); 31 | return exch.calculateKey( 32 | keyBits, 33 | new ParametersWithID(new SM2KeyExchangePublicParameters(otherStaticPub, otherEphemeralPub), otherId)); 34 | } 35 | 36 | /** 37 | * @param initiator true表示发起方,false表示响应方 38 | * @param keyBits 生成的密钥长度 39 | * @param confirmationTag 确认信息,如果是响应方可以为null;如果是发起方则应为响应方的s1 40 | * @param selfStaticPriv 己方固定私钥 41 | * @param selfEphemeralPriv 己方临时私钥 42 | * @param selfId 己方ID 43 | * @param otherStaticPub 对方固定公钥 44 | * @param otherEphemeralPub 对方临时公钥 45 | * @param otherId 对方ID 46 | * @return 47 | */ 48 | public static ExchangeResult calculateKeyWithConfirmation(boolean initiator, int keyBits, byte[] confirmationTag, 49 | ECPrivateKeyParameters selfStaticPriv, ECPrivateKeyParameters selfEphemeralPriv, byte[] selfId, 50 | ECPublicKeyParameters otherStaticPub, ECPublicKeyParameters otherEphemeralPub, byte[] otherId) { 51 | SM2KeyExchange exch = new SM2KeyExchange(); 52 | exch.init(new ParametersWithID( 53 | new SM2KeyExchangePrivateParameters(initiator, selfStaticPriv, selfEphemeralPriv), 54 | selfId)); 55 | byte[][] result = exch.calculateKeyWithConfirmation( 56 | keyBits, 57 | confirmationTag, 58 | new ParametersWithID(new SM2KeyExchangePublicParameters(otherStaticPub, otherEphemeralPub), otherId)); 59 | ExchangeResult confirmResult = new ExchangeResult(); 60 | confirmResult.setKey(result[0]); 61 | if (initiator) { 62 | confirmResult.setS2(result[1]); 63 | } else { 64 | confirmResult.setS1(result[1]); 65 | confirmResult.setS2(result[2]); 66 | } 67 | return confirmResult; 68 | } 69 | 70 | /** 71 | * @param s2 72 | * @param confirmationTag 实际上是发起方的s2 73 | * @return 74 | */ 75 | public static boolean responderConfirm(byte[] s2, byte[] confirmationTag) { 76 | return Arrays.equals(s2, confirmationTag); 77 | } 78 | 79 | public static class ExchangeResult { 80 | private byte[] key; 81 | 82 | /** 83 | * 发起方没有s1 84 | */ 85 | private byte[] s1; 86 | 87 | private byte[] s2; 88 | 89 | public byte[] getKey() { 90 | return key; 91 | } 92 | 93 | public void setKey(byte[] key) { 94 | this.key = key; 95 | } 96 | 97 | public byte[] getS1() { 98 | return s1; 99 | } 100 | 101 | public void setS1(byte[] s1) { 102 | this.s1 = s1; 103 | } 104 | 105 | public byte[] getS2() { 106 | return s2; 107 | } 108 | 109 | public void setS2(byte[] s2) { 110 | this.s2 = s2; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/SM2PreprocessSigner.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper; 2 | 3 | import org.bouncycastle.asn1.ASN1EncodableVector; 4 | import org.bouncycastle.asn1.ASN1Encoding; 5 | import org.bouncycastle.asn1.ASN1Integer; 6 | import org.bouncycastle.asn1.ASN1Primitive; 7 | import org.bouncycastle.asn1.ASN1Sequence; 8 | import org.bouncycastle.asn1.DERSequence; 9 | import org.bouncycastle.crypto.CipherParameters; 10 | import org.bouncycastle.crypto.CryptoException; 11 | import org.bouncycastle.crypto.CryptoServicesRegistrar; 12 | import org.bouncycastle.crypto.Digest; 13 | import org.bouncycastle.crypto.digests.SM3Digest; 14 | import org.bouncycastle.crypto.params.ECDomainParameters; 15 | import org.bouncycastle.crypto.params.ECKeyParameters; 16 | import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 17 | import org.bouncycastle.crypto.params.ECPublicKeyParameters; 18 | import org.bouncycastle.crypto.params.ParametersWithID; 19 | import org.bouncycastle.crypto.params.ParametersWithRandom; 20 | import org.bouncycastle.crypto.signers.DSAKCalculator; 21 | import org.bouncycastle.crypto.signers.RandomDSAKCalculator; 22 | import org.bouncycastle.math.ec.ECAlgorithms; 23 | import org.bouncycastle.math.ec.ECConstants; 24 | import org.bouncycastle.math.ec.ECFieldElement; 25 | import org.bouncycastle.math.ec.ECMultiplier; 26 | import org.bouncycastle.math.ec.ECPoint; 27 | import org.bouncycastle.math.ec.FixedPointCombMultiplier; 28 | import org.bouncycastle.util.Arrays; 29 | import org.bouncycastle.util.encoders.Hex; 30 | 31 | import java.io.IOException; 32 | import java.math.BigInteger; 33 | 34 | /** 35 | * 有的国密需求是用户可以自己做预处理,签名验签只是对预处理的结果进行签名和验签 36 | */ 37 | public class SM2PreprocessSigner implements ECConstants { 38 | private static final int DIGEST_LENGTH = 32; // bytes 39 | 40 | private final DSAKCalculator kCalculator = new RandomDSAKCalculator(); 41 | private Digest digest = null; 42 | 43 | private ECDomainParameters ecParams; 44 | private ECPoint pubPoint; 45 | private ECKeyParameters ecKey; 46 | private byte[] userID; 47 | 48 | /** 49 | * 初始化 50 | * 51 | * @param forSigning true表示用于签名,false表示用于验签 52 | * @param param 53 | */ 54 | public void init(boolean forSigning, CipherParameters param) { 55 | init(forSigning, new SM3Digest(), param); 56 | } 57 | 58 | /** 59 | * 初始化 60 | * 61 | * @param forSigning true表示用于签名,false表示用于验签 62 | * @param digest SM2算法的话,一般是采用SM3摘要算法 63 | * @param param 64 | * @throws RuntimeException 65 | */ 66 | public void init(boolean forSigning, Digest digest, CipherParameters param) throws RuntimeException { 67 | CipherParameters baseParam; 68 | 69 | if (digest.getDigestSize() != DIGEST_LENGTH) { 70 | throw new RuntimeException("Digest size must be " + DIGEST_LENGTH); 71 | } 72 | this.digest = digest; 73 | 74 | if (param instanceof ParametersWithID) { 75 | baseParam = ((ParametersWithID) param).getParameters(); 76 | userID = ((ParametersWithID) param).getID(); 77 | } else { 78 | baseParam = param; 79 | userID = Hex.decode("31323334353637383132333435363738"); // the default value 80 | } 81 | 82 | if (forSigning) { 83 | if (baseParam instanceof ParametersWithRandom) { 84 | ParametersWithRandom rParam = (ParametersWithRandom) baseParam; 85 | 86 | ecKey = (ECKeyParameters) rParam.getParameters(); 87 | ecParams = ecKey.getParameters(); 88 | kCalculator.init(ecParams.getN(), rParam.getRandom()); 89 | } else { 90 | ecKey = (ECKeyParameters) baseParam; 91 | ecParams = ecKey.getParameters(); 92 | kCalculator.init(ecParams.getN(), CryptoServicesRegistrar.getSecureRandom()); 93 | } 94 | pubPoint = createBasePointMultiplier().multiply(ecParams.getG(), ((ECPrivateKeyParameters) ecKey).getD()).normalize(); 95 | } else { 96 | ecKey = (ECKeyParameters) baseParam; 97 | ecParams = ecKey.getParameters(); 98 | pubPoint = ((ECPublicKeyParameters) ecKey).getQ(); 99 | } 100 | } 101 | 102 | /** 103 | * 预处理,辅助方法 104 | * ZA=H256(ENT LA ∥ IDA ∥ a ∥ b ∥ xG ∥yG ∥ xA ∥ yA)。 105 | * M=ZA ∥ M; 106 | * e = Hv(M) 107 | * 108 | * @return 109 | */ 110 | public byte[] preprocess(byte[] m, int off, int len) { 111 | byte[] z = getZ(userID); 112 | digest.update(z, 0, z.length); 113 | digest.update(m, off, len); 114 | byte[] eHash = new byte[DIGEST_LENGTH]; 115 | digest.doFinal(eHash, 0); 116 | return eHash; 117 | } 118 | 119 | public boolean verifySignature(byte[] eHash, byte[] signature) { 120 | try { 121 | BigInteger[] rs = derDecode(signature); 122 | if (rs != null) { 123 | return verifySignature(eHash, rs[0], rs[1]); 124 | } 125 | } catch (IOException e) { 126 | } 127 | 128 | return false; 129 | } 130 | 131 | public void reset() { 132 | digest.reset(); 133 | } 134 | 135 | public byte[] generateSignature(byte[] eHash) throws CryptoException { 136 | BigInteger n = ecParams.getN(); 137 | BigInteger e = calculateE(eHash); 138 | BigInteger d = ((ECPrivateKeyParameters) ecKey).getD(); 139 | 140 | BigInteger r, s; 141 | 142 | ECMultiplier basePointMultiplier = createBasePointMultiplier(); 143 | 144 | // 5.2.1 Draft RFC: SM2 Public Key Algorithms 145 | do // generate s 146 | { 147 | BigInteger k; 148 | do // generate r 149 | { 150 | // A3 151 | k = kCalculator.nextK(); 152 | 153 | // A4 154 | ECPoint p = basePointMultiplier.multiply(ecParams.getG(), k).normalize(); 155 | 156 | // A5 157 | r = e.add(p.getAffineXCoord().toBigInteger()).mod(n); 158 | } 159 | while (r.equals(ZERO) || r.add(k).equals(n)); 160 | 161 | // A6 162 | BigInteger dPlus1ModN = d.add(ONE).modInverse(n); 163 | 164 | s = k.subtract(r.multiply(d)).mod(n); 165 | s = dPlus1ModN.multiply(s).mod(n); 166 | } 167 | while (s.equals(ZERO)); 168 | 169 | // A7 170 | try { 171 | return derEncode(r, s); 172 | } catch (IOException ex) { 173 | throw new CryptoException("unable to encode signature: " + ex.getMessage(), ex); 174 | } 175 | } 176 | 177 | private boolean verifySignature(byte[] eHash, BigInteger r, BigInteger s) { 178 | BigInteger n = ecParams.getN(); 179 | 180 | // 5.3.1 Draft RFC: SM2 Public Key Algorithms 181 | // B1 182 | if (r.compareTo(ONE) < 0 || r.compareTo(n) >= 0) { 183 | return false; 184 | } 185 | 186 | // B2 187 | if (s.compareTo(ONE) < 0 || s.compareTo(n) >= 0) { 188 | return false; 189 | } 190 | 191 | // B3 eHash 192 | 193 | // B4 194 | BigInteger e = calculateE(eHash); 195 | 196 | // B5 197 | BigInteger t = r.add(s).mod(n); 198 | if (t.equals(ZERO)) { 199 | return false; 200 | } 201 | 202 | // B6 203 | ECPoint q = ((ECPublicKeyParameters) ecKey).getQ(); 204 | ECPoint x1y1 = ECAlgorithms.sumOfTwoMultiplies(ecParams.getG(), s, q, t).normalize(); 205 | if (x1y1.isInfinity()) { 206 | return false; 207 | } 208 | 209 | // B7 210 | BigInteger expectedR = e.add(x1y1.getAffineXCoord().toBigInteger()).mod(n); 211 | 212 | return expectedR.equals(r); 213 | } 214 | 215 | private byte[] digestDoFinal() { 216 | byte[] result = new byte[digest.getDigestSize()]; 217 | digest.doFinal(result, 0); 218 | 219 | reset(); 220 | 221 | return result; 222 | } 223 | 224 | private byte[] getZ(byte[] userID) { 225 | digest.reset(); 226 | 227 | addUserID(digest, userID); 228 | 229 | addFieldElement(digest, ecParams.getCurve().getA()); 230 | addFieldElement(digest, ecParams.getCurve().getB()); 231 | addFieldElement(digest, ecParams.getG().getAffineXCoord()); 232 | addFieldElement(digest, ecParams.getG().getAffineYCoord()); 233 | addFieldElement(digest, pubPoint.getAffineXCoord()); 234 | addFieldElement(digest, pubPoint.getAffineYCoord()); 235 | 236 | byte[] result = new byte[digest.getDigestSize()]; 237 | 238 | digest.doFinal(result, 0); 239 | 240 | return result; 241 | } 242 | 243 | private void addUserID(Digest digest, byte[] userID) { 244 | int len = userID.length * 8; 245 | digest.update((byte) (len >> 8 & 0xFF)); 246 | digest.update((byte) (len & 0xFF)); 247 | digest.update(userID, 0, userID.length); 248 | } 249 | 250 | private void addFieldElement(Digest digest, ECFieldElement v) { 251 | byte[] p = v.getEncoded(); 252 | digest.update(p, 0, p.length); 253 | } 254 | 255 | protected ECMultiplier createBasePointMultiplier() { 256 | return new FixedPointCombMultiplier(); 257 | } 258 | 259 | protected BigInteger calculateE(byte[] message) { 260 | return new BigInteger(1, message); 261 | } 262 | 263 | protected BigInteger[] derDecode(byte[] encoding) 264 | throws IOException { 265 | ASN1Sequence seq = ASN1Sequence.getInstance(ASN1Primitive.fromByteArray(encoding)); 266 | if (seq.size() != 2) { 267 | return null; 268 | } 269 | 270 | BigInteger r = ASN1Integer.getInstance(seq.getObjectAt(0)).getValue(); 271 | BigInteger s = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue(); 272 | 273 | byte[] expectedEncoding = derEncode(r, s); 274 | if (!Arrays.constantTimeAreEqual(expectedEncoding, encoding)) { 275 | return null; 276 | } 277 | 278 | return new BigInteger[]{r, s}; 279 | } 280 | 281 | protected byte[] derEncode(BigInteger r, BigInteger s) 282 | throws IOException { 283 | 284 | ASN1EncodableVector v = new ASN1EncodableVector(); 285 | v.add(new ASN1Integer(r)); 286 | v.add(new ASN1Integer(s)); 287 | return new DERSequence(v).getEncoded(ASN1Encoding.DER); 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/SM3Util.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper; 2 | 3 | import org.bouncycastle.crypto.digests.SM3Digest; 4 | import org.bouncycastle.crypto.macs.HMac; 5 | import org.bouncycastle.crypto.params.KeyParameter; 6 | 7 | import java.util.Arrays; 8 | 9 | public class SM3Util extends GMBaseUtil { 10 | 11 | /** 12 | * 计算SM3摘要值 13 | * 14 | * @param srcData 原文 15 | * @return 摘要值,对于SM3算法来说是32字节 16 | */ 17 | public static byte[] hash(byte[] srcData) { 18 | SM3Digest digest = new SM3Digest(); 19 | digest.update(srcData, 0, srcData.length); 20 | byte[] hash = new byte[digest.getDigestSize()]; 21 | digest.doFinal(hash, 0); 22 | return hash; 23 | } 24 | 25 | /** 26 | * 验证摘要 27 | * 28 | * @param srcData 原文 29 | * @param sm3Hash 摘要值 30 | * @return 返回true标识验证成功,false标识验证失败 31 | */ 32 | public static boolean verify(byte[] srcData, byte[] sm3Hash) { 33 | byte[] newHash = hash(srcData); 34 | if (Arrays.equals(newHash, sm3Hash)) { 35 | return true; 36 | } else { 37 | return false; 38 | } 39 | } 40 | 41 | /** 42 | * 计算SM3 Mac值 43 | * 44 | * @param key key值,可以是任意长度的字节数组 45 | * @param srcData 原文 46 | * @return Mac值,对于HMac-SM3来说是32字节 47 | */ 48 | public static byte[] hmac(byte[] key, byte[] srcData) { 49 | KeyParameter keyParameter = new KeyParameter(key); 50 | SM3Digest digest = new SM3Digest(); 51 | HMac mac = new HMac(digest); 52 | mac.init(keyParameter); 53 | mac.update(srcData, 0, srcData.length); 54 | byte[] result = new byte[mac.getMacSize()]; 55 | mac.doFinal(result, 0); 56 | return result; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/SM4Util.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper; 2 | 3 | import org.bouncycastle.crypto.CipherParameters; 4 | import org.bouncycastle.crypto.engines.SM4Engine; 5 | import org.bouncycastle.crypto.macs.CBCBlockCipherMac; 6 | import org.bouncycastle.crypto.macs.GMac; 7 | import org.bouncycastle.crypto.modes.GCMBlockCipher; 8 | import org.bouncycastle.crypto.paddings.BlockCipherPadding; 9 | import org.bouncycastle.crypto.paddings.PKCS7Padding; 10 | import org.bouncycastle.crypto.params.KeyParameter; 11 | import org.bouncycastle.crypto.params.ParametersWithIV; 12 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 13 | 14 | import java.security.InvalidAlgorithmParameterException; 15 | import java.security.InvalidKeyException; 16 | import java.security.Key; 17 | import java.security.NoSuchAlgorithmException; 18 | import java.security.NoSuchProviderException; 19 | import java.security.SecureRandom; 20 | import javax.crypto.BadPaddingException; 21 | import javax.crypto.Cipher; 22 | import javax.crypto.IllegalBlockSizeException; 23 | import javax.crypto.KeyGenerator; 24 | import javax.crypto.Mac; 25 | import javax.crypto.NoSuchPaddingException; 26 | import javax.crypto.spec.IvParameterSpec; 27 | import javax.crypto.spec.SecretKeySpec; 28 | 29 | public class SM4Util extends GMBaseUtil { 30 | public static final String ALGORITHM_NAME = "SM4"; 31 | public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding"; 32 | public static final String ALGORITHM_NAME_ECB_NOPADDING = "SM4/ECB/NoPadding"; 33 | public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding"; 34 | public static final String ALGORITHM_NAME_CBC_NOPADDING = "SM4/CBC/NoPadding"; 35 | 36 | /** 37 | * SM4算法目前只支持128位(即密钥16字节) 38 | */ 39 | public static final int DEFAULT_KEY_SIZE = 128; 40 | 41 | public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException { 42 | return generateKey(DEFAULT_KEY_SIZE); 43 | } 44 | 45 | public static byte[] generateKey(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException { 46 | KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME); 47 | kg.init(keySize, new SecureRandom()); 48 | return kg.generateKey().getEncoded(); 49 | } 50 | 51 | public static byte[] encrypt_ECB_Padding(byte[] key, byte[] data) 52 | throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, 53 | NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { 54 | Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key); 55 | return cipher.doFinal(data); 56 | } 57 | 58 | public static byte[] decrypt_ECB_Padding(byte[] key, byte[] cipherText) 59 | throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, 60 | NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException { 61 | Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key); 62 | return cipher.doFinal(cipherText); 63 | } 64 | 65 | public static byte[] encrypt_ECB_NoPadding(byte[] key, byte[] data) 66 | throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, 67 | NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { 68 | Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.ENCRYPT_MODE, key); 69 | return cipher.doFinal(data); 70 | } 71 | 72 | public static byte[] decrypt_ECB_NoPadding(byte[] key, byte[] cipherText) 73 | throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, 74 | NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException { 75 | Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.DECRYPT_MODE, key); 76 | return cipher.doFinal(cipherText); 77 | } 78 | 79 | public static byte[] encrypt_CBC_Padding(byte[] key, byte[] iv, byte[] data) 80 | throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, 81 | NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, 82 | InvalidAlgorithmParameterException { 83 | Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key, iv); 84 | return cipher.doFinal(data); 85 | } 86 | 87 | public static byte[] decrypt_CBC_Padding(byte[] key, byte[] iv, byte[] cipherText) 88 | throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, 89 | NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, 90 | InvalidAlgorithmParameterException { 91 | Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key, iv); 92 | return cipher.doFinal(cipherText); 93 | } 94 | 95 | public static byte[] encrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] data) 96 | throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, 97 | NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, 98 | InvalidAlgorithmParameterException { 99 | Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.ENCRYPT_MODE, key, iv); 100 | return cipher.doFinal(data); 101 | } 102 | 103 | public static byte[] decrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] cipherText) 104 | throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, 105 | NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, 106 | InvalidAlgorithmParameterException { 107 | Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.DECRYPT_MODE, key, iv); 108 | return cipher.doFinal(cipherText); 109 | } 110 | 111 | public static byte[] doCMac(byte[] key, byte[] data) throws NoSuchProviderException, NoSuchAlgorithmException, 112 | InvalidKeyException { 113 | Key keyObj = new SecretKeySpec(key, ALGORITHM_NAME); 114 | return doMac("SM4-CMAC", keyObj, data); 115 | } 116 | 117 | public static byte[] doGMac(byte[] key, byte[] iv, int tagLength, byte[] data) { 118 | org.bouncycastle.crypto.Mac mac = new GMac(new GCMBlockCipher(new SM4Engine()), tagLength * 8); 119 | return doMac(mac, key, iv, data); 120 | } 121 | 122 | /** 123 | * 默认使用PKCS7Padding/PKCS5Padding填充的CBCMAC 124 | * 125 | * @param key 126 | * @param iv 127 | * @param data 128 | * @return 129 | */ 130 | public static byte[] doCBCMac(byte[] key, byte[] iv, byte[] data) { 131 | SM4Engine engine = new SM4Engine(); 132 | org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, new PKCS7Padding()); 133 | return doMac(mac, key, iv, data); 134 | } 135 | 136 | /** 137 | * @param key 138 | * @param iv 139 | * @param padding 可以传null,传null表示NoPadding,由调用方保证数据必须是BlockSize的整数倍 140 | * @param data 141 | * @return 142 | * @throws Exception 143 | */ 144 | public static byte[] doCBCMac(byte[] key, byte[] iv, BlockCipherPadding padding, byte[] data) throws Exception { 145 | SM4Engine engine = new SM4Engine(); 146 | if (padding == null) { 147 | if (data.length % engine.getBlockSize() != 0) { 148 | throw new Exception("if no padding, data length must be multiple of SM4 BlockSize"); 149 | } 150 | } 151 | org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, padding); 152 | return doMac(mac, key, iv, data); 153 | } 154 | 155 | 156 | private static byte[] doMac(org.bouncycastle.crypto.Mac mac, byte[] key, byte[] iv, byte[] data) { 157 | CipherParameters cipherParameters = new KeyParameter(key); 158 | mac.init(new ParametersWithIV(cipherParameters, iv)); 159 | mac.update(data, 0, data.length); 160 | byte[] result = new byte[mac.getMacSize()]; 161 | mac.doFinal(result, 0); 162 | return result; 163 | } 164 | 165 | private static byte[] doMac(String algorithmName, Key key, byte[] data) throws NoSuchProviderException, 166 | NoSuchAlgorithmException, InvalidKeyException { 167 | Mac mac = Mac.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); 168 | mac.init(key); 169 | mac.update(data); 170 | return mac.doFinal(); 171 | } 172 | 173 | private static Cipher generateECBCipher(String algorithmName, int mode, byte[] key) 174 | throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, 175 | InvalidKeyException { 176 | Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); 177 | Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); 178 | cipher.init(mode, sm4Key); 179 | return cipher; 180 | } 181 | 182 | private static Cipher generateCBCCipher(String algorithmName, int mode, byte[] key, byte[] iv) 183 | throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, 184 | NoSuchProviderException, NoSuchPaddingException { 185 | Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); 186 | Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); 187 | IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); 188 | cipher.init(mode, sm4Key, ivParameterSpec); 189 | return cipher; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/cert/CertSNAllocator.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert; 2 | 3 | import java.math.BigInteger; 4 | 5 | public interface CertSNAllocator { 6 | BigInteger nextSerialNumber() throws Exception; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/cert/CommonUtil.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert; 2 | 3 | import org.bouncycastle.asn1.ASN1EncodableVector; 4 | import org.bouncycastle.asn1.ASN1ObjectIdentifier; 5 | import org.bouncycastle.asn1.DERBitString; 6 | import org.bouncycastle.asn1.DERSet; 7 | import org.bouncycastle.asn1.pkcs.CertificationRequest; 8 | import org.bouncycastle.asn1.pkcs.CertificationRequestInfo; 9 | import org.bouncycastle.asn1.x500.X500Name; 10 | import org.bouncycastle.asn1.x500.X500NameBuilder; 11 | import org.bouncycastle.asn1.x500.style.BCStyle; 12 | import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 13 | import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 14 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; 15 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; 16 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 17 | import org.bouncycastle.operator.ContentSigner; 18 | import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; 19 | import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; 20 | import org.bouncycastle.operator.OperatorCreationException; 21 | import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; 22 | import org.bouncycastle.pkcs.PKCS10CertificationRequest; 23 | import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; 24 | import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; 25 | import org.zz.gmhelper.SM2Util; 26 | import org.zz.gmhelper.cert.exception.InvalidX500NameException; 27 | 28 | import java.security.PrivateKey; 29 | import java.util.Iterator; 30 | import java.util.Map; 31 | 32 | public class CommonUtil { 33 | /** 34 | * 如果不知道怎么填充names,可以查看org.bouncycastle.asn1.x500.style.BCStyle这个类, 35 | * names的key值必须是BCStyle.DefaultLookUp中存在的(可以不关心大小写) 36 | * 37 | * @param names 38 | * @return 39 | * @throws InvalidX500NameException 40 | */ 41 | public static X500Name buildX500Name(Map names) throws InvalidX500NameException { 42 | if (names == null || names.size() == 0) { 43 | throw new InvalidX500NameException("names can not be empty"); 44 | } 45 | try { 46 | X500NameBuilder builder = new X500NameBuilder(); 47 | Iterator itr = names.entrySet().iterator(); 48 | BCStyle x500NameStyle = (BCStyle) BCStyle.INSTANCE; 49 | Map.Entry entry; 50 | while (itr.hasNext()) { 51 | entry = (Map.Entry) itr.next(); 52 | ASN1ObjectIdentifier oid = x500NameStyle.attrNameToOID((String) entry.getKey()); 53 | builder.addRDN(oid, (String) entry.getValue()); 54 | } 55 | return builder.build(); 56 | } catch (Exception ex) { 57 | throw new InvalidX500NameException(ex.getMessage(), ex); 58 | } 59 | } 60 | 61 | 62 | /** 63 | * 生成CSR 64 | * 65 | * @param subject 主题信息 66 | * @param pubKey 公钥 67 | * @param priKey 私钥 68 | * @param signAlgo 签名算法 69 | * @return CSR 70 | */ 71 | public static PKCS10CertificationRequest createCSR(X500Name subject, SM2PublicKey pubKey, PrivateKey priKey, 72 | String signAlgo) throws OperatorCreationException { 73 | PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(subject, pubKey); 74 | ContentSigner signerBuilder = new JcaContentSignerBuilder(signAlgo) 75 | .setProvider(BouncyCastleProvider.PROVIDER_NAME).build(priKey); 76 | return csrBuilder.build(signerBuilder); 77 | } 78 | 79 | /** 80 | * 生成CSR 81 | * 实际业务大部部分情况私钥是在UKey中,只能调用UKey的签名接口,因此上面的方法不能使用,本方法是为了解决这个问题,从外部签名完毕,传入签名值就可以 82 | * 需要签名的对象为本方法中的 info ,取info.getEncoded()后签名 83 | * @param subject 主题信息 84 | * @param pubKey 公钥 85 | * @param signAlgo 签名算法 86 | * @param sign 签名值 对本方法中的 info ,取info.getEncoded()后签名 87 | * @return CSR 88 | */ 89 | public static PKCS10CertificationRequest createCSR(X500Name subject, SM2PublicKey pubKey, String signAlgo, byte[] sign) throws OperatorCreationException { 90 | //info 91 | SM2PublicKey sm2SubPub = new SM2PublicKey(pubKey.getAlgorithm(), pubKey); 92 | ASN1EncodableVector v = new ASN1EncodableVector(); 93 | CertificationRequestInfo info = new CertificationRequestInfo(subject, SubjectPublicKeyInfo.getInstance(sm2SubPub.getEncoded()), new DERSet(v)); 94 | AlgorithmIdentifier algorithmIdentifier = new DefaultSignatureAlgorithmIdentifierFinder().find(signAlgo); 95 | CertificationRequest certificationRequest = new CertificationRequest(info, algorithmIdentifier, new DERBitString(sign)); 96 | return new PKCS10CertificationRequest(certificationRequest); 97 | } 98 | 99 | 100 | public static AlgorithmIdentifier findSignatureAlgorithmIdentifier(String algoName) { 101 | DefaultSignatureAlgorithmIdentifierFinder sigFinder = new DefaultSignatureAlgorithmIdentifierFinder(); 102 | return sigFinder.find(algoName); 103 | } 104 | 105 | public static AlgorithmIdentifier findDigestAlgorithmIdentifier(String algoName) { 106 | DefaultDigestAlgorithmIdentifierFinder digFinder = new DefaultDigestAlgorithmIdentifierFinder(); 107 | return digFinder.find(findSignatureAlgorithmIdentifier(algoName)); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/cert/FileSNAllocator.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert; 2 | 3 | import java.io.IOException; 4 | import java.io.RandomAccessFile; 5 | import java.math.BigInteger; 6 | 7 | public class FileSNAllocator implements CertSNAllocator { 8 | private static final String SN_FILENAME = "sn.dat"; 9 | private static String snFilePath; 10 | 11 | static { 12 | ClassLoader loader = FileSNAllocator.class.getClassLoader(); 13 | snFilePath = loader.getResource(SN_FILENAME).getPath(); 14 | } 15 | 16 | public synchronized BigInteger nextSerialNumber() throws Exception { 17 | BigInteger sn = readSN(); 18 | writeSN(sn.add(BigInteger.ONE)); 19 | return sn; 20 | } 21 | 22 | private BigInteger readSN() throws IOException { 23 | RandomAccessFile raf = null; 24 | try { 25 | raf = new RandomAccessFile(snFilePath, "r"); 26 | byte[] data = new byte[(int) raf.length()]; 27 | raf.read(data); 28 | String snStr = new String(data); 29 | return new BigInteger(snStr); 30 | } finally { 31 | if (raf != null) { 32 | raf.close(); 33 | } 34 | } 35 | } 36 | 37 | private void writeSN(BigInteger sn) throws IOException { 38 | RandomAccessFile raf = null; 39 | try { 40 | raf = new RandomAccessFile(snFilePath, "rw"); 41 | raf.writeBytes(sn.toString(10)); 42 | } finally { 43 | if (raf != null) { 44 | raf.close(); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/cert/RandomSNAllocator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * This is simplified version of the RandomSerialNumberGenerator from the project 4 | * https://github.com/xipki/xipki. 5 | */ 6 | 7 | package org.zz.gmhelper.cert; 8 | 9 | import java.math.BigInteger; 10 | import java.security.SecureRandom; 11 | 12 | /** 13 | * Random serial number generator. 14 | * 15 | * This class is thread safe. 16 | * 17 | * @author Lijun Liao 18 | */ 19 | public class RandomSNAllocator implements CertSNAllocator { 20 | 21 | /** 22 | * The highest bit is always set to 1, so the effective bit length is bitLen - 1. To ensure that 23 | * at least 64 bit entropy, bitLen must be at least 65. 24 | */ 25 | private final static int MIN_SERIALNUMBER_SIZE = 65; 26 | 27 | /** 28 | * Since serial number should be positive and maximal 20 bytes, the maximal value of bitLen is 29 | * 159. 30 | */ 31 | private final static int MAX_SERIALNUMBER_SIZE = 159; 32 | 33 | private static int[] AND_MASKS = new int[] {0xFF, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F}; 34 | 35 | private static int[] OR_MASKS = new int[] {0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40}; 36 | 37 | private final SecureRandom random; 38 | 39 | private final int bitLen; 40 | 41 | /** 42 | * Constructor with the bitLen = 65. 43 | */ 44 | public RandomSNAllocator() { 45 | this(MIN_SERIALNUMBER_SIZE); 46 | } 47 | 48 | /** 49 | * Constructor with the specification of bitLen. 50 | * @param bitLen bit length of the serial number. The highest bit is always set to 1, so the 51 | * effective bit length is bitLen - 1. Valid value is [65, 159]. 52 | */ 53 | public RandomSNAllocator(int bitLen) { 54 | if (bitLen < MIN_SERIALNUMBER_SIZE || bitLen > MAX_SERIALNUMBER_SIZE) { 55 | throw new IllegalArgumentException(String.format( 56 | "%s may not be out of the range [%d, %d]: %d", 57 | "bitLen", MIN_SERIALNUMBER_SIZE, MAX_SERIALNUMBER_SIZE, bitLen)); 58 | } 59 | 60 | this.random = new SecureRandom(); 61 | this.bitLen = bitLen; 62 | } 63 | 64 | @Override 65 | public BigInteger nextSerialNumber() { 66 | final byte[] rdnBytes = new byte[(bitLen + 7) / 8]; 67 | final int ci = bitLen % 8; 68 | 69 | random.nextBytes(rdnBytes); 70 | if (ci != 0) { 71 | rdnBytes[0] = (byte) (rdnBytes[0] & AND_MASKS[ci]); 72 | } 73 | rdnBytes[0] = (byte) (rdnBytes[0] | OR_MASKS[ci]); 74 | 75 | return new BigInteger(1, rdnBytes); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/cert/SM2CertUtil.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert; 2 | 3 | import org.bouncycastle.asn1.pkcs.ContentInfo; 4 | import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 5 | import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; 6 | import org.bouncycastle.cert.X509CertificateHolder; 7 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; 8 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; 9 | import org.bouncycastle.jce.interfaces.ECPublicKey; 10 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 11 | import org.bouncycastle.jce.spec.ECParameterSpec; 12 | import org.bouncycastle.jce.spec.ECPublicKeySpec; 13 | import org.bouncycastle.math.ec.ECPoint; 14 | import org.bouncycastle.operator.InputDecryptorProvider; 15 | import org.bouncycastle.pkcs.PKCS12PfxPdu; 16 | import org.bouncycastle.pkcs.PKCS12SafeBag; 17 | import org.bouncycastle.pkcs.PKCS12SafeBagFactory; 18 | import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; 19 | import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder; 20 | import org.zz.gmhelper.BCECUtil; 21 | import org.zz.gmhelper.SM2Util; 22 | 23 | import java.io.ByteArrayInputStream; 24 | import java.io.FileInputStream; 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.security.NoSuchProviderException; 28 | import java.security.cert.CertPath; 29 | import java.security.cert.CertificateEncodingException; 30 | import java.security.cert.CertificateException; 31 | import java.security.cert.CertificateFactory; 32 | import java.security.cert.X509Certificate; 33 | import java.util.List; 34 | 35 | public class SM2CertUtil { 36 | public static BCECPublicKey getBCECPublicKey(X509Certificate sm2Cert) { 37 | ECPublicKey pubKey = (ECPublicKey) sm2Cert.getPublicKey(); 38 | ECPoint q = pubKey.getQ(); 39 | ECParameterSpec parameterSpec = new ECParameterSpec(SM2Util.CURVE, SM2Util.G_POINT, 40 | SM2Util.SM2_ECC_N, SM2Util.SM2_ECC_H); 41 | ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(q, parameterSpec); 42 | return new BCECPublicKey(pubKey.getAlgorithm(), pubKeySpec, 43 | BouncyCastleProvider.CONFIGURATION); 44 | } 45 | 46 | /** 47 | * 校验证书 48 | * 49 | * @param issuerPubKey 从颁发者CA证书中提取出来的公钥 50 | * @param cert 待校验的证书 51 | * @return 52 | */ 53 | public static boolean verifyCertificate(BCECPublicKey issuerPubKey, X509Certificate cert) { 54 | try { 55 | cert.verify(issuerPubKey, BouncyCastleProvider.PROVIDER_NAME); 56 | } catch (Exception ex) { 57 | return false; 58 | } 59 | return true; 60 | } 61 | 62 | public static X509Certificate getX509Certificate(String certFilePath) throws IOException, CertificateException, 63 | NoSuchProviderException { 64 | InputStream is = null; 65 | try { 66 | is = new FileInputStream(certFilePath); 67 | return getX509Certificate(is); 68 | } finally { 69 | if (is != null) { 70 | is.close(); 71 | } 72 | } 73 | } 74 | 75 | public static X509Certificate getX509Certificate(byte[] certBytes) throws CertificateException, 76 | NoSuchProviderException { 77 | ByteArrayInputStream bais = new ByteArrayInputStream(certBytes); 78 | return getX509Certificate(bais); 79 | } 80 | 81 | public static X509Certificate getX509Certificate(InputStream is) throws CertificateException, 82 | NoSuchProviderException { 83 | CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); 84 | return (X509Certificate) cf.generateCertificate(is); 85 | } 86 | 87 | public static CertPath getCertificateChain(String certChainPath) throws IOException, CertificateException, 88 | NoSuchProviderException { 89 | InputStream is = null; 90 | try { 91 | is = new FileInputStream(certChainPath); 92 | return getCertificateChain(is); 93 | } finally { 94 | if (is != null) { 95 | is.close(); 96 | } 97 | } 98 | } 99 | 100 | public static CertPath getCertificateChain(byte[] certChainBytes) throws CertificateException, 101 | NoSuchProviderException { 102 | ByteArrayInputStream bais = new ByteArrayInputStream(certChainBytes); 103 | return getCertificateChain(bais); 104 | } 105 | 106 | public static byte[] getCertificateChainBytes(CertPath certChain) throws CertificateEncodingException { 107 | return certChain.getEncoded("PKCS7"); 108 | } 109 | 110 | public static CertPath getCertificateChain(InputStream is) throws CertificateException, NoSuchProviderException { 111 | CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); 112 | return cf.generateCertPath(is, "PKCS7"); 113 | } 114 | 115 | public static CertPath getCertificateChain(List certs) throws CertificateException, 116 | NoSuchProviderException { 117 | CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); 118 | return cf.generateCertPath(certs); 119 | } 120 | 121 | public static X509Certificate getX509CertificateFromPfx(byte[] pfxDER, String passwd) throws Exception { 122 | InputDecryptorProvider inputDecryptorProvider = new JcePKCSPBEInputDecryptorProviderBuilder() 123 | .setProvider(BouncyCastleProvider.PROVIDER_NAME).build(passwd.toCharArray()); 124 | PKCS12PfxPdu pfx = new PKCS12PfxPdu(pfxDER); 125 | 126 | ContentInfo[] infos = pfx.getContentInfos(); 127 | if (infos.length != 2) { 128 | throw new Exception("Only support one pair ContentInfo"); 129 | } 130 | 131 | for (int i = 0; i != infos.length; i++) { 132 | if (infos[i].getContentType().equals(PKCSObjectIdentifiers.encryptedData)) { 133 | PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i], inputDecryptorProvider); 134 | PKCS12SafeBag[] bags = dataFact.getSafeBags(); 135 | X509CertificateHolder certHoler = (X509CertificateHolder) bags[0].getBagValue(); 136 | return SM2CertUtil.getX509Certificate(certHoler.getEncoded()); 137 | } 138 | } 139 | 140 | throw new Exception("Not found X509Certificate in this pfx"); 141 | } 142 | 143 | public static BCECPublicKey getPublicKeyFromPfx(byte[] pfxDER, String passwd) throws Exception { 144 | return SM2CertUtil.getBCECPublicKey(getX509CertificateFromPfx(pfxDER, passwd)); 145 | } 146 | 147 | public static BCECPrivateKey getPrivateKeyFromPfx(byte[] pfxDER, String passwd) throws Exception { 148 | InputDecryptorProvider inputDecryptorProvider = new JcePKCSPBEInputDecryptorProviderBuilder() 149 | .setProvider(BouncyCastleProvider.PROVIDER_NAME).build(passwd.toCharArray()); 150 | PKCS12PfxPdu pfx = new PKCS12PfxPdu(pfxDER); 151 | 152 | ContentInfo[] infos = pfx.getContentInfos(); 153 | if (infos.length != 2) { 154 | throw new Exception("Only support one pair ContentInfo"); 155 | } 156 | 157 | for (int i = 0; i != infos.length; i++) { 158 | if (!infos[i].getContentType().equals(PKCSObjectIdentifiers.encryptedData)) { 159 | PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i]); 160 | PKCS12SafeBag[] bags = dataFact.getSafeBags(); 161 | PKCS8EncryptedPrivateKeyInfo encInfo = (PKCS8EncryptedPrivateKeyInfo) bags[0].getBagValue(); 162 | PrivateKeyInfo info = encInfo.decryptPrivateKeyInfo(inputDecryptorProvider); 163 | BCECPrivateKey privateKey = BCECUtil.convertPKCS8ToECPrivateKey(info.getEncoded()); 164 | return privateKey; 165 | } 166 | } 167 | 168 | throw new Exception("Not found Private Key in this pfx"); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/cert/SM2PfxMaker.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert; 2 | 3 | import org.bouncycastle.asn1.DERBMPString; 4 | import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 5 | import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; 6 | import org.bouncycastle.crypto.engines.DESedeEngine; 7 | import org.bouncycastle.crypto.engines.RC2Engine; 8 | import org.bouncycastle.crypto.modes.CBCBlockCipher; 9 | import org.bouncycastle.pkcs.PKCS12PfxPdu; 10 | import org.bouncycastle.pkcs.PKCS12PfxPduBuilder; 11 | import org.bouncycastle.pkcs.PKCS12SafeBag; 12 | import org.bouncycastle.pkcs.PKCS12SafeBagBuilder; 13 | import org.bouncycastle.pkcs.PKCSException; 14 | import org.bouncycastle.pkcs.bc.BcPKCS12MacCalculatorBuilder; 15 | import org.bouncycastle.pkcs.bc.BcPKCS12PBEOutputEncryptorBuilder; 16 | import org.bouncycastle.pkcs.jcajce.JcaPKCS12SafeBagBuilder; 17 | 18 | import java.io.IOException; 19 | import java.security.NoSuchAlgorithmException; 20 | import java.security.PrivateKey; 21 | import java.security.PublicKey; 22 | import java.security.cert.X509Certificate; 23 | 24 | public class SM2PfxMaker { 25 | 26 | /** 27 | * @param privKey 用户私钥 28 | * @param pubKey 用户公钥 29 | * @param chain X509证书数组,切记这里固定了必须是3个元素的数组,且第一个必须是叶子证书、第二个为中级CA证书、第三个为根CA证书 30 | * @param passwd 口令 31 | * @return 32 | * @throws NoSuchAlgorithmException 33 | * @throws IOException 34 | * @throws PKCSException 35 | */ 36 | public PKCS12PfxPdu makePfx(PrivateKey privKey, PublicKey pubKey, X509Certificate[] chain, String passwd) 37 | throws NoSuchAlgorithmException, IOException, PKCSException { 38 | JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); 39 | 40 | PKCS12SafeBagBuilder taCertBagBuilder = new JcaPKCS12SafeBagBuilder(chain[2]); 41 | taCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, 42 | new DERBMPString("Primary Certificate")); 43 | 44 | PKCS12SafeBagBuilder caCertBagBuilder = new JcaPKCS12SafeBagBuilder(chain[1]); 45 | caCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, 46 | new DERBMPString("Intermediate Certificate")); 47 | 48 | PKCS12SafeBagBuilder eeCertBagBuilder = new JcaPKCS12SafeBagBuilder(chain[0]); 49 | eeCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, 50 | new DERBMPString("User Key")); 51 | eeCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, 52 | extUtils.createSubjectKeyIdentifier(pubKey)); 53 | 54 | char[] passwdChars = passwd.toCharArray(); 55 | PKCS12SafeBagBuilder keyBagBuilder = new JcaPKCS12SafeBagBuilder(privKey, 56 | new BcPKCS12PBEOutputEncryptorBuilder( 57 | PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC, 58 | new CBCBlockCipher(new DESedeEngine())).build(passwdChars)); 59 | keyBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, 60 | new DERBMPString("User Key")); 61 | keyBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, 62 | extUtils.createSubjectKeyIdentifier(pubKey)); 63 | 64 | PKCS12PfxPduBuilder pfxPduBuilder = new PKCS12PfxPduBuilder(); 65 | PKCS12SafeBag[] certs = new PKCS12SafeBag[3]; 66 | certs[0] = eeCertBagBuilder.build(); 67 | certs[1] = caCertBagBuilder.build(); 68 | certs[2] = taCertBagBuilder.build(); 69 | pfxPduBuilder.addEncryptedData(new BcPKCS12PBEOutputEncryptorBuilder( 70 | PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC, 71 | new CBCBlockCipher(new RC2Engine())).build(passwdChars), 72 | certs); 73 | pfxPduBuilder.addData(keyBagBuilder.build()); 74 | return pfxPduBuilder.build(new BcPKCS12MacCalculatorBuilder(), passwdChars); 75 | } 76 | 77 | /** 78 | * @param privKey 用户私钥 79 | * @param pubKey 用户公钥 80 | * @param cert X509证书 81 | * @param passwd 口令 82 | * @return 83 | * @throws NoSuchAlgorithmException 84 | * @throws IOException 85 | * @throws PKCSException 86 | */ 87 | public PKCS12PfxPdu makePfx(PrivateKey privKey, PublicKey pubKey, X509Certificate cert, String passwd) 88 | throws NoSuchAlgorithmException, IOException, PKCSException { 89 | JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); 90 | 91 | PKCS12SafeBagBuilder eeCertBagBuilder = new JcaPKCS12SafeBagBuilder(cert); 92 | eeCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, 93 | new DERBMPString("User Key")); 94 | eeCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, 95 | extUtils.createSubjectKeyIdentifier(pubKey)); 96 | 97 | char[] passwdChars = passwd.toCharArray(); 98 | PKCS12SafeBagBuilder keyBagBuilder = new JcaPKCS12SafeBagBuilder(privKey, 99 | new BcPKCS12PBEOutputEncryptorBuilder( 100 | PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC, 101 | new CBCBlockCipher(new DESedeEngine())).build(passwdChars)); 102 | keyBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, 103 | new DERBMPString("User Key")); 104 | keyBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, 105 | extUtils.createSubjectKeyIdentifier(pubKey)); 106 | 107 | PKCS12PfxPduBuilder pfxPduBuilder = new PKCS12PfxPduBuilder(); 108 | PKCS12SafeBag[] certs = new PKCS12SafeBag[1]; 109 | certs[0] = eeCertBagBuilder.build(); 110 | pfxPduBuilder.addEncryptedData(new BcPKCS12PBEOutputEncryptorBuilder( 111 | PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC, 112 | new CBCBlockCipher(new RC2Engine())).build(passwdChars), 113 | certs); 114 | pfxPduBuilder.addData(keyBagBuilder.build()); 115 | return pfxPduBuilder.build(new BcPKCS12MacCalculatorBuilder(), passwdChars); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/cert/SM2Pkcs12Maker.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert; 2 | 3 | import java.io.IOException; 4 | import java.security.KeyStore; 5 | import java.security.KeyStoreException; 6 | import java.security.NoSuchAlgorithmException; 7 | import java.security.NoSuchProviderException; 8 | import java.security.PrivateKey; 9 | import java.security.cert.CertificateException; 10 | import java.security.cert.X509Certificate; 11 | 12 | /** 13 | * @author Lijun Liao https:/github.com/xipki 14 | */ 15 | public class SM2Pkcs12Maker { 16 | 17 | /** 18 | * @param privKey 用户私钥 19 | * @param chain X509证书数组, 20 | * 第一个(index 0)为privKey对应的证书,index i+1 是index i的CA证书 21 | * @param passwd 口令 22 | * @return the PKCS#12 keystore 23 | * @throws NoSuchProviderException 24 | * @throws KeyStoreException 25 | * @throws CertificateException 26 | * @throws NoSuchAlgorithmException 27 | * @throws IOException 28 | * @throws PKCSException 29 | */ 30 | public KeyStore makePkcs12(PrivateKey privKey, X509Certificate[] chain, char[] passwd) 31 | throws KeyStoreException, NoSuchProviderException, 32 | NoSuchAlgorithmException, CertificateException, IOException { 33 | KeyStore ks = KeyStore.getInstance("PKCS12", "BC"); 34 | ks.load(null, passwd); 35 | ks.setKeyEntry("User Key", privKey, passwd, chain); 36 | return ks; 37 | } 38 | 39 | /** 40 | * @param privKey 用户私钥 41 | * @param cert X509证书 42 | * @param passwd 口令 43 | * @return the PKCS12 keystore 44 | * @throws NoSuchAlgorithmException 45 | * @throws IOException 46 | * @throws PKCSException 47 | */ 48 | public KeyStore makePkcs12(PrivateKey privKey, X509Certificate cert, char[] passwd) 49 | throws KeyStoreException, NoSuchProviderException, 50 | NoSuchAlgorithmException, CertificateException, IOException { 51 | return makePkcs12(privKey, new X509Certificate[] {cert}, passwd); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/cert/SM2PrivateKey.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert; 2 | 3 | import org.bouncycastle.asn1.ASN1Encodable; 4 | import org.bouncycastle.asn1.ASN1Encoding; 5 | import org.bouncycastle.asn1.ASN1Primitive; 6 | import org.bouncycastle.asn1.DERBitString; 7 | import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; 8 | import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 9 | import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 10 | import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; 11 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; 12 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; 13 | import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; 14 | import org.bouncycastle.jcajce.provider.config.ProviderConfiguration; 15 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 16 | 17 | import java.io.IOException; 18 | import java.security.spec.ECParameterSpec; 19 | 20 | public class SM2PrivateKey extends BCECPrivateKey { 21 | private transient DERBitString sm2PublicKey; 22 | private boolean withCompression; 23 | 24 | public SM2PrivateKey(BCECPrivateKey privateKey, BCECPublicKey publicKey) { 25 | super(privateKey.getAlgorithm(), privateKey); 26 | this.sm2PublicKey = getSM2PublicKeyDetails(new SM2PublicKey(publicKey.getAlgorithm(), publicKey)); 27 | this.withCompression = false; 28 | } 29 | 30 | @Override 31 | public void setPointFormat(String style) { 32 | withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); 33 | } 34 | 35 | /** 36 | * Return a PKCS8 representation of the key. The sequence returned 37 | * represents a full PrivateKeyInfo object. 38 | * 39 | * @return a PKCS8 representation of the key. 40 | */ 41 | @Override 42 | public byte[] getEncoded() { 43 | ECParameterSpec ecSpec = getParams(); 44 | ProviderConfiguration configuration = BouncyCastleProvider.CONFIGURATION; 45 | ASN1Encodable params = SM2PublicKey.ID_SM2_PUBKEY_PARAM; 46 | 47 | int orderBitLength; 48 | if (ecSpec == null) { 49 | orderBitLength = ECUtil.getOrderBitLength(configuration, null, this.getS()); 50 | } else { 51 | orderBitLength = ECUtil.getOrderBitLength(configuration, ecSpec.getOrder(), this.getS()); 52 | } 53 | 54 | PrivateKeyInfo info; 55 | org.bouncycastle.asn1.sec.ECPrivateKey keyStructure; 56 | 57 | if (sm2PublicKey != null) { 58 | keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), sm2PublicKey, params); 59 | } else { 60 | keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), params); 61 | } 62 | 63 | try { 64 | info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure); 65 | 66 | return info.getEncoded(ASN1Encoding.DER); 67 | } catch (IOException e) { 68 | return null; 69 | } 70 | } 71 | 72 | private DERBitString getSM2PublicKeyDetails(SM2PublicKey pub) { 73 | try { 74 | SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(pub.getEncoded())); 75 | 76 | return info.getPublicKeyData(); 77 | } catch (IOException e) { // should never happen 78 | return null; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/cert/SM2PublicKey.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert; 2 | 3 | import org.bouncycastle.asn1.ASN1ObjectIdentifier; 4 | import org.bouncycastle.asn1.ASN1OctetString; 5 | import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 6 | import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 7 | import org.bouncycastle.asn1.x9.X9ECPoint; 8 | import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; 9 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; 10 | import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil; 11 | 12 | public class SM2PublicKey extends BCECPublicKey { 13 | public static final ASN1ObjectIdentifier ID_SM2_PUBKEY_PARAM = new ASN1ObjectIdentifier("1.2.156.10197.1.301"); 14 | 15 | private boolean withCompression; 16 | 17 | public SM2PublicKey(BCECPublicKey key) { 18 | super(key.getAlgorithm(), key); 19 | this.withCompression = false; 20 | } 21 | 22 | public SM2PublicKey(String algorithm, BCECPublicKey key) { 23 | super(algorithm, key); 24 | this.withCompression = false; 25 | } 26 | 27 | @Override 28 | public byte[] getEncoded() { 29 | ASN1OctetString p = ASN1OctetString.getInstance( 30 | new X9ECPoint(getQ(), withCompression).toASN1Primitive()); 31 | 32 | // stored curve is null if ImplicitlyCa 33 | SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( 34 | new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, ID_SM2_PUBKEY_PARAM), 35 | p.getOctets()); 36 | 37 | return KeyUtil.getEncodedSubjectPublicKeyInfo(info); 38 | } 39 | 40 | @Override 41 | public void setPointFormat(String style) { 42 | withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/cert/SM2X509CertMaker.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert; 2 | 3 | import java.math.BigInteger; 4 | import java.security.KeyPair; 5 | import java.security.PrivateKey; 6 | import java.security.PublicKey; 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.BasicConstraints; 21 | import org.bouncycastle.asn1.x509.ExtendedKeyUsage; 22 | import org.bouncycastle.asn1.x509.Extension; 23 | import org.bouncycastle.asn1.x509.GeneralName; 24 | import org.bouncycastle.asn1.x509.GeneralNames; 25 | import org.bouncycastle.asn1.x509.KeyPurposeId; 26 | import org.bouncycastle.asn1.x509.KeyUsage; 27 | import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 28 | import org.bouncycastle.cert.X509v3CertificateBuilder; 29 | import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; 30 | import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; 31 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 32 | import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; 33 | import org.bouncycastle.pkcs.PKCS10CertificationRequest; 34 | 35 | public class SM2X509CertMaker { 36 | 37 | private static enum CertLevel { 38 | RootCA, 39 | SubCA, 40 | EndEntity 41 | } // class CertLevel 42 | 43 | public static final String SIGN_ALGO_SM3WITHSM2 = "SM3withSM2"; 44 | 45 | private long certExpire; 46 | private X500Name issuerDN; 47 | private CertSNAllocator snAllocator; 48 | private KeyPair issuerKeyPair; 49 | 50 | /** 51 | * @param issuerKeyPair 证书颁发者的密钥对。 52 | * 其实一般的CA的私钥都是要严格保护的。 53 | * 一般CA的私钥都会放在加密卡/加密机里,证书的签名由加密卡/加密机完成。 54 | * 这里仅是为了演示BC库签发证书的用法,所以暂时不作太多要求。 55 | * @param certExpire 证书有效时间,单位毫秒 56 | * @param issuer 证书颁发者信息 57 | * @param snAllocator 维护/分配证书序列号的实例,证书序列号应该递增且不重复 58 | */ 59 | public SM2X509CertMaker(KeyPair issuerKeyPair, long certExpire, X500Name issuer, 60 | CertSNAllocator snAllocator) { 61 | this.issuerKeyPair = issuerKeyPair; 62 | this.certExpire = certExpire; 63 | this.issuerDN = issuer; 64 | this.snAllocator = snAllocator; 65 | } 66 | 67 | /** 68 | * 生成根CA证书 69 | * 70 | * @param csr CSR 71 | * @return 新的证书 72 | * @throws Exception 如果错误发生 73 | */ 74 | public X509Certificate makeRootCACert(byte[] csr) 75 | throws Exception { 76 | KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign); 77 | return makeCertificate(CertLevel.RootCA, null, csr, usage, null); 78 | } 79 | 80 | /** 81 | * 生成SubCA证书 82 | * 83 | * @param csr CSR 84 | * @return 新的证书 85 | * @throws Exception 如果错误发生 86 | */ 87 | public X509Certificate makeSubCACert(byte[] csr) 88 | throws Exception { 89 | KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign); 90 | return makeCertificate(CertLevel.SubCA, 0, csr, usage, null); 91 | } 92 | 93 | /** 94 | * 生成SSL用户证书 95 | * 96 | * @param csr CSR 97 | * @return 新的证书 98 | * @throws Exception 如果错误发生 99 | */ 100 | public X509Certificate makeSSLEndEntityCert(byte[] csr) 101 | throws Exception { 102 | return makeEndEntityCert(csr, 103 | new KeyPurposeId[] {KeyPurposeId.id_kp_clientAuth, KeyPurposeId.id_kp_serverAuth}); 104 | } 105 | 106 | /** 107 | * 生成用户证书 108 | * 109 | * @param csr CSR 110 | * @param extendedKeyUsages 扩展指数用途。 111 | * @return 新的证书 112 | * @throws Exception 如果错误发生 113 | */ 114 | public X509Certificate makeEndEntityCert(byte[] csr, 115 | KeyPurposeId[] extendedKeyUsages) 116 | throws Exception { 117 | KeyUsage usage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyAgreement 118 | | KeyUsage.dataEncipherment | KeyUsage.keyEncipherment); 119 | return makeCertificate(CertLevel.SubCA, null, csr, usage, extendedKeyUsages); 120 | } 121 | 122 | /** 123 | * @param certLevel 证书级别 {@link CertLevel} 124 | * @param keyUsage 证书用途 125 | * @param csr CSR 126 | * @return 127 | * @throws Exception 128 | */ 129 | private X509Certificate makeCertificate(CertLevel certLevel, Integer pathLenConstrain, 130 | byte[] csr, KeyUsage keyUsage, KeyPurposeId[] extendedKeyUsages) 131 | throws Exception { 132 | if (certLevel == CertLevel.EndEntity) { 133 | if (keyUsage.hasUsages(KeyUsage.keyCertSign)) { 134 | throw new IllegalArgumentException( 135 | "keyusage keyCertSign is not allowed in EndEntity Certificate"); 136 | } 137 | } 138 | 139 | PKCS10CertificationRequest request = new PKCS10CertificationRequest(csr); 140 | SubjectPublicKeyInfo subPub = request.getSubjectPublicKeyInfo(); 141 | 142 | PrivateKey issPriv = issuerKeyPair.getPrivate(); 143 | PublicKey issPub = issuerKeyPair.getPublic(); 144 | 145 | X500Name subject = request.getSubject(); 146 | String email = null; 147 | String commonName = null; 148 | /* 149 | * RFC 5280 §4.2.1.6 Subject 150 | * Conforming implementations generating new certificates with 151 | * electronic mail addresses MUST use the rfc822Name in the subject 152 | * alternative name extension (Section 4.2.1.6) to describe such 153 | * identities. Simultaneous inclusion of the emailAddress attribute in 154 | * the subject distinguished name to support legacy implementations is 155 | * deprecated but permitted. 156 | */ 157 | RDN[] rdns = subject.getRDNs(); 158 | List newRdns = new ArrayList<>(rdns.length); 159 | for (int i = 0; i < rdns.length; i++) { 160 | RDN rdn = rdns[i]; 161 | 162 | AttributeTypeAndValue atv = rdn.getFirst(); 163 | ASN1ObjectIdentifier type = atv.getType(); 164 | if (BCStyle.EmailAddress.equals(type)) { 165 | email = IETFUtils.valueToString(atv.getValue()); 166 | } else { 167 | if (BCStyle.CN.equals(type)) { 168 | commonName = IETFUtils.valueToString(atv.getValue()); 169 | } 170 | newRdns.add(rdn); 171 | } 172 | } 173 | 174 | List subjectAltNames = new LinkedList<>(); 175 | if (email != null) { 176 | subject = new X500Name(newRdns.toArray(new RDN[0])); 177 | subjectAltNames.add( 178 | new GeneralName(GeneralName.rfc822Name, 179 | new DERIA5String(email, true))); 180 | } 181 | 182 | boolean selfSignedEECert = false; 183 | switch (certLevel) { 184 | case RootCA: 185 | if (issuerDN.equals(subject)) { 186 | subject = issuerDN; 187 | } else { 188 | throw new IllegalArgumentException("subject != issuer for certLevel " + CertLevel.RootCA); 189 | } 190 | break; 191 | case SubCA: 192 | if (issuerDN.equals(subject)) { 193 | throw new IllegalArgumentException( 194 | "subject MUST not equals issuer for certLevel " + certLevel); 195 | } 196 | break; 197 | default: 198 | if (issuerDN.equals(subject)) { 199 | selfSignedEECert = true; 200 | subject = issuerDN; 201 | } 202 | } 203 | 204 | BigInteger serialNumber = snAllocator.nextSerialNumber(); 205 | Date notBefore = new Date(); 206 | Date notAfter = new Date(notBefore.getTime() + certExpire); 207 | X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder( 208 | issuerDN, serialNumber, 209 | notBefore, notAfter, 210 | subject, subPub); 211 | 212 | JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); 213 | v3CertGen.addExtension(Extension.subjectKeyIdentifier, false, 214 | extUtils.createSubjectKeyIdentifier(subPub)); 215 | if (certLevel != CertLevel.RootCA && !selfSignedEECert) { 216 | v3CertGen.addExtension(Extension.authorityKeyIdentifier, false, 217 | extUtils.createAuthorityKeyIdentifier(SubjectPublicKeyInfo.getInstance(issPub.getEncoded()))); 218 | } 219 | 220 | // RFC 5280 §4.2.1.9 Basic Constraints: 221 | // Conforming CAs MUST include this extension in all CA certificates 222 | // that contain public keys used to validate digital signatures on 223 | // certificates and MUST mark the extension as critical in such 224 | // certificates. 225 | BasicConstraints basicConstraints; 226 | if (certLevel == CertLevel.EndEntity) { 227 | basicConstraints = new BasicConstraints(false); 228 | } else { 229 | basicConstraints = pathLenConstrain == null 230 | ? new BasicConstraints(true) : new BasicConstraints(pathLenConstrain.intValue()); 231 | } 232 | v3CertGen.addExtension(Extension.basicConstraints, true, basicConstraints); 233 | 234 | // RFC 5280 §4.2.1.3 Key Usage: When present, conforming CAs SHOULD mark this extension as critical. 235 | v3CertGen.addExtension(Extension.keyUsage, true, keyUsage); 236 | 237 | if (extendedKeyUsages != null) { 238 | ExtendedKeyUsage xku = new ExtendedKeyUsage(extendedKeyUsages); 239 | v3CertGen.addExtension(Extension.extendedKeyUsage, false, xku); 240 | 241 | boolean forSSLServer = false; 242 | for (KeyPurposeId purposeId : extendedKeyUsages) { 243 | if (KeyPurposeId.id_kp_serverAuth.equals(purposeId)) { 244 | forSSLServer = true; 245 | break; 246 | } 247 | } 248 | 249 | if (forSSLServer) { 250 | if (commonName == null) { 251 | throw new IllegalArgumentException("commonName must not be null"); 252 | } 253 | GeneralName name = new GeneralName(GeneralName.dNSName, 254 | new DERIA5String(commonName, true)); 255 | subjectAltNames.add(name); 256 | } 257 | } 258 | 259 | if (!subjectAltNames.isEmpty()) { 260 | v3CertGen.addExtension(Extension.subjectAlternativeName, false, 261 | new GeneralNames(subjectAltNames.toArray(new GeneralName[0]))); 262 | } 263 | 264 | JcaContentSignerBuilder contentSignerBuilder = makeContentSignerBuilder(issPub); 265 | X509Certificate cert = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME) 266 | .getCertificate(v3CertGen.build(contentSignerBuilder.build(issPriv))); 267 | cert.verify(issPub); 268 | 269 | return cert; 270 | } 271 | 272 | private JcaContentSignerBuilder makeContentSignerBuilder(PublicKey issPub) throws Exception { 273 | if (issPub.getAlgorithm().equals("EC")) { 274 | JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder(SIGN_ALGO_SM3WITHSM2); 275 | contentSignerBuilder.setProvider(BouncyCastleProvider.PROVIDER_NAME); 276 | return contentSignerBuilder; 277 | } 278 | throw new Exception("Unsupported PublicKey Algorithm:" + issPub.getAlgorithm()); 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/main/java/org/zz/gmhelper/cert/exception/InvalidX500NameException.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert.exception; 2 | 3 | public class InvalidX500NameException extends Exception { 4 | private static final long serialVersionUID = 3192247087539921768L; 5 | 6 | public InvalidX500NameException() { 7 | super(); 8 | } 9 | 10 | public InvalidX500NameException(String message) { 11 | super(message); 12 | } 13 | 14 | public InvalidX500NameException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | 18 | public InvalidX500NameException(Throwable cause) { 19 | super(cause); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/resources/sn.dat: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/cert/test/FileSNAllocatorTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert.test; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import org.zz.gmhelper.cert.FileSNAllocator; 6 | 7 | import java.math.BigInteger; 8 | 9 | public class FileSNAllocatorTest { 10 | 11 | @Test 12 | public void TestIncrementAndGetSN() { 13 | try { 14 | FileSNAllocator allocator = new FileSNAllocator(); 15 | BigInteger sn = allocator.nextSerialNumber(); 16 | System.out.println("sn:" + sn.toString(10)); 17 | BigInteger sn2 = allocator.nextSerialNumber(); 18 | System.out.println("sn2:" + sn2.toString(10)); 19 | if (sn2.compareTo(sn.add(BigInteger.ONE)) != 0) { 20 | Assert.fail("sn2 != (sn + 1)"); 21 | } 22 | } catch (Exception ex) { 23 | ex.printStackTrace(); 24 | Assert.fail(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/cert/test/SM2CertUtilTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert.test; 2 | 3 | import org.bouncycastle.asn1.x500.X500Name; 4 | import org.bouncycastle.asn1.x500.X500NameBuilder; 5 | import org.bouncycastle.asn1.x500.style.BCStyle; 6 | import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 7 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; 8 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; 9 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 10 | import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; 11 | import org.junit.Assert; 12 | import org.junit.Test; 13 | import org.zz.gmhelper.BCECUtil; 14 | import org.zz.gmhelper.SM2Util; 15 | import org.zz.gmhelper.cert.CertSNAllocator; 16 | import org.zz.gmhelper.cert.CommonUtil; 17 | import org.zz.gmhelper.cert.RandomSNAllocator; 18 | import org.zz.gmhelper.cert.SM2CertUtil; 19 | import org.zz.gmhelper.cert.SM2PublicKey; 20 | import org.zz.gmhelper.cert.SM2X509CertMaker; 21 | import org.zz.gmhelper.test.GMBaseTest; 22 | import org.zz.gmhelper.test.util.FileUtil; 23 | 24 | import java.security.KeyPair; 25 | import java.security.Security; 26 | import java.security.cert.X509Certificate; 27 | import java.util.Arrays; 28 | 29 | public class SM2CertUtilTest { 30 | private static final String ROOT_PRI_PATH = "target/test.root.ca.pri"; 31 | private static final String ROOT_CERT_PATH = "target/test.root.ca.cer"; 32 | private static final String MID_PRI_PATH = "target/test.mid.ca.pri"; 33 | private static final String MID_CERT_PATH = "target/test.mid.ca.cer"; 34 | private static final String USER_PRI_PATH = "target/test.user.pri"; 35 | private static final String USER_CERT_PATH = "target/test.user.cer"; 36 | 37 | static { 38 | Security.addProvider(new BouncyCastleProvider()); 39 | } 40 | 41 | @Test 42 | public void testGetBCECPublicKey() { 43 | try { 44 | //当前测试例依赖以下测试例生成的文件,所以先调用一下 45 | new SM2X509CertMakerTest().testMakeCertificate(); 46 | 47 | X509Certificate cert = SM2CertUtil.getX509Certificate("target/test.sm2.cer"); 48 | BCECPublicKey pubKey = SM2CertUtil.getBCECPublicKey(cert); 49 | byte[] priKeyData = FileUtil.readFile("target/test.sm2.pri"); 50 | ECPrivateKeyParameters priKeyParameters = BCECUtil.convertSEC1ToECPrivateKey(priKeyData); 51 | 52 | byte[] sign = SM2Util.sign(priKeyParameters, GMBaseTest.WITH_ID, GMBaseTest.SRC_DATA); 53 | System.out.println("SM2 sign with withId result:\n" + ByteUtils.toHexString(sign)); 54 | boolean flag = SM2Util.verify(pubKey, GMBaseTest.WITH_ID, GMBaseTest.SRC_DATA, sign); 55 | if (!flag) { 56 | Assert.fail("[withId] verify failed"); 57 | } 58 | 59 | sign = SM2Util.sign(priKeyParameters, GMBaseTest.SRC_DATA); 60 | System.out.println("SM2 sign without withId result:\n" + ByteUtils.toHexString(sign)); 61 | flag = SM2Util.verify(pubKey, GMBaseTest.SRC_DATA, sign); 62 | if (!flag) { 63 | Assert.fail("verify failed"); 64 | } 65 | 66 | byte[] cipherText = SM2Util.encrypt(pubKey, GMBaseTest.SRC_DATA); 67 | System.out.println("SM2 encrypt result:\n" + ByteUtils.toHexString(cipherText)); 68 | byte[] plain = SM2Util.decrypt(priKeyParameters, cipherText); 69 | System.out.println("SM2 decrypt result:\n" + ByteUtils.toHexString(plain)); 70 | if (!Arrays.equals(plain, GMBaseTest.SRC_DATA)) { 71 | Assert.fail("plain not equals the src"); 72 | } 73 | } catch (Exception ex) { 74 | ex.printStackTrace(); 75 | Assert.fail(); 76 | } 77 | } 78 | 79 | @Test 80 | public void testVerifyCertificate() { 81 | try { 82 | long certExpire = 20L * 365 * 24 * 60 * 60 * 1000; 83 | CertSNAllocator snAllocator = new RandomSNAllocator(); 84 | KeyPair rootKP = SM2Util.generateKeyPair(); 85 | X500Name rootDN = SM2X509CertMakerTest.buildRootCADN(); 86 | SM2X509CertMaker rootCertMaker = new SM2X509CertMaker(rootKP, certExpire, rootDN, snAllocator); 87 | SM2PublicKey rootPub = new SM2PublicKey(rootKP.getPublic().getAlgorithm(), 88 | (BCECPublicKey) rootKP.getPublic()); 89 | byte[] rootCSR = CommonUtil.createCSR(rootDN, rootPub, rootKP.getPrivate(), 90 | SM2X509CertMaker.SIGN_ALGO_SM3WITHSM2).getEncoded(); 91 | SM2X509CertMakerTest.savePriKey(ROOT_PRI_PATH, (BCECPrivateKey) rootKP.getPrivate(), 92 | (BCECPublicKey) rootKP.getPublic()); 93 | X509Certificate rootCACert = rootCertMaker.makeRootCACert(rootCSR); 94 | FileUtil.writeFile(ROOT_CERT_PATH, rootCACert.getEncoded()); 95 | 96 | KeyPair midKP = SM2Util.generateKeyPair(); 97 | X500Name midDN = buildMidCADN(); 98 | SM2PublicKey midPub = new SM2PublicKey(midKP.getPublic().getAlgorithm(), 99 | (BCECPublicKey) midKP.getPublic()); 100 | byte[] midCSR = CommonUtil.createCSR(midDN, midPub, midKP.getPrivate(), 101 | SM2X509CertMaker.SIGN_ALGO_SM3WITHSM2).getEncoded(); 102 | SM2X509CertMakerTest.savePriKey(MID_PRI_PATH, (BCECPrivateKey) midKP.getPrivate(), 103 | (BCECPublicKey) midKP.getPublic()); 104 | X509Certificate midCACert = rootCertMaker.makeSubCACert(midCSR); 105 | FileUtil.writeFile(MID_CERT_PATH, midCACert.getEncoded()); 106 | 107 | SM2X509CertMaker midCertMaker = new SM2X509CertMaker(midKP, certExpire, midDN, snAllocator); 108 | KeyPair userKP = SM2Util.generateKeyPair(); 109 | X500Name userDN = SM2X509CertMakerTest.buildSubjectDN(); 110 | SM2PublicKey userPub = new SM2PublicKey(userKP.getPublic().getAlgorithm(), 111 | (BCECPublicKey) userKP.getPublic()); 112 | byte[] userCSR = CommonUtil.createCSR(userDN, userPub, userKP.getPrivate(), 113 | SM2X509CertMaker.SIGN_ALGO_SM3WITHSM2).getEncoded(); 114 | SM2X509CertMakerTest.savePriKey(USER_PRI_PATH, (BCECPrivateKey) userKP.getPrivate(), 115 | (BCECPublicKey) userKP.getPublic()); 116 | X509Certificate userCert = midCertMaker.makeSSLEndEntityCert(userCSR); 117 | FileUtil.writeFile(USER_CERT_PATH, userCert.getEncoded()); 118 | 119 | //根证书是自签名,所以用自己的公钥验证自己的证书 120 | BCECPublicKey bcRootPub = SM2CertUtil.getBCECPublicKey(rootCACert); 121 | rootCACert = SM2CertUtil.getX509Certificate(ROOT_CERT_PATH); 122 | if (!SM2CertUtil.verifyCertificate(bcRootPub, rootCACert)) { 123 | Assert.fail(); 124 | } 125 | 126 | midCACert = SM2CertUtil.getX509Certificate(MID_CERT_PATH); 127 | if (!SM2CertUtil.verifyCertificate(bcRootPub, midCACert)) { 128 | Assert.fail(); 129 | } 130 | 131 | BCECPublicKey bcMidPub = SM2CertUtil.getBCECPublicKey(midCACert); 132 | userCert = SM2CertUtil.getX509Certificate(USER_CERT_PATH); 133 | if (!SM2CertUtil.verifyCertificate(bcMidPub, userCert)) { 134 | Assert.fail(); 135 | } 136 | } catch (Exception ex) { 137 | ex.printStackTrace(); 138 | } 139 | } 140 | 141 | public static X500Name buildMidCADN() { 142 | X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE); 143 | builder.addRDN(BCStyle.C, "CN"); 144 | builder.addRDN(BCStyle.O, "org.zz"); 145 | builder.addRDN(BCStyle.OU, "org.zz"); 146 | builder.addRDN(BCStyle.CN, "ZZ Intermediate CA"); 147 | return builder.build(); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/cert/test/SM2PfxMakerTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert.test; 2 | 3 | import java.security.KeyPair; 4 | import java.security.PublicKey; 5 | import java.security.Security; 6 | import java.security.cert.X509Certificate; 7 | 8 | import org.bouncycastle.asn1.ASN1Encoding; 9 | import org.bouncycastle.asn1.x500.X500Name; 10 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; 11 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; 12 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 13 | import org.bouncycastle.pkcs.PKCS10CertificationRequest; 14 | import org.bouncycastle.pkcs.PKCS12PfxPdu; 15 | import org.junit.Assert; 16 | import org.junit.Test; 17 | import org.zz.gmhelper.BCECUtil; 18 | import org.zz.gmhelper.SM2Util; 19 | import org.zz.gmhelper.cert.CommonUtil; 20 | import org.zz.gmhelper.cert.SM2CertUtil; 21 | import org.zz.gmhelper.cert.SM2PfxMaker; 22 | import org.zz.gmhelper.cert.SM2PublicKey; 23 | import org.zz.gmhelper.cert.SM2X509CertMaker; 24 | import org.zz.gmhelper.test.util.FileUtil; 25 | 26 | public class SM2PfxMakerTest { 27 | static { 28 | Security.addProvider(new BouncyCastleProvider()); 29 | } 30 | 31 | private static final String TEST_PFX_PASSWD = "12345678"; 32 | private static final String TEST_PFX_FILENAME = "target/test.pfx"; 33 | 34 | @Test 35 | public void testMakePfx() { 36 | try { 37 | KeyPair subKP = SM2Util.generateKeyPair(); 38 | X500Name subDN = SM2X509CertMakerTest.buildSubjectDN(); 39 | SM2PublicKey sm2SubPub = new SM2PublicKey(subKP.getPublic().getAlgorithm(), 40 | (BCECPublicKey) subKP.getPublic()); 41 | byte[] csr = CommonUtil.createCSR(subDN, sm2SubPub, subKP.getPrivate(), 42 | SM2X509CertMaker.SIGN_ALGO_SM3WITHSM2).getEncoded(); 43 | SM2X509CertMaker certMaker = SM2X509CertMakerTest.buildCertMaker(); 44 | X509Certificate cert = certMaker.makeSSLEndEntityCert(csr); 45 | 46 | SM2PfxMaker pfxMaker = new SM2PfxMaker(); 47 | PKCS10CertificationRequest request = new PKCS10CertificationRequest(csr); 48 | PublicKey subPub = BCECUtil.createPublicKeyFromSubjectPublicKeyInfo(request.getSubjectPublicKeyInfo()); 49 | PKCS12PfxPdu pfx = pfxMaker.makePfx(subKP.getPrivate(), subPub, cert, TEST_PFX_PASSWD); 50 | byte[] pfxDER = pfx.getEncoded(ASN1Encoding.DER); 51 | FileUtil.writeFile(TEST_PFX_FILENAME, pfxDER); 52 | } catch (Exception ex) { 53 | ex.printStackTrace(); 54 | Assert.fail(); 55 | } 56 | } 57 | 58 | @Test 59 | public void testPfxSign() { 60 | //先生成一个pfx 61 | testMakePfx(); 62 | 63 | try { 64 | byte[] pkcs12 = FileUtil.readFile(TEST_PFX_FILENAME); 65 | BCECPublicKey publicKey = SM2CertUtil.getPublicKeyFromPfx(pkcs12, TEST_PFX_PASSWD); 66 | BCECPrivateKey privateKey = SM2CertUtil.getPrivateKeyFromPfx(pkcs12, TEST_PFX_PASSWD); 67 | 68 | String srcData = "1234567890123456789012345678901234567890"; 69 | byte[] sign = SM2Util.sign(privateKey, srcData.getBytes()); 70 | boolean flag = SM2Util.verify(publicKey, srcData.getBytes(), sign); 71 | if (!flag) { 72 | Assert.fail(); 73 | } 74 | } catch (Exception ex) { 75 | ex.printStackTrace(); 76 | Assert.fail(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/cert/test/SM2Pkcs12MakerTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert.test; 2 | 3 | import java.io.InputStream; 4 | import java.io.OutputStream; 5 | import java.nio.file.Files; 6 | import java.nio.file.Paths; 7 | import java.nio.file.StandardOpenOption; 8 | import java.security.KeyPair; 9 | import java.security.KeyStore; 10 | import java.security.PrivateKey; 11 | import java.security.Security; 12 | import java.security.Signature; 13 | import java.security.cert.X509Certificate; 14 | 15 | import org.bouncycastle.asn1.x500.X500Name; 16 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; 17 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; 18 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | import org.zz.gmhelper.SM2Util; 22 | import org.zz.gmhelper.cert.CommonUtil; 23 | import org.zz.gmhelper.cert.SM2Pkcs12Maker; 24 | import org.zz.gmhelper.cert.SM2PublicKey; 25 | import org.zz.gmhelper.cert.SM2X509CertMaker; 26 | 27 | /** 28 | * @author Lijun Liao https:/github.com/xipki 29 | */ 30 | public class SM2Pkcs12MakerTest { 31 | static { 32 | Security.addProvider(new BouncyCastleProvider()); 33 | } 34 | 35 | private static final char[] TEST_P12_PASSWD = "12345678".toCharArray(); 36 | private static final String TEST_P12_FILENAME = "target/test.p12"; 37 | 38 | @Test 39 | public void testMakePkcs12() { 40 | try { 41 | KeyPair subKP = SM2Util.generateKeyPair(); 42 | X500Name subDN = SM2X509CertMakerTest.buildSubjectDN(); 43 | SM2PublicKey sm2SubPub = new SM2PublicKey(subKP.getPublic().getAlgorithm(), 44 | (BCECPublicKey) subKP.getPublic()); 45 | byte[] csr = CommonUtil.createCSR(subDN, sm2SubPub, subKP.getPrivate(), 46 | SM2X509CertMaker.SIGN_ALGO_SM3WITHSM2).getEncoded(); 47 | SM2X509CertMaker certMaker = SM2X509CertMakerTest.buildCertMaker(); 48 | X509Certificate cert = certMaker.makeSSLEndEntityCert(csr); 49 | 50 | SM2Pkcs12Maker pkcs12Maker = new SM2Pkcs12Maker(); 51 | KeyStore pkcs12 = pkcs12Maker.makePkcs12(subKP.getPrivate(), cert, TEST_P12_PASSWD); 52 | try (OutputStream os = Files.newOutputStream(Paths.get(TEST_P12_FILENAME), 53 | StandardOpenOption.CREATE, StandardOpenOption.WRITE)) { 54 | pkcs12.store(os, TEST_P12_PASSWD); 55 | } 56 | } catch (Exception ex) { 57 | ex.printStackTrace(); 58 | Assert.fail(); 59 | } 60 | } 61 | 62 | @Test 63 | public void testPkcs12Sign() { 64 | //先生成一个pkcs12 65 | testMakePkcs12(); 66 | 67 | try { 68 | KeyStore ks = KeyStore.getInstance("PKCS12", "BC"); 69 | try (InputStream is = Files.newInputStream(Paths.get(TEST_P12_FILENAME), 70 | StandardOpenOption.READ)) { 71 | ks.load(is, TEST_P12_PASSWD); 72 | } 73 | 74 | PrivateKey privateKey = (BCECPrivateKey) ks.getKey("User Key", TEST_P12_PASSWD); 75 | X509Certificate cert = (X509Certificate) ks.getCertificate("User Key"); 76 | 77 | byte[] srcData = "1234567890123456789012345678901234567890".getBytes(); 78 | 79 | // create signature 80 | Signature sign = Signature.getInstance(SM2X509CertMaker.SIGN_ALGO_SM3WITHSM2, "BC"); 81 | sign.initSign(privateKey); 82 | sign.update(srcData); 83 | byte[] signatureValue = sign.sign(); 84 | 85 | // verify signature 86 | Signature verify = Signature.getInstance(SM2X509CertMaker.SIGN_ALGO_SM3WITHSM2, "BC"); 87 | verify.initVerify(cert); 88 | verify.update(srcData); 89 | boolean sigValid = verify.verify(signatureValue); 90 | Assert.assertTrue("signature validation result", sigValid); 91 | } catch (Exception ex) { 92 | ex.printStackTrace(); 93 | Assert.fail(); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/cert/test/SM2PrivateKeyTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert.test; 2 | 3 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; 4 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; 5 | import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import org.zz.gmhelper.SM2Util; 9 | import org.zz.gmhelper.cert.SM2PrivateKey; 10 | import org.zz.gmhelper.cert.SM2PublicKey; 11 | 12 | import java.security.InvalidAlgorithmParameterException; 13 | import java.security.KeyPair; 14 | import java.security.NoSuchAlgorithmException; 15 | import java.security.NoSuchProviderException; 16 | 17 | public class SM2PrivateKeyTest { 18 | @Test 19 | public void testEncoded() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException { 20 | KeyPair keyPair = SM2Util.generateKeyPair(); 21 | BCECPrivateKey privateKey = (BCECPrivateKey) keyPair.getPrivate(); 22 | BCECPublicKey publicKey = (BCECPublicKey) keyPair.getPublic(); 23 | SM2PublicKey sm2PublicKey = new SM2PublicKey(publicKey.getAlgorithm(), publicKey); 24 | SM2PrivateKey sm2PrivateKey1 = new SM2PrivateKey(privateKey, publicKey); 25 | SM2PrivateKey sm2PrivateKey2 = new SM2PrivateKey(privateKey, sm2PublicKey); 26 | String nativePriDER = ByteUtils.toHexString(privateKey.getEncoded()); 27 | String sm2PriDER1 = ByteUtils.toHexString(sm2PrivateKey1.getEncoded()); 28 | String sm2PriDER2 = ByteUtils.toHexString(sm2PrivateKey2.getEncoded()); 29 | if (nativePriDER.equalsIgnoreCase(sm2PriDER1)) { 30 | Assert.fail(); 31 | } 32 | if (!sm2PriDER1.equalsIgnoreCase(sm2PriDER2)) { 33 | Assert.fail(); 34 | } 35 | System.out.println("Native EC Private Key DER:\n" + nativePriDER.toUpperCase()); 36 | System.out.println("SM2 EC Private Key DER:\n" + sm2PriDER1.toUpperCase()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/cert/test/SM2X509CertMakerTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.cert.test; 2 | 3 | import java.io.IOException; 4 | import java.security.InvalidAlgorithmParameterException; 5 | import java.security.KeyPair; 6 | import java.security.NoSuchAlgorithmException; 7 | import java.security.NoSuchProviderException; 8 | import java.security.Security; 9 | import java.security.cert.X509Certificate; 10 | 11 | import org.bouncycastle.asn1.x500.X500Name; 12 | import org.bouncycastle.asn1.x500.X500NameBuilder; 13 | import org.bouncycastle.asn1.x500.style.BCStyle; 14 | import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 15 | import org.bouncycastle.crypto.params.ECPublicKeyParameters; 16 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; 17 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; 18 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | import org.zz.gmhelper.BCECUtil; 22 | import org.zz.gmhelper.SM2Util; 23 | import org.zz.gmhelper.cert.CertSNAllocator; 24 | import org.zz.gmhelper.cert.CommonUtil; 25 | import org.zz.gmhelper.cert.RandomSNAllocator; 26 | import org.zz.gmhelper.cert.SM2PublicKey; 27 | import org.zz.gmhelper.cert.SM2X509CertMaker; 28 | import org.zz.gmhelper.cert.exception.InvalidX500NameException; 29 | import org.zz.gmhelper.test.util.FileUtil; 30 | 31 | public class SM2X509CertMakerTest { 32 | 33 | static { 34 | Security.addProvider(new BouncyCastleProvider()); 35 | } 36 | 37 | @Test 38 | public void testMakeCertificate() { 39 | try { 40 | KeyPair subKP = SM2Util.generateKeyPair(); 41 | X500Name subDN = buildSubjectDN(); 42 | SM2PublicKey sm2SubPub = new SM2PublicKey(subKP.getPublic().getAlgorithm(), 43 | (BCECPublicKey) subKP.getPublic()); 44 | byte[] csr = CommonUtil.createCSR(subDN, sm2SubPub, subKP.getPrivate(), 45 | SM2X509CertMaker.SIGN_ALGO_SM3WITHSM2).getEncoded(); 46 | savePriKey("target/test.sm2.pri", (BCECPrivateKey) subKP.getPrivate(), 47 | (BCECPublicKey) subKP.getPublic()); 48 | SM2X509CertMaker certMaker = buildCertMaker(); 49 | X509Certificate cert = certMaker.makeSSLEndEntityCert(csr); 50 | FileUtil.writeFile("target/test.sm2.cer", cert.getEncoded()); 51 | } catch (Exception ex) { 52 | ex.printStackTrace(); 53 | Assert.fail(); 54 | } 55 | } 56 | 57 | public static void savePriKey(String filePath, BCECPrivateKey priKey, BCECPublicKey pubKey) throws IOException { 58 | ECPrivateKeyParameters priKeyParam = BCECUtil.convertPrivateKeyToParameters(priKey); 59 | ECPublicKeyParameters pubKeyParam = BCECUtil.convertPublicKeyToParameters(pubKey); 60 | byte[] derPriKey = BCECUtil.convertECPrivateKeyToSEC1(priKeyParam, pubKeyParam); 61 | FileUtil.writeFile(filePath, derPriKey); 62 | } 63 | 64 | public static X500Name buildSubjectDN() { 65 | X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE); 66 | builder.addRDN(BCStyle.C, "CN"); 67 | builder.addRDN(BCStyle.O, "org.zz"); 68 | builder.addRDN(BCStyle.OU, "org.zz"); 69 | builder.addRDN(BCStyle.CN, "example.org"); 70 | builder.addRDN(BCStyle.EmailAddress, "abc@example.org"); 71 | return builder.build(); 72 | } 73 | 74 | public static X500Name buildRootCADN() { 75 | X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE); 76 | builder.addRDN(BCStyle.C, "CN"); 77 | builder.addRDN(BCStyle.O, "org.zz"); 78 | builder.addRDN(BCStyle.OU, "org.zz"); 79 | builder.addRDN(BCStyle.CN, "ZZ Root CA"); 80 | return builder.build(); 81 | } 82 | 83 | public static SM2X509CertMaker buildCertMaker() throws InvalidAlgorithmParameterException, 84 | NoSuchAlgorithmException, NoSuchProviderException, InvalidX500NameException { 85 | X500Name issuerName = buildRootCADN(); 86 | KeyPair issKP = SM2Util.generateKeyPair(); 87 | long certExpire = 20L * 365 * 24 * 60 * 60 * 1000; // 20年 88 | CertSNAllocator snAllocator = new RandomSNAllocator(); // 实际应用中可能需要使用数据库来保证证书序列号的唯一性。 89 | return new SM2X509CertMaker(issKP, certExpire, issuerName, snAllocator); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/test/AllTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.test; 2 | 3 | import org.junit.runner.RunWith; 4 | import org.junit.runners.Suite; 5 | import org.junit.runners.Suite.SuiteClasses; 6 | import org.zz.gmhelper.cert.test.FileSNAllocatorTest; 7 | import org.zz.gmhelper.cert.test.SM2CertUtilTest; 8 | import org.zz.gmhelper.cert.test.SM2PfxMakerTest; 9 | import org.zz.gmhelper.cert.test.SM2PrivateKeyTest; 10 | import org.zz.gmhelper.cert.test.SM2X509CertMakerTest; 11 | 12 | @RunWith(Suite.class) 13 | @SuiteClasses({BCECUtilTest.class, SM2UtilTest.class, SM3UtilTest.class, SM4UtilTest.class, 14 | SM2KeyExchangeUtilTest.class, SM2PreprocessSignerTest.class, 15 | // ------------------------------------ 16 | FileSNAllocatorTest.class, SM2CertUtilTest.class, SM2PfxMakerTest.class, SM2PrivateKeyTest.class, 17 | SM2X509CertMakerTest.class}) 18 | public class AllTest { 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/test/BCECUtilTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.test; 2 | 3 | import org.bouncycastle.crypto.AsymmetricCipherKeyPair; 4 | import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 5 | import org.bouncycastle.crypto.params.ECPublicKeyParameters; 6 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; 7 | import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | import org.zz.gmhelper.BCECUtil; 11 | import org.zz.gmhelper.SM2Util; 12 | 13 | public class BCECUtilTest { 14 | 15 | @Test 16 | public void testECPrivateKeyPKCS8() { 17 | try { 18 | AsymmetricCipherKeyPair keyPair = SM2Util.generateKeyPairParameter(); 19 | ECPrivateKeyParameters priKeyParams = (ECPrivateKeyParameters) keyPair.getPrivate(); 20 | ECPublicKeyParameters pubKeyParams = (ECPublicKeyParameters) keyPair.getPublic(); 21 | byte[] pkcs8Bytes = BCECUtil.convertECPrivateKeyToPKCS8(priKeyParams, pubKeyParams); 22 | BCECPrivateKey priKey = BCECUtil.convertPKCS8ToECPrivateKey(pkcs8Bytes); 23 | 24 | byte[] sign = SM2Util.sign(priKey, GMBaseTest.WITH_ID, GMBaseTest.SRC_DATA); 25 | System.out.println("SM2 sign with withId result:\n" + ByteUtils.toHexString(sign)); 26 | boolean flag = SM2Util.verify(pubKeyParams, GMBaseTest.WITH_ID, GMBaseTest.SRC_DATA, sign); 27 | if (!flag) { 28 | Assert.fail("[withId] verify failed"); 29 | } 30 | } catch (Exception e) { 31 | e.printStackTrace(); 32 | Assert.fail(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/test/GMBaseTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.test; 2 | 3 | public class GMBaseTest { 4 | public static final byte[] SRC_DATA = new byte[]{1, 2, 3, 4, 5, 6, 7, 8}; 5 | public static final byte[] SRC_DATA_16B = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}; 6 | public static final byte[] SRC_DATA_24B = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}; 7 | public static final byte[] SRC_DATA_32B = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}; 8 | public static final byte[] SRC_DATA_48B = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 , 1, 2, 3, 4, 5, 6, 7, 8}; 9 | public static final byte[] WITH_ID = new byte[]{1, 2, 3, 4}; 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/test/SM2KeyExchangeUtilTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.test; 2 | 3 | import org.bouncycastle.crypto.AsymmetricCipherKeyPair; 4 | import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 5 | import org.bouncycastle.crypto.params.ECPublicKeyParameters; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import org.zz.gmhelper.SM2KeyExchangeUtil; 9 | import org.zz.gmhelper.SM2Util; 10 | 11 | import java.util.Arrays; 12 | 13 | public class SM2KeyExchangeUtilTest { 14 | private static final byte[] INITIATOR_ID = "ABCDEFG1234".getBytes(); 15 | private static final byte[] RESPONDER_ID = "1234567ABCD".getBytes(); 16 | private static final int KEY_BITS = 128; 17 | 18 | @Test 19 | public void testCaculateKey() { 20 | try { 21 | AsymmetricCipherKeyPair initiatorStaticKp = SM2Util.generateKeyPairParameter(); 22 | ECPrivateKeyParameters initiatorStaticPriv = (ECPrivateKeyParameters) initiatorStaticKp.getPrivate(); 23 | ECPublicKeyParameters initiatorStaticPub = (ECPublicKeyParameters) initiatorStaticKp.getPublic(); 24 | AsymmetricCipherKeyPair initiatorEphemeralKp = SM2Util.generateKeyPairParameter(); 25 | ECPrivateKeyParameters initiatorEphemeralPriv = (ECPrivateKeyParameters) initiatorEphemeralKp.getPrivate(); 26 | ECPublicKeyParameters initiatorSEphemeralPub = (ECPublicKeyParameters) initiatorEphemeralKp.getPublic(); 27 | AsymmetricCipherKeyPair responderStaticKp = SM2Util.generateKeyPairParameter(); 28 | ECPrivateKeyParameters responderStaticPriv = (ECPrivateKeyParameters) responderStaticKp.getPrivate(); 29 | ECPublicKeyParameters responderStaticPub = (ECPublicKeyParameters) responderStaticKp.getPublic(); 30 | AsymmetricCipherKeyPair responderEphemeralKp = SM2Util.generateKeyPairParameter(); 31 | ECPrivateKeyParameters responderEphemeralPriv = (ECPrivateKeyParameters) responderEphemeralKp.getPrivate(); 32 | ECPublicKeyParameters responderSEphemeralPub = (ECPublicKeyParameters) responderEphemeralKp.getPublic(); 33 | 34 | //实际应用中应该是通过网络交换临时公钥 35 | byte[] k1 = SM2KeyExchangeUtil.calculateKey(true, KEY_BITS, 36 | initiatorStaticPriv, initiatorEphemeralPriv, INITIATOR_ID, 37 | responderStaticPub, responderSEphemeralPub, RESPONDER_ID); 38 | byte[] k2 = SM2KeyExchangeUtil.calculateKey(false, KEY_BITS, 39 | responderStaticPriv, responderEphemeralPriv, RESPONDER_ID, 40 | initiatorStaticPub, initiatorSEphemeralPub, INITIATOR_ID); 41 | 42 | if (!Arrays.equals(k1, k2)) { 43 | Assert.fail(); 44 | } 45 | } catch (Exception ex) { 46 | Assert.fail(); 47 | } 48 | } 49 | 50 | @Test 51 | public void testCalculateKeyWithConfirmation() { 52 | try { 53 | AsymmetricCipherKeyPair initiatorStaticKp = SM2Util.generateKeyPairParameter(); 54 | ECPrivateKeyParameters initiatorStaticPriv = (ECPrivateKeyParameters) initiatorStaticKp.getPrivate(); 55 | ECPublicKeyParameters initiatorStaticPub = (ECPublicKeyParameters) initiatorStaticKp.getPublic(); 56 | AsymmetricCipherKeyPair initiatorEphemeralKp = SM2Util.generateKeyPairParameter(); 57 | ECPrivateKeyParameters initiatorEphemeralPriv = (ECPrivateKeyParameters) initiatorEphemeralKp.getPrivate(); 58 | ECPublicKeyParameters initiatorSEphemeralPub = (ECPublicKeyParameters) initiatorEphemeralKp.getPublic(); 59 | AsymmetricCipherKeyPair responderStaticKp = SM2Util.generateKeyPairParameter(); 60 | ECPrivateKeyParameters responderStaticPriv = (ECPrivateKeyParameters) responderStaticKp.getPrivate(); 61 | ECPublicKeyParameters responderStaticPub = (ECPublicKeyParameters) responderStaticKp.getPublic(); 62 | AsymmetricCipherKeyPair responderEphemeralKp = SM2Util.generateKeyPairParameter(); 63 | ECPrivateKeyParameters responderEphemeralPriv = (ECPrivateKeyParameters) responderEphemeralKp.getPrivate(); 64 | ECPublicKeyParameters responderSEphemeralPub = (ECPublicKeyParameters) responderEphemeralKp.getPublic(); 65 | 66 | //第一步应该是交换临时公钥等信息 67 | 68 | //第二步响应方生成密钥和验证信息 69 | SM2KeyExchangeUtil.ExchangeResult responderResult = SM2KeyExchangeUtil.calculateKeyWithConfirmation( 70 | false, KEY_BITS, null, 71 | responderStaticPriv, responderEphemeralPriv, RESPONDER_ID, 72 | initiatorStaticPub, initiatorSEphemeralPub, INITIATOR_ID); 73 | 74 | //第三步发起方生成密钥和验证消息,并验证响应方的验证消息 75 | SM2KeyExchangeUtil.ExchangeResult initiatorResult = SM2KeyExchangeUtil.calculateKeyWithConfirmation( 76 | true, KEY_BITS, responderResult.getS1(), 77 | initiatorStaticPriv, initiatorEphemeralPriv, INITIATOR_ID, 78 | responderStaticPub, responderSEphemeralPub, RESPONDER_ID); 79 | 80 | //第四步响应方验证发起方的验证消息 81 | if (!SM2KeyExchangeUtil.responderConfirm(responderResult.getS2(), initiatorResult.getS2())) { 82 | Assert.fail(); 83 | } 84 | } catch (Exception ex) { 85 | Assert.fail(); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/test/SM2PreprocessSignerTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.test; 2 | 3 | import org.bouncycastle.crypto.AsymmetricCipherKeyPair; 4 | import org.bouncycastle.crypto.CipherParameters; 5 | import org.bouncycastle.crypto.CryptoException; 6 | import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 7 | import org.bouncycastle.crypto.params.ECPublicKeyParameters; 8 | import org.bouncycastle.crypto.params.ParametersWithID; 9 | import org.bouncycastle.crypto.params.ParametersWithRandom; 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | import org.zz.gmhelper.SM2PreprocessSigner; 13 | import org.zz.gmhelper.SM2Util; 14 | 15 | import java.security.SecureRandom; 16 | import java.util.Arrays; 17 | 18 | public class SM2PreprocessSignerTest extends GMBaseTest { 19 | 20 | @Test 21 | public void test() throws CryptoException { 22 | AsymmetricCipherKeyPair keyPair = SM2Util.generateKeyPairParameter(); 23 | ECPrivateKeyParameters priKey = (ECPrivateKeyParameters) keyPair.getPrivate(); 24 | ECPublicKeyParameters pubKey = (ECPublicKeyParameters) keyPair.getPublic(); 25 | 26 | SM2PreprocessSigner signer = new SM2PreprocessSigner(); 27 | CipherParameters pwr = new ParametersWithRandom(priKey, new SecureRandom()); 28 | signer.init(true, pwr); 29 | byte[] eHash1 = signer.preprocess(SRC_DATA, 0, SRC_DATA.length); 30 | byte[] sign1 = signer.generateSignature(eHash1); 31 | 32 | signer = new SM2PreprocessSigner(); 33 | signer.init(false, pubKey); 34 | byte[] eHash2 = signer.preprocess(SRC_DATA, 0, SRC_DATA.length); 35 | if (!Arrays.equals(eHash1, eHash2)) { 36 | Assert.fail(); 37 | } 38 | if (!signer.verifySignature(eHash1, sign1)) { 39 | Assert.fail(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/test/SM2UtilTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.test; 2 | 3 | import org.bouncycastle.crypto.AsymmetricCipherKeyPair; 4 | import org.bouncycastle.crypto.engines.SM2Engine; 5 | import org.bouncycastle.crypto.engines.SM2Engine.Mode; 6 | import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 7 | import org.bouncycastle.crypto.params.ECPublicKeyParameters; 8 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; 9 | import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; 10 | import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; 11 | import org.junit.Assert; 12 | import org.junit.Test; 13 | import org.zz.gmhelper.BCECUtil; 14 | import org.zz.gmhelper.SM2Util; 15 | import org.zz.gmhelper.test.util.FileUtil; 16 | 17 | import java.math.BigInteger; 18 | import java.security.KeyPair; 19 | import java.util.Arrays; 20 | 21 | public class SM2UtilTest extends GMBaseTest { 22 | 23 | @Test 24 | public void testSignAndVerify() { 25 | try { 26 | AsymmetricCipherKeyPair keyPair = SM2Util.generateKeyPairParameter(); 27 | ECPrivateKeyParameters priKey = (ECPrivateKeyParameters) keyPair.getPrivate(); 28 | ECPublicKeyParameters pubKey = (ECPublicKeyParameters) keyPair.getPublic(); 29 | 30 | System.out.println("Pri Hex:" 31 | + ByteUtils.toHexString(priKey.getD().toByteArray()).toUpperCase()); 32 | System.out.println("Pub X Hex:" 33 | + ByteUtils.toHexString(pubKey.getQ().getAffineXCoord().getEncoded()).toUpperCase()); 34 | System.out.println("Pub Y Hex:" 35 | + ByteUtils.toHexString(pubKey.getQ().getAffineYCoord().getEncoded()).toUpperCase()); 36 | System.out.println("Pub Point Hex:" 37 | + ByteUtils.toHexString(pubKey.getQ().getEncoded(false)).toUpperCase()); 38 | 39 | byte[] sign = SM2Util.sign(priKey, WITH_ID, SRC_DATA); 40 | System.out.println("SM2 sign with withId result:\n" + ByteUtils.toHexString(sign)); 41 | byte[] rawSign = SM2Util.decodeDERSM2Sign(sign); 42 | sign = SM2Util.encodeSM2SignToDER(rawSign); 43 | System.out.println("SM2 sign with withId result:\n" + ByteUtils.toHexString(sign)); 44 | boolean flag = SM2Util.verify(pubKey, WITH_ID, SRC_DATA, sign); 45 | if (!flag) { 46 | Assert.fail("verify failed"); 47 | } 48 | 49 | sign = SM2Util.sign(priKey, SRC_DATA); 50 | System.out.println("SM2 sign without withId result:\n" + ByteUtils.toHexString(sign)); 51 | flag = SM2Util.verify(pubKey, SRC_DATA, sign); 52 | if (!flag) { 53 | Assert.fail("verify failed"); 54 | } 55 | } catch (Exception ex) { 56 | ex.printStackTrace(); 57 | Assert.fail(); 58 | } 59 | } 60 | 61 | @Test 62 | public void testEncryptAndDecrypt() { 63 | try { 64 | AsymmetricCipherKeyPair keyPair = SM2Util.generateKeyPairParameter(); 65 | ECPrivateKeyParameters priKey = (ECPrivateKeyParameters) keyPair.getPrivate(); 66 | ECPublicKeyParameters pubKey = (ECPublicKeyParameters) keyPair.getPublic(); 67 | 68 | System.out.println("Pri Hex:" 69 | + ByteUtils.toHexString(priKey.getD().toByteArray()).toUpperCase()); 70 | System.out.println("Pub X Hex:" 71 | + ByteUtils.toHexString(pubKey.getQ().getAffineXCoord().getEncoded()).toUpperCase()); 72 | System.out.println("Pub Y Hex:" 73 | + ByteUtils.toHexString(pubKey.getQ().getAffineYCoord().getEncoded()).toUpperCase()); 74 | System.out.println("Pub Point Hex:" 75 | + ByteUtils.toHexString(pubKey.getQ().getEncoded(false)).toUpperCase()); 76 | 77 | byte[] encryptedData = SM2Util.encrypt(pubKey, SRC_DATA_24B); 78 | System.out.println("SM2 encrypt result:\n" + ByteUtils.toHexString(encryptedData)); 79 | byte[] decryptedData = SM2Util.decrypt(priKey, encryptedData); 80 | System.out.println("SM2 decrypt result:\n" + ByteUtils.toHexString(decryptedData)); 81 | if (!Arrays.equals(decryptedData, SRC_DATA_24B)) { 82 | Assert.fail(); 83 | } 84 | } catch (Exception ex) { 85 | ex.printStackTrace(); 86 | Assert.fail(); 87 | } 88 | } 89 | 90 | @Test 91 | public void testEncryptAndDecrypt_C1C2C3() { 92 | try { 93 | AsymmetricCipherKeyPair keyPair = SM2Util.generateKeyPairParameter(); 94 | ECPrivateKeyParameters priKey = (ECPrivateKeyParameters) keyPair.getPrivate(); 95 | ECPublicKeyParameters pubKey = (ECPublicKeyParameters) keyPair.getPublic(); 96 | 97 | System.out.println("Pri Hex:" 98 | + ByteUtils.toHexString(priKey.getD().toByteArray()).toUpperCase()); 99 | System.out.println("Pub X Hex:" 100 | + ByteUtils.toHexString(pubKey.getQ().getAffineXCoord().getEncoded()).toUpperCase()); 101 | System.out.println("Pub Y Hex:" 102 | + ByteUtils.toHexString(pubKey.getQ().getAffineYCoord().getEncoded()).toUpperCase()); 103 | System.out.println("Pub Point Hex:" 104 | + ByteUtils.toHexString(pubKey.getQ().getEncoded(false)).toUpperCase()); 105 | 106 | byte[] encryptedData = SM2Util.encrypt(Mode.C1C3C2, pubKey, SRC_DATA_48B); 107 | System.out.println("SM2 encrypt result:\n" + ByteUtils.toHexString(encryptedData)); 108 | byte[] decryptedData = SM2Util.decrypt(Mode.C1C3C2, priKey, encryptedData); 109 | System.out.println("SM2 decrypt result:\n" + ByteUtils.toHexString(decryptedData)); 110 | if (!Arrays.equals(decryptedData, SRC_DATA_48B)) { 111 | Assert.fail(); 112 | } 113 | } catch (Exception ex) { 114 | ex.printStackTrace(); 115 | Assert.fail(); 116 | } 117 | } 118 | 119 | @Test 120 | public void testKeyPairEncoding() { 121 | try { 122 | AsymmetricCipherKeyPair keyPair = SM2Util.generateKeyPairParameter(); 123 | ECPrivateKeyParameters priKey = (ECPrivateKeyParameters) keyPair.getPrivate(); 124 | ECPublicKeyParameters pubKey = (ECPublicKeyParameters) keyPair.getPublic(); 125 | 126 | byte[] priKeyPkcs8Der = BCECUtil.convertECPrivateKeyToPKCS8(priKey, pubKey); 127 | System.out.println("private key pkcs8 der length:" + priKeyPkcs8Der.length); 128 | System.out.println("private key pkcs8 der:" + ByteUtils.toHexString(priKeyPkcs8Der)); 129 | FileUtil.writeFile("target/ec.pkcs8.pri.der", priKeyPkcs8Der); 130 | 131 | String priKeyPkcs8Pem = BCECUtil.convertECPrivateKeyPKCS8ToPEM(priKeyPkcs8Der); 132 | FileUtil.writeFile("target/ec.pkcs8.pri.pem", priKeyPkcs8Pem.getBytes("UTF-8")); 133 | byte[] priKeyFromPem = BCECUtil.convertECPrivateKeyPEMToPKCS8(priKeyPkcs8Pem); 134 | if (!Arrays.equals(priKeyFromPem, priKeyPkcs8Der)) { 135 | throw new Exception("priKeyFromPem != priKeyPkcs8Der"); 136 | } 137 | 138 | BCECPrivateKey newPriKey = BCECUtil.convertPKCS8ToECPrivateKey(priKeyPkcs8Der); 139 | 140 | byte[] priKeyPkcs1Der = BCECUtil.convertECPrivateKeyToSEC1(priKey, pubKey); 141 | System.out.println("private key pkcs1 der length:" + priKeyPkcs1Der.length); 142 | System.out.println("private key pkcs1 der:" + ByteUtils.toHexString(priKeyPkcs1Der)); 143 | FileUtil.writeFile("target/ec.pkcs1.pri", priKeyPkcs1Der); 144 | 145 | byte[] pubKeyX509Der = BCECUtil.convertECPublicKeyToX509(pubKey); 146 | System.out.println("public key der length:" + pubKeyX509Der.length); 147 | System.out.println("public key der:" + ByteUtils.toHexString(pubKeyX509Der)); 148 | FileUtil.writeFile("target/ec.x509.pub.der", pubKeyX509Der); 149 | 150 | String pubKeyX509Pem = BCECUtil.convertECPublicKeyX509ToPEM(pubKeyX509Der); 151 | FileUtil.writeFile("target/ec.x509.pub.pem", pubKeyX509Pem.getBytes("UTF-8")); 152 | byte[] pubKeyFromPem = BCECUtil.convertECPublicKeyPEMToX509(pubKeyX509Pem); 153 | if (!Arrays.equals(pubKeyFromPem, pubKeyX509Der)) { 154 | throw new Exception("pubKeyFromPem != pubKeyX509Der"); 155 | } 156 | } catch (Exception ex) { 157 | ex.printStackTrace(); 158 | Assert.fail(); 159 | } 160 | } 161 | 162 | @Test 163 | public void testSM2KeyRecovery() { 164 | try { 165 | String priHex = "5DD701828C424B84C5D56770ECF7C4FE882E654CAC53C7CC89A66B1709068B9D"; 166 | String xHex = "FF6712D3A7FC0D1B9E01FF471A87EA87525E47C7775039D19304E554DEFE0913"; 167 | String yHex = "F632025F692776D4C13470ECA36AC85D560E794E1BCCF53D82C015988E0EB956"; 168 | String encodedPubHex = "04FF6712D3A7FC0D1B9E01FF471A87EA87525E47C7775039D19304E554DEFE0913F632025F692776D4C13470ECA36AC85D560E794E1BCCF53D82C015988E0EB956"; 169 | String signHex = "30450220213C6CD6EBD6A4D5C2D0AB38E29D441836D1457A8118D34864C247D727831962022100D9248480342AC8513CCDF0F89A2250DC8F6EB4F2471E144E9A812E0AF497F801"; 170 | byte[] signBytes = ByteUtils.fromHexString(signHex); 171 | byte[] src = ByteUtils.fromHexString("0102030405060708010203040506070801020304050607080102030405060708"); 172 | byte[] withId = ByteUtils.fromHexString("31323334353637383132333435363738"); 173 | 174 | ECPrivateKeyParameters priKey = new ECPrivateKeyParameters( 175 | new BigInteger(ByteUtils.fromHexString(priHex)), SM2Util.DOMAIN_PARAMS); 176 | ECPublicKeyParameters pubKey = BCECUtil.createECPublicKeyParameters(xHex, yHex, SM2Util.CURVE, SM2Util.DOMAIN_PARAMS); 177 | 178 | if (!SM2Util.verify(pubKey, src, signBytes)) { 179 | Assert.fail("verify failed"); 180 | } 181 | } catch (Exception ex) { 182 | ex.printStackTrace(); 183 | Assert.fail(); 184 | } 185 | } 186 | 187 | @Test 188 | public void testSM2KeyGen2() { 189 | try { 190 | AsymmetricCipherKeyPair keyPair = SM2Util.generateKeyPairParameter(); 191 | ECPrivateKeyParameters priKey = (ECPrivateKeyParameters) keyPair.getPrivate(); 192 | ECPublicKeyParameters pubKey = (ECPublicKeyParameters) keyPair.getPublic(); 193 | 194 | System.out.println("Pri Hex:" 195 | + ByteUtils.toHexString(priKey.getD().toByteArray()).toUpperCase()); 196 | System.out.println("Pub X Hex:" 197 | + ByteUtils.toHexString(pubKey.getQ().getAffineXCoord().getEncoded()).toUpperCase()); 198 | System.out.println("Pub Y Hex:" 199 | + ByteUtils.toHexString(pubKey.getQ().getAffineYCoord().getEncoded()).toUpperCase()); 200 | System.out.println("Pub Point Hex:" 201 | + ByteUtils.toHexString(pubKey.getQ().getEncoded(false)).toUpperCase()); 202 | } catch (Exception ex) { 203 | ex.printStackTrace(); 204 | Assert.fail(); 205 | } 206 | } 207 | 208 | @Test 209 | public void testEncodeSM2CipherToDER() { 210 | try { 211 | AsymmetricCipherKeyPair keyPair = SM2Util.generateKeyPairParameter(); 212 | ECPrivateKeyParameters priKey = (ECPrivateKeyParameters) keyPair.getPrivate(); 213 | ECPublicKeyParameters pubKey = (ECPublicKeyParameters) keyPair.getPublic(); 214 | 215 | byte[] encryptedData = SM2Util.encrypt(pubKey, SRC_DATA); 216 | 217 | byte[] derCipher = SM2Util.encodeSM2CipherToDER(encryptedData); 218 | FileUtil.writeFile("target/derCipher.dat", derCipher); 219 | 220 | byte[] decryptedData = SM2Util.decrypt(priKey, SM2Util.decodeDERSM2Cipher(derCipher)); 221 | if (!Arrays.equals(decryptedData, SRC_DATA)) { 222 | Assert.fail(); 223 | } 224 | 225 | Assert.assertTrue(true); 226 | } catch (Exception ex) { 227 | ex.printStackTrace(); 228 | Assert.fail(); 229 | } 230 | } 231 | 232 | @Test 233 | public void testEncodeSM2CipherToDERForLoop() { 234 | try { 235 | for (int i = 0; i < 1000; ++i) { 236 | AsymmetricCipherKeyPair keyPair = SM2Util.generateKeyPairParameter(); 237 | ECPrivateKeyParameters priKey = (ECPrivateKeyParameters) keyPair.getPrivate(); 238 | ECPublicKeyParameters pubKey = (ECPublicKeyParameters) keyPair.getPublic(); 239 | 240 | byte[] encryptedData = SM2Util.encrypt(pubKey, SRC_DATA); 241 | 242 | byte[] derCipher = SM2Util.encodeSM2CipherToDER(encryptedData); 243 | 244 | byte[] decryptedData = SM2Util.decrypt(priKey, SM2Util.decodeDERSM2Cipher(derCipher)); 245 | if (!Arrays.equals(decryptedData, SRC_DATA)) { 246 | Assert.fail(); 247 | } 248 | } 249 | Assert.assertTrue(true); 250 | } catch (Exception ex) { 251 | ex.printStackTrace(); 252 | Assert.fail(); 253 | } 254 | } 255 | 256 | @Test 257 | public void testEncodeSM2CipherToDER_C1C2C3() { 258 | try { 259 | AsymmetricCipherKeyPair keyPair = SM2Util.generateKeyPairParameter(); 260 | ECPrivateKeyParameters priKey = (ECPrivateKeyParameters) keyPair.getPrivate(); 261 | ECPublicKeyParameters pubKey = (ECPublicKeyParameters) keyPair.getPublic(); 262 | 263 | byte[] encryptedData = SM2Util.encrypt(Mode.C1C2C3, pubKey, SRC_DATA); 264 | 265 | byte[] derCipher = SM2Util.encodeSM2CipherToDER(Mode.C1C2C3, encryptedData); 266 | FileUtil.writeFile("target/derCipher_c1c2c3.dat", derCipher); 267 | 268 | byte[] decryptedData = SM2Util.decrypt(Mode.C1C2C3, priKey, SM2Util.decodeDERSM2Cipher(Mode.C1C2C3, derCipher)); 269 | if (!Arrays.equals(decryptedData, SRC_DATA)) { 270 | Assert.fail(); 271 | } 272 | 273 | Assert.assertTrue(true); 274 | } catch (Exception ex) { 275 | ex.printStackTrace(); 276 | Assert.fail(); 277 | } 278 | } 279 | 280 | @Test 281 | public void testGenerateBCECKeyPair() { 282 | try { 283 | KeyPair keyPair = SM2Util.generateKeyPair(); 284 | ECPrivateKeyParameters priKey = BCECUtil.convertPrivateKeyToParameters((BCECPrivateKey) keyPair.getPrivate()); 285 | ECPublicKeyParameters pubKey = BCECUtil.convertPublicKeyToParameters((BCECPublicKey) keyPair.getPublic()); 286 | 287 | byte[] sign = SM2Util.sign(priKey, WITH_ID, SRC_DATA); 288 | boolean flag = SM2Util.verify(pubKey, WITH_ID, SRC_DATA, sign); 289 | if (!flag) { 290 | Assert.fail("verify failed"); 291 | } 292 | 293 | sign = SM2Util.sign(priKey, SRC_DATA); 294 | flag = SM2Util.verify(pubKey, SRC_DATA, sign); 295 | if (!flag) { 296 | Assert.fail("verify failed"); 297 | } 298 | } catch (Exception ex) { 299 | ex.printStackTrace(); 300 | Assert.fail(); 301 | } 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/test/SM3UtilTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.test; 2 | 3 | import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | import org.zz.gmhelper.SM3Util; 7 | 8 | import java.util.Arrays; 9 | 10 | public class SM3UtilTest extends GMBaseTest { 11 | @Test 12 | public void testHashAndVerify() { 13 | try { 14 | byte[] hash = SM3Util.hash(SRC_DATA); 15 | System.out.println("SM3 hash result:\n" + ByteUtils.toHexString(hash)); 16 | boolean flag = SM3Util.verify(SRC_DATA, hash); 17 | if (!flag) { 18 | Assert.fail(); 19 | } 20 | } catch (Exception ex) { 21 | ex.printStackTrace(); 22 | Assert.fail(); 23 | } 24 | } 25 | 26 | @Test 27 | public void testHmacSM3() { 28 | try { 29 | byte[] hmacKey = new byte[]{1, 2, 3, 4, 5, 6, 7, 8}; 30 | byte[] hmac = SM3Util.hmac(hmacKey, SRC_DATA); 31 | System.out.println("SM3 hash result:\n" + Arrays.toString(hmac)); 32 | } catch (Exception ex) { 33 | ex.printStackTrace(); 34 | Assert.fail(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/test/SM4UtilTest.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.test; 2 | 3 | import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | import org.zz.gmhelper.SM4Util; 7 | 8 | import java.util.Arrays; 9 | 10 | public class SM4UtilTest extends GMBaseTest { 11 | 12 | @Test 13 | public void testEncryptAndDecrypt() { 14 | try { 15 | byte[] key = SM4Util.generateKey(); 16 | byte[] iv = SM4Util.generateKey(); 17 | byte[] cipherText = null; 18 | byte[] decryptedData = null; 19 | 20 | cipherText = SM4Util.encrypt_ECB_NoPadding(key, SRC_DATA_16B); 21 | System.out.println("SM4 ECB NoPadding encrypt result:\n" + Arrays.toString(cipherText)); 22 | decryptedData = SM4Util.decrypt_ECB_NoPadding(key, cipherText); 23 | System.out.println("SM4 ECB NoPadding decrypt result:\n" + Arrays.toString(decryptedData)); 24 | if (!Arrays.equals(decryptedData, SRC_DATA_16B)) { 25 | Assert.fail(); 26 | } 27 | 28 | cipherText = SM4Util.encrypt_ECB_Padding(key, SRC_DATA); 29 | System.out.println("SM4 ECB Padding encrypt result:\n" + Arrays.toString(cipherText)); 30 | decryptedData = SM4Util.decrypt_ECB_Padding(key, cipherText); 31 | System.out.println("SM4 ECB Padding decrypt result:\n" + Arrays.toString(decryptedData)); 32 | if (!Arrays.equals(decryptedData, SRC_DATA)) { 33 | Assert.fail(); 34 | } 35 | 36 | cipherText = SM4Util.encrypt_CBC_Padding(key, iv, SRC_DATA); 37 | System.out.println("SM4 CBC Padding encrypt result:\n" + Arrays.toString(cipherText)); 38 | decryptedData = SM4Util.decrypt_CBC_Padding(key, iv, cipherText); 39 | System.out.println("SM4 CBC Padding decrypt result:\n" + Arrays.toString(decryptedData)); 40 | if (!Arrays.equals(decryptedData, SRC_DATA)) { 41 | Assert.fail(); 42 | } 43 | 44 | cipherText = SM4Util.encrypt_CBC_NoPadding(key, iv, SRC_DATA_16B); 45 | System.out.println("SM4 CBC NoPadding encrypt result:\n" + Arrays.toString(cipherText)); 46 | decryptedData = SM4Util.decrypt_CBC_NoPadding(key, iv, cipherText); 47 | System.out.println("SM4 CBC NoPadding decrypt result:\n" + Arrays.toString(decryptedData)); 48 | if (!Arrays.equals(decryptedData, SRC_DATA_16B)) { 49 | Assert.fail(); 50 | } 51 | } catch (Exception ex) { 52 | ex.printStackTrace(); 53 | Assert.fail(); 54 | } 55 | } 56 | 57 | @Test 58 | public void testMac() throws Exception { 59 | byte[] key = SM4Util.generateKey(); 60 | byte[] iv = SM4Util.generateKey(); 61 | 62 | byte[] mac = SM4Util.doCMac(key, SRC_DATA_24B); 63 | System.out.println("CMAC:\n" + ByteUtils.toHexString(mac).toUpperCase()); 64 | 65 | mac = SM4Util.doGMac(key, iv, 16, SRC_DATA_24B); 66 | System.out.println("GMAC:\n" + ByteUtils.toHexString(mac).toUpperCase()); 67 | 68 | byte[] cipher = SM4Util.encrypt_CBC_NoPadding(key, iv, SRC_DATA_32B); 69 | byte[] cipherLast16 = Arrays.copyOfRange(cipher, cipher.length - 16, cipher.length); 70 | mac = SM4Util.doCBCMac(key, iv, null, SRC_DATA_32B); 71 | if (!Arrays.equals(cipherLast16, mac)) { 72 | Assert.fail(); 73 | } 74 | System.out.println("CBCMAC:\n" + ByteUtils.toHexString(mac).toUpperCase()); 75 | 76 | cipher = SM4Util.encrypt_CBC_Padding(key, iv, SRC_DATA_32B); 77 | cipherLast16 = Arrays.copyOfRange(cipher, cipher.length - 16, cipher.length); 78 | mac = SM4Util.doCBCMac(key, iv, SRC_DATA_32B); 79 | if (!Arrays.equals(cipherLast16, mac)) { 80 | Assert.fail(); 81 | } 82 | System.out.println("CBCMAC:\n" + ByteUtils.toHexString(mac).toUpperCase()); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/org/zz/gmhelper/test/util/FileUtil.java: -------------------------------------------------------------------------------- 1 | package org.zz.gmhelper.test.util; 2 | 3 | import java.io.IOException; 4 | import java.io.RandomAccessFile; 5 | 6 | public class FileUtil { 7 | public static void writeFile(String filePath, byte[] data) throws IOException { 8 | RandomAccessFile raf = null; 9 | try { 10 | raf = new RandomAccessFile(filePath, "rw"); 11 | raf.write(data); 12 | } finally { 13 | if (raf != null) { 14 | raf.close(); 15 | } 16 | } 17 | } 18 | 19 | public static byte[] readFile(String filePath) throws IOException { 20 | RandomAccessFile raf = null; 21 | byte[] data; 22 | try { 23 | raf = new RandomAccessFile(filePath, "r"); 24 | data = new byte[(int) raf.length()]; 25 | raf.read(data); 26 | return data; 27 | } finally { 28 | if (raf != null) { 29 | raf.close(); 30 | } 31 | } 32 | } 33 | } 34 | --------------------------------------------------------------------------------