├── .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 | [](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 |
--------------------------------------------------------------------------------