KeyTool生成KeyStore,证书、公钥、私钥文档JAVA生成,JAVA实现ECC签名验签
一、首先我们可以写个工具类生成密钥对、证书、公钥、私钥文本
jksAndCerGenerator.java
package com.southwind.util.u;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.Base64;
import java.util.Enumeration;
import org.springframework.core.io.ClassPathResource;
public class jksAndCerGenerator{
}
步骤:
1、生成密钥库
我们这里使用Java自带的数字证书管理工具keytool生成keystore(密钥库),
cmd 直接输入下面的命令就能生成密钥对
keytool -genkeypair -storetype PKCS12 -alias raviSSL -keyalg EC -keysize 521 -sigalg SHA256withECDSA -dname "CN=Hivesplace,OU=oAuth2.0 Authorization Server,O=123.com,L=Guangzhou,S=Guangdong,C=CN" -keystore C:\Users\zty\ideawork\springboot_jwt_001\src\main\resources\keys-and-certs/raviSSL.keystore -keypass 123456789 -storepass 123456789 -validity 36500 -v
命令详细如下:
// keytool -genkeypair 生成密钥对
// -storetype PKCS12 密钥库类型
// -alias raviSSL 生成密钥的别名的别名
// -keyalg EC 密钥算法名称 ECC
// -keysize 521 密钥位大小 521
// -sigalg SHA256withECDSA 签名算法名称 SHA256withECDSA
// -dname \"CN=Hivesplace,OU=oAuth2.0 Authorization Server,O=123.com,L=Guangzhou,S=Guangdong,C=CN\" 唯一判别名
// -keystore C:\Users\zty\ideawork\springboot_jwt_001\src\main\resources\keys-and-certs\raviSSL.keystore 密钥库名称
// -keypass 123456789 密钥口令
// -storepass 123456789 密钥库口令
// -validity 36500 有效天数
// -v" 详细输出
2、OK命令可行, 那么我们就可以通过java中·Runtime.getRuntime().exec()·
执行命令脚本
jksAndCerGenerator.java
public static void main (String[] args) throws IOException, InterruptedException, CertificateException {
buildKeyAndSaveToJksFile();
}
/**
* 生成密钥对
* */
public static void buildKeyAndSaveToJksFile() throws IOException {
// 相当于在cmd输入以下命令
// keytool -genkeypair -storetype PKCS12 -alias test -keyalg EC -groupname secp521r1 -sigalg SHA256withECDSA -dname "CN=Hivesplace,OU=oAuth2.0 Authorization Server,O=123.com,L=Guangzhou,S=Guangdong,C=CN" -keystore ./src/main/resources/keys-and-certs/raviSSL.keystore -keypass 123456789 -storepass 123456789 -validity 36500 -v
String[] command = new String[] {
//[避坑] 必须用/c(不要用/k),否则连续执行functions没效果
"cmd","/C",
"keytool -genkeypair -storetype PKCS12 -alias raviSSL -keyalg EC -keysize 521 -sigalg SHA256withECDSA -dname \"CN=Hivesplace,OU=oAuth2.0 Authorization Server,O=123.com,L=Guangzhou,S=Guangdong,C=CN\" -keystore ./src/main/resources/keys-and-certs/raviSSL.keystore -keypass 123456789 -storepass 123456789 -validity 36500 -v"
};
executeCommand(command);
}
/**
* java执行命令
* */
public static void executeCommand (String[] command) throws IOException {
Runtime.getRuntime().exec(command);
}
先把上一步cmd生成的keystore先删了,启动就会在resources/keys-and-certs下生成密钥库raviSSL.keystore存到本地
其中密钥库
里面就存着我们的密钥对
(公钥私钥)及证书
3、本地有了密钥库我们可以用java里的来获取本地的keystore
public static void main (String[] args) throws IOException, InterruptedException, CertificateException {
buildKeyAndSaveToJksFile();
try {
//从keystore提取公钥和私钥
exportKeysAndCertsFromKeyStore();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 导出证书、公钥、私钥
* */
public static void exportKeysAndCertsFromKeyStore() throws Exception {
//以 PKCS12 规格,创建 KeyStore
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//载入 jks 和该 jks 的密码 到 KeyStore 内
keyStore.load(new FileInputStream(new ClassPathResource("keys-and-certs/raviSSL.keystore").getFile()), "123456789".toCharArray());
// 要获取 key,需要提供 KeyStore 的别名 和该 KeyStore 的密码
// 获取 keyStore 内所有别名 alias
Enumeration<String> aliases = keyStore.aliases();
String alias = null;
while (aliases.hasMoreElements()) {
alias = aliases.nextElement();
System.out.println("jks文件别名是:"+ alias);
char[] keyPassword = "123456789".toCharArray();
}
}
我们可以启动一下工具 看看我们有没有将本地的keystore读取
OK java读取本地的keystore成功
4、接下来我们可以通过java导出base64编码的x.509格式的证书、公钥、私钥
(这边导出base64编码的x.509证书没使用OpenSSL,我们闲它转换麻烦)
/**
* 导出证书、公钥、私钥
* */
public static void exportKeysAndCertsFromKeyStore() throws Exception {
//以 PKCS12 规格,创建 KeyStore
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//载入 jks 和该 jks 的密码 到 KeyStore 内
keyStore.load(new FileInputStream(new ClassPathResource("keys-and-certs/raviSSL.keystore").getFile()), "123456789".toCharArray());
// 要获取 key,需要提供 KeyStore 的别名 和该 KeyStore 的密码
// 获取 keyStore 内所有别名 alias
Enumeration<String> aliases = keyStore.aliases();
String alias = null;
//文档写入格式换行+Base64
final String LINE_SEPARATOR = System.getProperty("line.separator");
final Base64.Encoder encoder = Base64.getMimeEncoder(64, LINE_SEPARATOR.getBytes());
while (aliases.hasMoreElements()) {
alias = aliases.nextElement();
System.out.println("jks文件别名是:"+ alias);
char[] keyPassword = "123456789".toCharArray();
System.out.println("jks文件中的私钥是:\nkey format: "+keyStore.getKey(alias, keyPassword).getFormat()+"\n"+"-----BEGIN PRIVATE KEY-----"+LINE_SEPARATOR+new String(encoder.encode(keyStore.getKey(alias, keyPassword).getEncoded()))+LINE_SEPARATOR+"-----END PRIVATE KEY-----"+"\n");
String keyContent = "-----BEGIN PRIVATE KEY-----"+LINE_SEPARATOR+new String(encoder.encode(keyStore.getKey(alias, keyPassword).getEncoded()))+LINE_SEPARATOR+"-----END PRIVATE KEY-----";
// 命名方法: alias.key
writeKeyOrCertToFile("./src/main/resources/keys-and-certs/"+alias+".key", keyContent);
;
Certificate certificate = keyStore.getCertificate(alias);
System.out.println("jks文件中的证书是:\ncertificate format: "+certificate.getType()+"\n"+"-----BEGIN CERTIFICATE-----"+LINE_SEPARATOR+new String(encoder.encode(certificate.getEncoded()))+LINE_SEPARATOR+"-----END CERTIFICATE-----");
String certificateContent = "-----BEGIN CERTIFICATE-----"+LINE_SEPARATOR+new String(encoder.encode(certificate.getEncoded()))+LINE_SEPARATOR+"-----END CERTIFICATE-----";
// 命名方法: alias.cer
writeKeyOrCertToFile("./src/main/resources/keys-and-certs/"+alias+".cer", certificateContent);
PublicKey publicKey = certificate.getPublicKey();
System.out.println("jks文件中的公钥是:\npublic key format: "+publicKey.getFormat()+"\n"+"-----BEGIN PUBLIC KEY-----"+LINE_SEPARATOR+new String(encoder.encode(publicKey.getEncoded()))+LINE_SEPARATOR+"-----END PUBLIC KEY-----");
String cerContent = "-----BEGIN PUBLIC KEY-----"+LINE_SEPARATOR+new String(encoder.encode(publicKey.getEncoded()))+LINE_SEPARATOR+"-----END PUBLIC KEY-----";
// 命名方法: alias.pub
writeKeyOrCertToFile("./src/main/resources/keys-and-certs/"+alias+".pub", cerContent);
}
}
/**
*创建文档流
* */
public static void writeKeyOrCertToFile(String filePathAndName, String fileContent) throws IOException {
// FileOutputStream :是字节流,它一个字节一个字节的向外边送数据 该类用来创建一个文件并向文件中写数据。
// OutputStreamWriter:是字符流,它一个字符一个字符的向外边送数据
// 因为中文是一个字符,至少占俩字节。
// 如果只用stream,读出来的中文会乱码;如果用WRITER,就不会有乱码
//
// BufferedWriter建立了一个缓冲区,
// 如果直接用stream或者writer,你的硬盘可能就是读一个字符或者一个字节 就去读写硬盘一次,IO负担巨大
// 可是用了Buffer,你的硬盘就是读了一堆数据之后,读写一下硬盘。这样对硬盘有好处
//
注意:用这三个类新建文件,如果没有文件则会重新创建文件,如果有,则仍然用之前的文件,
FileOutputStream fos = new FileOutputStream(filePathAndName);
OutputStreamWriter osw = new OutputStreamWriter(fos);
BufferedWriter bw = new BufferedWriter(osw);
bw.write(fileContent);
bw.close();
}
运行结果;
控制台:
文件夹文件:
证书:
公钥:
私钥:
二、JAVA简单实现ECC 私钥签名及公钥验签
这边简单写个接口测试签名、签名算法用ECDSA(椭圆曲线数字签名算法(Elliptic Curve Digital Signatrue Algorithm)特点:速度快,强度高,签名短)SHA256withECDSA 签名长度位256
package com.southwind.controller;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.util.Base64;
import java.util.Enumeration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestingControllerNew {
@GetMapping("/")
public void test() throws Exception {ops_request_misc=&request_id=&biz_id=102&utm_term=keyStore.load()&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-7-72830466.142^v9^pc_search_result_control_group,157^v4^new_style&spm=1018.2226.3001.4187
//以 PKCS12 规格,创建 KeyStore
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//载入 jks 和该 jks 的密码 到 KeyStore 内
keyStore.load(new FileInputStream(new ClassPathResource("keys-and-certs/jeffreychengoauth2sso.keystore").getFile()), "rOXoL5KtJdRV0GQv".toCharArray());
// 要获取 key,需要提供 KeyStore 的别名 和该 KeyStore 的密码
// 获取 keyStore 内所有别名 alias
Enumeration<String> aliases = keyStore.aliases();
String alias = null;
alias = aliases.nextElement();
System.out.println("jks文件别名是:" + alias);
char[] keyPassword = "rOXoL5KtJdRV0GQv".toCharArray();
String msg = "RaviJun.东风广场.sig";
String msgTwo = "Jeffrey.东风广场.sig";
//
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, keyPassword);
System.out.println("===============privateKey==================\n" + new String(Base64.getEncoder().encode(privateKey.getEncoded())));
Certificate certificate = keyStore.getCertificate(alias);
System.out.println("===============certificate=================\n" + new String(Base64.getEncoder().encode(certificate.getEncoded())));
PublicKey publicKey = certificate.getPublicKey();
System.out.println("==============publicKey===============\n" + new String(Base64.getEncoder().encode(publicKey.getEncoded())));
//私钥签名
byte[] signOne = sign(msg, privateKey) ;
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>签名后>>>>>>>>>>>>\n" + new String(Base64.getEncoder().encode(signOne)));
//公钥验签
boolean verifySign = verify(msg,signOne,publicKey);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>验签结果>>>>>>>>>>>>>>>>\n" + verifySign);
}
//私钥签名
public static byte[] sign(String content, PrivateKey priKey) throws Exception {
//这里可以从证书中解析出签名算法名称
//Signature signature = Signature.getInstance(getSigAlgName(pubCert));
Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initSign(priKey);
signature.update(content.getBytes());
return signature.sign();
}
//公钥验签
public static boolean verify(String content, byte[] sign, PublicKey pubKey) throws Exception {
//这里可以从证书中解析出签名算法名称
//Signature signature = Signature.getInstance(getSigAlgName(priCert));
Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initVerify(pubKey);
signature.update(content.getBytes());
return signature.verify(sign);
}
}
启动服务,用posman测试或直接访问浏览器
参考网址:https://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html#exportCertCmd