【可食用】KeyTool生成KeyStore,证书、公钥、私钥文档JAVA生成,JAVA实现ECC签名验签

KeyTool生成KeyStore,证书、公钥、私钥文档JAVA生成,JAVA实现ECC签名验签

de5d20496cd1409ebb23c62fdd33d768.png#pic_center

一、首先我们可以写个工具类生成密钥对、证书、公钥、私钥文本
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"					详细输出

f0999108639346a9b57c03c1ed0a3d15.png#pic_center

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存到本地
f2a971888b8648b187af60060830d2e8.png
其中密钥库里面就存着我们的密钥对(公钥私钥)及证书
832f25958f6547b8bfa3b7333ae3bf05.png

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读取
be68ff04907a44af8d427e1363002864.png
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();
	}

运行结果;
控制台:
ec083a1882d443dbbff00411f9d568b6.png
文件夹文件:
a13e72b42d7148d5bd74f428c93883f0.png

证书:
9d4226d554d04cadba754830b6bfe4e6.png
公钥:

8ff8d49b65154dcb9a7d0dc7dbbbd129.png
私钥:

31a9a24169a748078a0c3c45b6279ac0.png

二、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测试或直接访问浏览器
4e289fe2e57e40a3afc14ad5a92819b2.png
099f0de5cf1443179d1b90d48da101da.png
参考网址:https://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html#exportCertCmd

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值