安全密码学
在了解安全密码学之前,我们需要补充一些额外知识。
ASCII
是基于拉丁字母的一套电脑编码系统,就好像这些字符,对应的就是十进制的65 97,简单来说就是计算机没有办法识别字符,他只理解01二进制,所以用一个字符表,规定了什么字符用什么01表示。
@Test
public void ascii() {
char c1 = 'A';
char c2 = 'a';
System.out.println((byte)c1);
System.out.println((byte)c2);
//65
//97
}
@Test
public void ascii() {
char c1 = 'A';
char c2 = 'a';
// 字符 = 整型 无强转可替换
int n1 = c1;
int n2 = c2;
System.out.println("---将字符转为十进制的ASCII码--");
System.out.println(n1);
System.out.println(n2);
System.out.println("---将字符转为二进制的ASCII码--");
String binary1 = Integer.toBinaryString(n1);
String binary2 = Integer.toBinaryString(n2);
System.out.println(binary1);
System.out.println(binary2);
/**
*
* ---将字符转为十进制的ASCII码--
* 65
* 97
* ---将字符转为二进制的ASCII码--
* 1000001
* 1100001
*
*/
}
PBE
PBE(Password Based Encryption,基于口令加密)算法是一种基于口令的加密算法,其特点在于口令是由用户自己掌握的,采用随机数杂凑多重加密等方法保证数据的安全性, PBE算法没有密钥的概念,密钥在其它对称加密算法中是经过算法计算得出来的,PBE算法则是使用口令替代了密钥。
PKCS&X.509
ASN.1
在计算机界,有各种密码学标准,它们表示了如何在计算机中计算、存储、传输(等)各种算法,这些标准由 IEFT、ITU-T、ISO 等标准组织机构编写。
ASN.1(Abstract Syntax Notation One) 是 ISO 和 ITU-T 的联合标准,是描述数据的表示、编码、传输、解码的灵活的记法。它提供了一套正式、无歧义和精确的规则以描述独立于特定计算机硬件的对象结构。
ASN.1 本身只定义了表示信息的抽象句法,但是没有限定其编码的方法**。各种 ASN.1 编码规则提供了由 ASN.1 描述其抽象句法的数据的值的传送语法(具体表达)。标准的 ASN.1 编码规则有基本编码规则(BER,Basic Encoding Rules)、规范编码规则(CER,Canonical Encoding Rules)、可分辨编码规则(DER,Distinguished Encoding Rules)、压缩编码规则(PER,Packed Encoding Rules)和 XML 编码规则(XER,XML Encoding Rules)。
该机构从上层规定了规范,有点像IP协议ISO组织一个意思。
- X.509
X.509 是密码学里公钥证书的格式标准
。X.509 证书已应用包括 TLS/SSL 在内的众多网络协议里和一些非在线应用场景里,比如电子签名服务。X.509 证书里含有公钥、身份信息(比如网络主机名,组织的名称或个体名称等、 签名信息(可以是证书签发机构 CA 的签名,也可以是自签名)
- PKCS 系列
公钥加密标准(Public Key Cryptography Standards, PKCS),此一标准的设计与发布皆由 RSA 资讯安全公司(英语:RSA Security)所制定。
标准 | 版本 | 名称 | 简介 |
---|---|---|---|
PKCS #1 | 2.2 | RSA 加密规范(RSA Cryptography Standard) | 提供了基于 RSA 算法的公钥加密实现的建议,包括加密原语、加密方案、带附录的签名方案,以及用于表示密钥和识别方案的 ASN.1 语法。参见 RFC 8017。 |
PKCS #2 | - | 弃用 | 原本是用以规范 RSA 加密摘要的转换方式,现已被纳入 PKCS #1 之中。 |
PKCS #3 | 1.4 | Diffie-Hellman 协议标准(Diffie-Hellman key agreement Standard) | 规范以 Diffie-Hellman 协议为基础的密钥协议标准。其功能可以让两方通过过协议,拟定一把会议密钥(Session key)。 |
PKCS #4 | - | 弃用 | 原本用以规范转换 RSA 密钥的流程。已被纳入 PKCS #1 之中。 |
PKCS #5 | 2.1 | 基于密码的加密规范(Password-based Encryption Standard) | 参见 RFC 8018。 |
PKCS #6 | 1.5 | 证书扩展语法标准(Extended-Certificate Syntax Standard) | 将原本 X.509 的证书格式标准加以扩充。 |
PKCS #7 | 1.5 | 密码讯息语法标准(Cryptographic Message Syntax Standard) | 参见 RFC 2315。规范了以公开密钥基础设施(PKI)所产生之签章/密文的格式。其目的一样是为了拓展数位证书的应用。其中,包含了 S/MIME 与 CMS(英语:Cryptographic Message Syntax)。 |
PKCS #8 | 1.2 | 私钥信息语法规范(Private-Key Information Syntax Standard) | 存储私钥信息的标准语法。参见 RFC 5208。 |
PKCS #9 | 2.0 | 选择属性格式(Selected Attribute Types) | 定义 PKCS #6、7、8、10 的选择属性格式。 |
PKCS #10 | 1.7 | 证书申请标准(Certification Request Standard) | 参见 RFC 2986。规范了向证书中心申请证书的 CSR(certificate signing request)的格式。 |
PKCS #11 | 2.20 | 密码装置标准介面(Cryptographic Token Interface (Cryptoki)) | 定义了密码装置的应用程式介面(API)之规格。 |
PKCS #12 | 1.0 | 个人讯息交换标准(Personal Information Exchange Syntax Standard) | 定义了包含私钥与公钥证书(public key certificate)的文件格式。私钥采密码(password)保护。常见的 PFX 就履行了 PKCS #12。 |
PKCS #13 | – | 椭圆曲线密码学标准(Elliptic curve cryptography Standard) | 制定中。规范以椭圆曲线密码学为基础所发展之密码技术应用。椭圆曲线密码学是新的密码学技术,其强度与效率皆比现行以指数运算为基础之密码学演算法来的优秀。然而,该演算法的应用尚不普及。 |
PKCS #14 | – | 伪随机数生成器(英语:Pseudorandom number generator)标准 | 制定中。规范伪随机数生成器的使用与设计。 |
PKCS #15 | 1.1 | 密码装置讯息格式标准(Cryptographic Token Information Format Standard) | 定义了密码设备内部数据的组织结构。 |
Base64
Base64只是更利于数据显示,并不具备加密效果。
- 能表示的全部字符
因为Base64编码会把字节按照6bit
为一组进行划分,而6位所表示的二进制数的范围就是 000000 ~ 111111
能表示的字符如下所示:
- 编码过程
他会把客户端的字符按照 3个字节 为一组进行编码。如果客户端字符不够不够三个字节,他内部会用0进行补位。补齐到3个字节一组。
对于一组来说,他会按照6bit
进行二次分组。分成4组。
这4组,每一组就能表示一个字符
请注意,如果是补位得到的组,也就是000000
表示的不是ASCII码里面的A字符,而是 = 。
Java内置了Base64类,封装了编码解码的算法,帮我们做上面这些事情。
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Base64Test {
@Test
@DisplayName("将字符串转为Base64编码的字符串")
public void strToBase64(){
String source = "123456789";
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(source.getBytes(StandardCharsets.UTF_8));
System.out.println(base64); // MTIzNDU2Nzg5
}
@Test
@DisplayName("将Base64编码的字符串转为字符串")
public void base64ToStr(){
String base64 = "MTIzNDU2Nzg5";
Base64.Decoder decoder = Base64.getDecoder();
byte[] decode = decoder.decode(base64);
String source = new String(decode);
System.out.println(source); //123456789
}
}
签名
签名的优点:
A向B发送签名的消息P,可以有如下好处:
- 1、B可以验证消息P确实来源A
- 2、A以后不能否定发送过P
- 3、B不能编造或者改变消息P
如商城A发送消息P给支付宝网关B
从支付宝网关的角度看:
从互联网环境中,拿到了A的消息,通过验签(解密出来的数据和发送数据是否一致)来确定数据传输过程中没有被他人利用篡改。
从而执行了转账业务操作。
从商城A的角度看:
发送的数据签名是用了自己的私钥签名的,还用了支付宝的公钥加密了。很安全。支付宝不能改变这个消息。
摘要
摘要算法:SHA和MD5:就是通过一种算法,依据数据内容生成一种固定长度的摘要,这串摘要值与原数据存在对应关系。
实际应用过程中,因为需要加密的数据可能会很大,进行加密费时费力,所以一般都会把原数据先进行摘要,然后对这个摘要值进行加密,将原数据的明文和加密后的摘要值一起传给你.这样你解开加密后的摘要值,再和你得到的数据进行的摘要值对应一下就可以知道数据有没有被修改了(下文说的签名)。
摘要算法有很多,来看看JDK支持的摘要算法,摘要算法又分两种实现
一种是 MessageDigest
消息摘要
另一种是 Mac
Mac实现
MessageDigest
我先来看看MessageDigest
实现的。
MD2
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class CipherTest {
@Test
@DisplayName("MD2 ")
public void encAndDen() throws NoSuchAlgorithmException {
String algorithm = "MD2";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String result = Base64.getEncoder().encodeToString(digest);
System.out.println(result); // 1FQSULWGKW/M5d6kRjrhfw==
}
}
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class CipherTest {
@Test
@DisplayName("MD5 加密")
public void jdkMd5AndEncodeTOBase64() throws NoSuchAlgorithmException {
String algorithm = "MD5";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String result = Base64.getEncoder().encodeToString(digest);
System.out.println(result); // 4QrcOUm6Wau+VuBX8g+IPg==
}
}
MD5 可以将任意长度的原文 生成一个 128位(16字节的哈希值)。
在某些业务场景,我们除了会把该16字节的哈希值 转为 Base64 字符串 之外,也可以处理成 16进制字符串。
package com.wnx.naizi;
import cn.hutool.core.util.HexUtil;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class CipherTest {
@Test
@DisplayName("MD5 加密")
public void jdkMd5AndEncodeTOHexStr() throws NoSuchAlgorithmException {
String algorithm = "MD5";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String s = HexUtil.encodeHexStr(digest);
System.out.println(s); //
}
}
SHA-1
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class CipherTest {
@Test
@DisplayName("SHA-1 摘要")
public void encAndDen() throws NoSuchAlgorithmException {
String algorithm = "SHA-1";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
System.out.println(digest.length); // 20
String result = Base64.getEncoder().encodeToString(digest);
System.out.println(result); // fEqNCco3Yq9h5ZUglD3CZJT4lBs=
}
}
package com.wnx.naizi;
import cn.hutool.core.util.HexUtil;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class CipherTest {
@Test
@DisplayName("SHA-1 摘要")
public void encAndDenToHexString() throws NoSuchAlgorithmException {
String algorithm = "SHA-1";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String str = HexUtil.encodeHexStr(digest); // 7c4a8d09ca3762af61e59520943dc26494f8941b
System.out.println(str);
}
}
SHA-1可以将任意长度的原文 生成 20个字节(20 * 8 bit = 160 bit)位的哈希值。
我们也会把这样的Hash值转为Base64编码字符串之外,有时业务上也会转为 十六进制字符串。
SHA-224
package com.wnx.naizi;
import cn.hutool.core.util.HexUtil;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class CipherTest {
@Test
@DisplayName("SHA-224 摘要")
public void encAndDenToHexString() throws NoSuchAlgorithmException {
String algorithm = "SHA-224";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String str = HexUtil.encodeHexStr(digest); // f8cdb04495ded47615258f9dc6a3f4707fd2405434fefc3cbf4ef4e6
System.out.println(str);
}
}
SHA-256
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class CipherTest {
@Test
@DisplayName("SHA-256 摘要")
public void encAndDen() throws NoSuchAlgorithmException {
String algorithm = "SHA-256";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String result = Base64.getEncoder().encodeToString(digest);
System.out.println(result); // jZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=
}
}
SHA-384
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class CipherTest {
@Test
@DisplayName("SHA-384 加密")
public void encAndDen() throws NoSuchAlgorithmException {
String algorithm = "SHA-384";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String result = Base64.getEncoder().encodeToString(digest);
System.out.println(result); // CpievEp3tWpuK7exnZldGFzkQJDBPimEt+zG1EbUth6pmRt2pMLwSxtNJEhBRJRU
}
}
SHA-512
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class CipherTest {
@Test
@DisplayName("SHA-512 加密")
public void encAndDen() throws NoSuchAlgorithmException {
String algorithm = "SHA-512";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String result = Base64.getEncoder().encodeToString(digest);
System.out.println(result); // ujJTh2rta8ItSm/1PYQGxq2GQZXtFEq1yHYhtsIztUi66uaVbfNG7IwX9eoQ817jy8UUeX7X3dMUVGTioLq0Ew==
}
}
SHA-512/224
@Test
@DisplayName("SHA-512/224 加密")
public void encAndDenToHexString() throws NoSuchAlgorithmException {
String algorithm = "SHA-512/224";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String str = HexUtil.encodeHexStr(digest); // 007ca663c61310fbee4c1680a5bbe70071825079b23f092713383296
System.out.println(str);
}
SHA-512/256
@Test
@DisplayName("SHA-512/256 加密")
public void encAndDenToHexString() throws NoSuchAlgorithmException {
String algorithm = "SHA-512/256";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String str = HexUtil.encodeHexStr(digest); // 184b5379d5b5a7ab42d3de1d0ca1fedc1f0ffb14a7673ebd026a6369745deb72
System.out.println(str);
}
SHA3-224
@Test
@DisplayName("SHA3-224 加密")
public void encAndDenToHexString() throws NoSuchAlgorithmException {
String algorithm = "SHA3-224";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String str = new BigInteger(1, digest).toString(16); // 6be790258b73da9441099c4cb6aeec1f0c883152dd74e7581b70a648
System.out.println(str);
}
SHA3-256
@Test
@DisplayName("SHA3-256 加密")
public void encAndDenToHexString() throws NoSuchAlgorithmException {
String algorithm = "SHA3-256";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String str = new BigInteger(1, digest).toString(16); // d7190eb194ff9494625514b6d178c87f99c5973e28c398969d2233f2960a573e
System.out.println(str);
}
SHA3-384
@Test
@DisplayName("SHA3-384 加密")
public void encAndDenToHexString() throws NoSuchAlgorithmException {
String algorithm = "SHA3-384";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String str = new BigInteger(1, digest).toString(16); // 1fb0da774034ba308fbe02f3e90dc004191df7aec3758b6be8451d09f1ff7ec18765f96e71faff637925c6be1d65f1cd
System.out.println(str);
}
SHA3-512
@Test
@DisplayName("SHA3-512 加密")
public void encAndDenToHexString() throws NoSuchAlgorithmException {
String algorithm = "SHA3-512";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
String source = "123456";
byte[] digest = messageDigest.digest(source.getBytes(StandardCharsets.UTF_8));
String str = new BigInteger(1, digest).toString(16); //64d09d9930c8ecf79e513167a588cb75439b762ce8f9b22ea59765f32aa74ca19d2f1e97dc922a3d4954594a05062917fb24d1f8e72f2ed02a58ed7534f94d27
System.out.println(str);
}
Mac
和MessageDigest
比较。这个Mac
对象需要一个SecretKey
进行初始化,才能用。
HmacMD5
@Test
@DisplayName("HmacMD5 加密")
public void jdkMd5AndEncodeTOBase64() throws NoSuchAlgorithmException, InvalidKeyException {
String useKey = "123456789";
String algorithm = "HmacMD5";
SecretKey secretKey = new SecretKeySpec(useKey.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKey);
String source = "123456";
mac.update(source.getBytes());
byte[] result = mac.doFinal();
String hexStr = new BigInteger(1, result).toString(16);
System.out.println(hexStr);
// 2fa5a2a2d2e9d68a2cfb9821e2415464
}
HmacSHA1
public class CipherTest {
@Test
@DisplayName("HmacSHA1 加密")
public void encAndDec() throws NoSuchAlgorithmException, InvalidKeyException {
String useKey = "123456789";
String algorithm = "HmacSHA1";
SecretKey secretKey = new SecretKeySpec(useKey.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKey);
String source = "123456";
mac.update(source.getBytes());
byte[] result = mac.doFinal();
String hexStr = new BigInteger(1, result).toString(16);
System.out.println(hexStr);
// 560462b95eff73ca176032de405c0c08297de10f
}
}
HmacSHA256
@Test
@DisplayName("HmacSHA256 加密")
public void encAndDec() throws NoSuchAlgorithmException, InvalidKeyException {
String useKey = "123456789";
String algorithm = "HmacSHA256";
SecretKey secretKey = new SecretKeySpec(useKey.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKey);
String source = "123456";
mac.update(source.getBytes());
byte[] result = mac.doFinal();
String hexStr = new BigInteger(1, result).toString(16);
System.out.println(hexStr);
// 8ad6f4abeb59ba6009724395c90049105b427050c10808618ebb9be4f9a33aa
}
HmacSHA224
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class CipherTest {
@Test
@DisplayName("HmacSHA224 加密")
public void encAndDec() throws NoSuchAlgorithmException, InvalidKeyException {
String useKey = "123456789";
String algorithm = "HmacSHA224";
SecretKey secretKey = new SecretKeySpec(useKey.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKey);
String source = "123456";
mac.update(source.getBytes());
byte[] result = mac.doFinal();
String hexStr = new BigInteger(1, result).toString(16);
System.out.println(hexStr);
// 4cd1c941009fae2ac26587cc2cdaabe5c3a0f798f01fb618288aedf0
}
}
HmacSHA384
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class CipherTest {
@Test
@DisplayName("HmacSHA384 加密")
public void encAndDec() throws NoSuchAlgorithmException, InvalidKeyException {
String useKey = "123456789";
String algorithm = "HmacSHA384";
SecretKey secretKey = new SecretKeySpec(useKey.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKey);
String source = "123456";
mac.update(source.getBytes());
byte[] result = mac.doFinal();
String hexStr = new BigInteger(1, result).toString(16);
System.out.println(hexStr);
// a107a03e92e770c79d4d0e76160d853d63f253d4cc28654d39f03c0b8f900ea110b791dd9f67567ac3ed85f4692748d6
}
}
HmacSHA512
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class CipherTest {
@Test
@DisplayName("HmacSHA512 加密")
public void encAndDec() throws NoSuchAlgorithmException, InvalidKeyException {
String useKey = "123456789";
String algorithm = "HmacSHA512";
SecretKey secretKey = new SecretKeySpec(useKey.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKey);
String source = "123456";
mac.update(source.getBytes());
byte[] result = mac.doFinal();
String hexStr = new BigInteger(1, result).toString(16);
System.out.println(hexStr);
// 4e646209bd3a2aab053efdaeb58300ebb1bb80c6c5c66640218bd0136b6faa051ef5e8834531d51f4f413bc1e439d72bd52b40292674d3381aeccbc030d912e2
}
}
HmacSHA512/224
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class CipherTest {
@Test
@DisplayName("HmacSHA512/224 加密")
public void encAndDec() throws NoSuchAlgorithmException, InvalidKeyException {
String useKey = "123456789";
String algorithm = "HmacSHA512/224";
SecretKey secretKey = new SecretKeySpec(useKey.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKey);
String source = "123456";
mac.update(source.getBytes());
byte[] result = mac.doFinal();
String hexStr = new BigInteger(1, result).toString(16);
System.out.println(hexStr);
// ee9f2b8c40fdded9f753b4816db7c308252726f14618c7b77573218a
}
}
HmacSHA512/256
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class CipherTest {
@Test
@DisplayName("HmacSHA512/256 加密")
public void encAndDec() throws NoSuchAlgorithmException, InvalidKeyException {
String useKey = "123456789";
String algorithm = "HmacSHA512/256";
SecretKey secretKey = new SecretKeySpec(useKey.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKey);
String source = "123456";
mac.update(source.getBytes());
byte[] result = mac.doFinal();
String hexStr = new BigInteger(1, result).toString(16);
System.out.println(hexStr);
// 421dd7a2b5df6a6deac04da3f087a0a842ea77da07db58a65097a22fa616dfb5
}
}
HmacSHA3-224
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class CipherTest {
@Test
@DisplayName("HmacSHA3-224 加密")
public void encAndDec() throws NoSuchAlgorithmException, InvalidKeyException {
String useKey = "123456789";
String algorithm = "HmacSHA3-224";
SecretKey secretKey = new SecretKeySpec(useKey.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKey);
String source = "123456";
mac.update(source.getBytes());
byte[] result = mac.doFinal();
String hexStr = new BigInteger(1, result).toString(16);
System.out.println(hexStr);
// 82889d40ef2964e42efb2bbb7ef9e23e9353a1e9002d8059049819b4
}
}
HmacSHA3-256
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class CipherTest {
@Test
@DisplayName("HmacSHA3-256 加密")
public void encAndDec() throws NoSuchAlgorithmException, InvalidKeyException {
String useKey = "123456789";
String algorithm = "HmacSHA3-256";
SecretKey secretKey = new SecretKeySpec(useKey.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKey);
String source = "123456";
mac.update(source.getBytes());
byte[] result = mac.doFinal();
String hexStr = new BigInteger(1, result).toString(16);
System.out.println(hexStr);
// 42646c3ebc0eb96d605cc6a0d181c25ac558ca3710bf4fd4971698367648fd84
}
}
HmacSHA3-224
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class CipherTest {
@Test
@DisplayName("HmacSHA3-224 加密")
public void encAndDec() throws NoSuchAlgorithmException, InvalidKeyException {
String useKey = "123456789";
String algorithm = "HmacSHA3-224";
SecretKey secretKey = new SecretKeySpec(useKey.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKey);
String source = "123456";
mac.update(source.getBytes());
byte[] result = mac.doFinal();
String hexStr = new BigInteger(1, result).toString(16);
System.out.println(hexStr);
// 82889d40ef2964e42efb2bbb7ef9e23e9353a1e9002d8059049819b4
}
}
HmacSHA3-384
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class CipherTest {
@Test
@DisplayName("HmacSHA3-384加密")
public void encAndDec() throws NoSuchAlgorithmException, InvalidKeyException {
String useKey = "123456789";
String algorithm = "HmacSHA3-384";
SecretKey secretKey = new SecretKeySpec(useKey.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKey);
String source = "123456";
mac.update(source.getBytes());
byte[] result = mac.doFinal();
String hexStr = new BigInteger(1, result).toString(16);
System.out.println(hexStr);
// 742b2721109585685add7d0c0abf5b655c78d749d8a1e3b59527e8faa4e0c9d2b4ce53a71aec3ae58f6e9f93780b283c
}
}
HmacSHA3-512
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class CipherTest {
@Test
@DisplayName("HmacSHA3-512加密")
public void encAndDec() throws NoSuchAlgorithmException, InvalidKeyException {
String useKey = "123456789";
String algorithm = "HmacSHA3-512";
SecretKey secretKey = new SecretKeySpec(useKey.getBytes(), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKey);
String source = "123456";
mac.update(source.getBytes());
byte[] result = mac.doFinal();
String hexStr = new BigInteger(1, result).toString(16);
System.out.println(hexStr);
// b10de64a2b9cb26893448e676a65e14aeffbb98014b94361ed37430c983ee52ca99f20a8a9fc66834e56e59a7cf822bb50dfab4ae7abe2a4dfc7764ae65fbc1f
}
}
签名
日常什么时候需要签名?确定这个东西和你有关,是你批准的,是你同意的。XXXX合同??签名技术也被运用到互联网领域,确定这个数据包是你的。这XXX合同不是伪造的,对你,对他都是一种保护。在Java中对签名这件事用Signature
对象表示。我们只需要写入签名算法就能把他创建出来。
Signature signature = Signature.getInstance("MD5WithRSA");
initSign
签名实例,实际上就是用某个hash算法把IP数据报的数据部分(POST报文请求体)得到一个Hash值了。然后用私钥加密。这就完成了签名工作了。在Java你需要对签名进行初始化,等价于人家给你笔的过程。
以下演示了根据私钥初始化签名。
private String privateKeyStr = "MIIG/AIBADANBgkqhkiG9w0BAQEFAASCBuYwggbiAgEAA...";
private String publicKeyStr = "MIIBojANBgkqhkiG9w0BAQEFAAOCA...";
@Test
@DisplayName("根据私钥初始化签名对象")
public void initSignByPriateKey() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
String algorithm = "RSA";
byte[] encodedKey = Base64.getDecoder().decode(privateKeyStr);
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(encodedKey);
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance("MD5WithRSA");
signature.initSign(privateKey);
}
@Test
@DisplayName("根据 私钥和秘密随机对象 初始化签名对象")
public void getSignatureByPrivateKeyAndSecureRandom() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
String algorithm = "RSA";
byte[] encodedKey = Base64.getDecoder().decode(privateKeyStr);
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(encodedKey);
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance("MD5WithRSA");
SecureRandom secureRandom = new SecureRandom();
signature.initSign(privateKey,secureRandom);
byte[] sign = signature.sign();
}
sign
拿到笔了,签个名吧老哥。
private String privateKeyStr = "MIIG/AIBADANBgkqhkiG9w0BAQEFA...";
private String publicKeyStr = "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBig...";
@Test
@DisplayName("签名对象进行签名")
public void getSignatureByPrivateKey() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
String algorithm = "RSA";
byte[] encodedKey = Base64.getDecoder().decode(privateKeyStr);
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(encodedKey);
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance("MD5WithRSA");
signature.initSign(privateKey);
// 需要签名的数据 = POST请求体数据
byte[] needSignatureData = "王乃醒".getBytes(StandardCharsets.UTF_8);
signature.update(needSignatureData);
byte[] sign = signature.sign();
String signResult = Base64.getEncoder().encodeToString(sign);
System.out.println(signResult);
// 需要签名的数据
byte[] needSignatureData = "王乃醒".getBytes(StandardCharsets.UTF_8);
signature.update(needSignatureData);
//Scbno/HT9YmNPbzC0S3Sk1Q+6UivEecjoAZ9loFbmmsJvOfwiXEF48naPNfX1dRNt4FRAagv4EDpc0jUSAZmKn0yYkG44leXBRNMp72pMnIrl138odSRDlG4OZCFxnot3OGWJiBVVK+dRi5Sqe3js+/5Y35sxoCOe0d4HNvyFkiR4PWT1CTA62Q7UDNfBv/lgA2UNtzGpEREZvDU86utVj3H9nOvy4oo/X5k9NhdDr3EVODw2/tajoQNWWKNITMAfpz8Gd/uQe/us34sXZk7tkIFDCkqpcTpOlibN+yoIQ8yEcw3cnDlWHSNVxMBWzUQd0l9qMGHhqQZv3mayajxQ6zpKT5B3k8/+DBCTsH3bmkqYTllt60X+mToMRv0ZFQr8JEw/ZZDVV/yyCd+eg2EIGnqNY5p9M+UVYuFteF/vVwrJ4SbCWEsgQ2MWJ2qD8fsh4C+xZydP1SPzG4ilO7+ltHaBGP07xR+rp5my5D3iNxV+7yP8Oa1RD7uzFpYR7v5
}
initVerify
好了,开始在互联网传输了。人家即使把信息拦截了,因为没有你的公钥,完全看不懂这是什么。。。最终经过路由转发到你手上了。你有公钥,解密他.
private String privateKeyStr = "MIIG/AIBADANBgkqhkiG9w0BAQEFA...";
private String publicKeyStr = "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBig...";
@Test
@DisplayName("签名验证公钥")
public void initVerify() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
String algorithm = "RSA";
byte[] encodedKey = Base64.getDecoder().decode(publicKeyStr);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initVerify(publicKey);
}
verify
怎么验签?无非把解密数据用发送方的hash算法做一次他做过的事情,由相同原文经过同一hash算法后最终得到的hash值均一致(hash算法特点),所以我们看这个签名和这个hash如果一致,我就认为这个签名没有被篡改。
Java封装好了,你只需要告诉我 相同原文是啥?该事例POST请求数据 = 王乃醒
private String privateKeyStr = "MIIG/AIBADANBgkqhkiG9w0BAQEFA...";
private String publicKeyStr = "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBig...";
@Test
@DisplayName("用公钥验证签名")
public void getPublicKeyByRSA() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
String algorithm = "RSA";
byte[] encodedKey = Base64.getDecoder().decode(publicKeyStr);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initVerify(publicKey);
String signResult = "Scbno/HT9YmNPbzC0S3Sk1Q+6UivEecjoAZ9loFbmmsJvOfwiXEF48naPNfX1dRNt4FRAagv4EDpc0jUSAZmKn0yYkG44leXBRNMp72pMnIrl138odSRDlG4OZCFxnot3OGWJiBVVK+dRi5Sqe3js+/5Y35sxoCOe0d4HNvyFkiR4PWT1CTA62Q7UDNfBv/lgA2UNtzGpEREZvDU86utVj3H9nOvy4oo/X5k9NhdDr3EVODw2/tajoQNWWKNITMAfpz8Gd/uQe/us34sXZk7tkIFDCkqpcTpOlibN+yoIQ8yEcw3cnDlWHSNVxMBWzUQd0l9qMGHhqQZv3mayajxQ6zpKT5B3k8/+DBCTsH3bmkqYTllt60X+mToMRv0ZFQr8JEw/ZZDVV/yyCd+eg2EIGnqNY5p9M+UVYuFteF/vVwrJ4SbCWEsgQ2MWJ2qD8fsh4C+xZydP1SPzG4ilO7+ltHaBGP07xR+rp5my5D3iNxV+7yP8Oa1RD7uzFpYR7v5";
byte[] data = Base64.getDecoder().decode(signResult);
//对王乃醒请求报文 做一次hash。
byte[] needSignatureData = "王乃醒".getBytes(StandardCharsets.UTF_8);
signature.update(needSignatureData);
boolean verify = signature.verify(data);
System.out.println(verify); // true
}
有了上面的API铺垫,我们来看看Java原生支持的签名算法,并执行一遍完整的流程。
MD5WithRSA
package com.wnx.naizi;
import cn.hutool.core.io.FileUtil;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* @ClassName: SecretKeyFactoryTest
* @Package: com.wnx.naizi
* @Description: 密钥工厂 API测试
* @Author: wangnaixing
* @Create: 2022/12/18 - 16:40
* @Version:v1.0
*/
public class SignatureTest {
private String privateKeyStr = "MIIG/AIBADANBgkqhkiG9w0BAQEFAASCB..";
private String publicKeyStr = "MIIBojANBgkqhkiG9w0BAQEFAAOCAY..";
@Test
@DisplayName("签名")
public void signByMD5WithRSA() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
String algorithm = "RSA";
byte[] encodedKey = Base64.getDecoder().decode(privateKeyStr);
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(encodedKey);
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance("MD5WithRSA");
signature.initSign(privateKey);
byte[] needSignatureData = "王乃醒".getBytes(StandardCharsets.UTF_8);
signature.update(needSignatureData);
byte[] sign = signature.sign();
String signResult = Base64.getEncoder().encodeToString(sign);
System.out.println(signResult);
//Scbno/HT9YmNPbzC0S3Sk1Q+6UivEecjoAZ9loFbmmsJvOfwiXEF48naPNfX1dRNt4FRAagv4EDpc0jUSAZmKn0yYkG44leXBRNMp72pMnIrl138odSRDlG4OZCFxnot3OGWJiBVVK+dRi5Sqe3js+/5Y35sxoCOe0d4HNvyFkiR4PWT1CTA62Q7UDNfBv/lgA2UNtzGpEREZvDU86utVj3H9nOvy4oo/X5k9NhdDr3EVODw2/tajoQNWWKNITMAfpz8Gd/uQe/us34sXZk7tkIFDCkqpcTpOlibN+yoIQ8yEcw3cnDlWHSNVxMBWzUQd0l9qMGHhqQZv3mayajxQ6zpKT5B3k8/+DBCTsH3bmkqYTllt60X+mToMRv0ZFQr8JEw/ZZDVV/yyCd+eg2EIGnqNY5p9M+UVYuFteF/vVwrJ4SbCWEsgQ2MWJ2qD8fsh4C+xZydP1SPzG4ilO7+ltHaBGP07xR+rp5my5D3iNxV+7yP8Oa1RD7uzFpYR7v5
}
@Test
@DisplayName("得到签名和传输数据 开始验签~~")
public void verifyByMD5WithRSA() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
String algorithm = "RSA";
byte[] encodedKey = Base64.getDecoder().decode(publicKeyStr);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initVerify(publicKey);
String signResult = "Scbno/HT9YmNPbzC0S3Sk1Q+6UivEecjoAZ9loFbmmsJvOfwiXEF48naPNfX1dRNt4FRAagv4EDpc0jUSAZmKn0yYkG44leXBRNMp72pMnIrl138odSRDlG4OZCFxnot3OGWJiBVVK+dRi5Sqe3js+/5Y35sxoCOe0d4HNvyFkiR4PWT1CTA62Q7UDNfBv/lgA2UNtzGpEREZvDU86utVj3H9nOvy4oo/X5k9NhdDr3EVODw2/tajoQNWWKNITMAfpz8Gd/uQe/us34sXZk7tkIFDCkqpcTpOlibN+yoIQ8yEcw3cnDlWHSNVxMBWzUQd0l9qMGHhqQZv3mayajxQ6zpKT5B3k8/+DBCTsH3bmkqYTllt60X+mToMRv0ZFQr8JEw/ZZDVV/yyCd+eg2EIGnqNY5p9M+UVYuFteF/vVwrJ4SbCWEsgQ2MWJ2qD8fsh4C+xZydP1SPzG4ilO7+ltHaBGP07xR+rp5my5D3iNxV+7yP8Oa1RD7uzFpYR7v5";
byte[] data = Base64.getDecoder().decode(signResult);
byte[] needSignatureData = "王乃醒".getBytes(StandardCharsets.UTF_8);
signature.update(needSignatureData);
boolean verify = signature.verify(data);
System.out.println(verify); // true
}
}
SHA256WithRSA
package com.wnx.naizi;
import cn.hutool.core.io.FileUtil;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* @ClassName: SignatureTest
* @Package: com.wnx.naizi
* @Description:
* @Author: wangnaixing
* @Create: 2022/12/25 - 17:39
* @Version:v1.0
*/
public class SignatureTest {
private String privateKeyStr = "MIIG/AIBADANBgkqhkiG9w0BAQEFAASC...";
private String publicKeyStr = "MIIBojANBgkqhkiG9w0BAQEFA...";
@Test
@DisplayName("签他")
public void signSHA256WithRSA() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
String algorithm = "RSA";
byte[] encodedKey = Base64.getDecoder().decode(privateKeyStr);
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(encodedKey);
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initSign(privateKey);
String plainText = "王乃醒";
byte[] needSignatureData = plainText.getBytes(StandardCharsets.UTF_8);
signature.update(needSignatureData);
byte[] sign = signature.sign();
String signResult = Base64.getEncoder().encodeToString(sign);
System.out.println(signResult);
// tlp4x/DKD2pwWCJ8vaGQ1f3KPFvL2uV4/WNgQGjyP6qEGtFCjLispMd78SOePb2xBweMpX6ddnSGTwHaTinPfIAE79MK0ebCzm9jnRy+2o1VOb6bmX1TL8BocNRifEAclLOUdw8DTRx6vWpf5Es+vDyTyR+RbNa8Q9qgX56Ot7Q1+85ze+15idvcWRy4YnUdYhh06zJm4avYqln8+vy93fFp83dKGptdi7ei5sJq08QUgw1btz+t/4T39rsWSvk4FNUpuCDKXNIuPjSzTVKk7i1GJUwO5tM/INwmsJal0Ill8p7awOfZKReh+UHbpXxr/VyFi53G0/eNa4f9Q96ws7mviKUsZN445ITjCBx2gRhfPAz8k9s96OTTlqru9XEvZduk+mXyGI9zEeoJe4lIF2hiyX4I3ExAgRxbIOCUukaOdVyc1ylNYeJwt6TPbstjP1XjogqPKsTVz/Y6y2f3gIArYNF1t8ZOT6r6W5cqjvkEVpeYeHw3S1kC0EbDLL8/
}
@Test
@DisplayName("验货")
public void verifySHA256WithRSA() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
String algorithm = "RSA";
byte[] encodedKey = Base64.getDecoder().decode(publicKeyStr);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initVerify(publicKey);
String signResult = "tlp4x/DKD2pwWCJ8vaGQ1f3KPFvL2uV4/WNgQGjyP6qEGtFCjLispMd78SOePb2xBweMpX6ddnSGTwHaTinPfIAE79MK0ebCzm9jnRy+2o1VOb6bmX1TL8BocNRifEAclLOUdw8DTRx6vWpf5Es+vDyTyR+RbNa8Q9qgX56Ot7Q1+85ze+15idvcWRy4YnUdYhh06zJm4avYqln8+vy93fFp83dKGptdi7ei5sJq08QUgw1btz+t/4T39rsWSvk4FNUpuCDKXNIuPjSzTVKk7i1GJUwO5tM/INwmsJal0Ill8p7awOfZKReh+UHbpXxr/VyFi53G0/eNa4f9Q96ws7mviKUsZN445ITjCBx2gRhfPAz8k9s96OTTlqru9XEvZduk+mXyGI9zEeoJe4lIF2hiyX4I3ExAgRxbIOCUukaOdVyc1ylNYeJwt6TPbstjP1XjogqPKsTVz/Y6y2f3gIArYNF1t8ZOT6r6W5cqjvkEVpeYeHw3S1kC0EbDLL8/";
byte[] data = Base64.getDecoder().decode(signResult);
String plainText = "王乃醒";
byte[] needSignatureData = plainText.getBytes(StandardCharsets.UTF_8);
signature.update(needSignatureData);
boolean verify = signature.verify(data);
System.out.println(verify); // true
}
}
证书
证书文件存在多种格式保存在计算机中。
1. .p12 .pfx 二进制格式,同时包含证书和私钥,一般有密码保护。
2. .pem 文本格式,保存证书或者私钥
3. .crt 二进制格式或者文本格式,只保存证书
4. .jks 二进制格式,同时包含证书和私钥,一般有密码保护;jks是java的专属格式,它里面可以存储多张证书。
5. .der .cer 二进制格式,只保存证书,不保存密钥
秘钥库类型:
- 1、JKS
- 2、PKCS12 行业标准
现在让我们制作一个证书吧,JDK提供了KeyTool
工具,借助他,我们就能生成证书啦。
# 使用JDK的KeyTool工具 生成秘钥
# 给秘钥起一个别名 指定为 keystore01
# 给秘钥库的类型 指定为 JKS,
# 给秘钥使用的算法 指定为 RSA
# 给秘钥的长度 指定为 2048 bit
# 给指定秘钥生成路径, 指定为 D盘 名称为 keystore01.p12文件中
# 给秘钥库访问的密码 指定为 wangnaixing
# 给秘钥有效期 指定为 3650
keytool -genkey -alias keystore01 -storetype JKS -keyalg RSA -keysize 2048 -keystore D:/keystore01.p12 -storepass wangnaixing -validity 3650
嗯嗯,我们再生成一个新的,行业标准的 PKCS12
# 使用JDK的KeyTool工具 生成秘钥
# 给秘钥起一个别名 指定为 keystore02
# 给秘钥库的类型 指定为 PKCS12,
# 给秘钥使用的算法 指定为 RSA
# 给秘钥的长度 指定为 2048 bit
# 给指定秘钥生成路径, 指定为 D盘 名称为 keystore02.p12文件中
# 给秘钥库访问的密码 指定为 wangnaixing
# 给秘钥有效期 指定为 3650
keytool -genkey -alias keystore02 -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore D:/keystore02.p12 -storepass wangnaixing -validity 3650
KeyStore
我们想把 keystore01.p12
keystore02.p12
加载到Java中。该怎么做呢?Java提供了keyStore
类来给我们做。
根据秘钥类型拿到KeyStore对象,load一下就好。这是不是很像Properties
配置文件加载进Java中一样。
@Test
@DisplayName("获取密钥库对象 PKCS12")
public void getInstanceUseTypeIsPKCS12() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore01.p12");
String type = "PKCS12";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
}
@Test
@DisplayName("获取密钥库对象 JKS")
public void getInstanceUseJKS() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore02.p12");
String type = "JKS";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
}
你可以加载多个同样类型的证书到KeyStore
存储中。假设此刻你加载了两个同样类型的证书到KeyStore了,我怎么取到我想要的那一个呢?通过别名alias
每一个证书都有一个别名的。
@Test
@DisplayName("返回与给定别名关联的证书")
public void getCertificate() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore02.p12");
String type = "PKCS12";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
String alias = "keystore02";
Certificate certificate = keyStore.getCertificate(alias);
System.out.println(certificate);
}
你记不清楚别名叫什么的情况下,你可以通过aliases()
拿到所有证书的别名数组。
@Test
@DisplayName(" 获取所有证书名")
public void aliases() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore02.p12");
String type = "PKCS12";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
System.out.println(alias); // keystore02
}
}
在生成证书的时候,你输入秘钥库访问的密码,这个密码底层采用的是对称加密。同样你通过KeyStore
对象拿到。
@Test
@DisplayName(" 返回与给定别名关联的密钥,并使用给定密码恢复该密钥。")
public void getKey() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException, UnrecoverableKeyException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore02.p12");
String type = "PKCS12";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
String alias = "keystore02";
// key = wangnaixing
Key key = keyStore.getKey(alias, password);
System.out.println(key);
}
Certificate
证书的格式,是遵从ITUT X509国际协议标准。在X.509标准中,数字证书的一般格式包含的作用域如下。
- 1、版本号
- 2、序列号
- 3、签名算法
- 4、发行者
- 5、有效期
- 6、主体名
- 7、公钥
- 8、发行者ID
- 9、主体ID
- 10、扩展域
- 11、CA(认证机构)对证书的签名
我们得到的Java对象 Certificate
确实封装了这些信息。
@Test
@DisplayName("获取证书版本号")
public void getVersion() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException, UnrecoverableKeyException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore02.p12");
String type = "PKCS12";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
String alias = "keystore02";
Certificate certificate = keyStore.getCertificate(alias);
X509Certificate x509Certificate = (X509Certificate) certificate;
int version = x509Certificate.getVersion();
System.out.println(version); // 3
}
@Test
@DisplayName("获取证书版本号 序列号是由证书颁发机构CA分配给每个证书的整数 此整数唯一")
public void getSerialNumber() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException, UnrecoverableKeyException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore02.p12");
String type = "PKCS12";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
String alias = "keystore02";
Certificate certificate = keyStore.getCertificate(alias);
X509Certificate x509Certificate = (X509Certificate) certificate;
BigInteger serialNumber = x509Certificate.getSerialNumber();
System.out.println(serialNumber); // 1928866772
@Test
@DisplayName("获取证书签名算法 签署整数所用到的算法以及参数")
public void getSigAlgName() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException, UnrecoverableKeyException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore02.p12");
String type = "PKCS12";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
String alias = "keystore02";
Certificate certificate = keyStore.getCertificate(alias);
X509Certificate x509Certificate = (X509Certificate) certificate;
String sigAlgName = x509Certificate.getSigAlgName();
System.out.println(sigAlgName); // SHA256withRSA
}
@Test
@DisplayName("获取证书发行者")
public void getIssuerX500Principal() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException, UnrecoverableKeyException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore02.p12");
String type = "PKCS12";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
String alias = "keystore02";
Certificate certificate = keyStore.getCertificate(alias);
X509Certificate x509Certificate = (X509Certificate) certificate;
X500Principal issuer = x509Certificate.getIssuerX500Principal();
System.out.println(issuer.getName()); // CN=test,OU=test,O=test,L=test,ST=test,C=test
}
@Test
@DisplayName("获取证书有效期 不早于 不晚于")
public void getNotBeforeAndGetNotAfter() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException, UnrecoverableKeyException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore02.p12");
String type = "PKCS12";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
String alias = "keystore02";
Certificate certificate = keyStore.getCertificate(alias);
X509Certificate x509Certificate = (X509Certificate) certificate;
Date notBefore = x509Certificate.getNotBefore();
Date notAfter = x509Certificate.getNotAfter();
System.out.println(DateUtil.format(notBefore, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:dd")));
System.out.println(DateUtil.format(notAfter, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:dd")));
/**
* 2022-12-25 21:00:25
* 2032-12-22 21:00:22
*/
}
@Test
@DisplayName("获取证书主体名 指的证书持有者的名称和有关信息")
public void getSubjectX500Principal() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException, UnrecoverableKeyException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore02.p12");
String type = "PKCS12";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
String alias = "keystore02";
Certificate certificate = keyStore.getCertificate(alias);
X509Certificate x509Certificate = (X509Certificate) certificate;
X500Principal subject= x509Certificate.getSubjectX500Principal();
System.out.println(subject); // CN=test, OU=test, O=test, L=test, ST=test, C=test
}
package com.wnx.naizi;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
public class CipherTest {
@Test
@DisplayName("生成证书对象扩展值")
public void getExtensionValue() throws CertificateException, IOException {
String type = "X.509";
CertificateFactory certificateFactory = CertificateFactory.getInstance(type);
String certPath = "D:/Microsoft2010.cer";
FileInputStream fileInputStream = new FileInputStream(certPath);
Certificate certificate = certificateFactory.generateCertificate(fileInputStream);
X509Certificate x509Certificate = (X509Certificate) certificate;
// 策略映射
byte[] SubjectKeyIdentifier = x509Certificate.getExtensionValue("2.5.29.14");
// 关键字用法
byte[] KeyUsage = x509Certificate.getExtensionValue("2.5.29.15");
// PrivateKey用法
byte[] PrivateKeyUsage = x509Certificate.getExtensionValue("2.5.29.16");
//主体可选名
byte[] SubjectAlternativeName = x509Certificate.getExtensionValue("2.5.29.17");
// 发行者可选名
byte[] IssuerAlternativeName = x509Certificate.getExtensionValue("2.5.29.18");
// 基本约束
byte[] BasicConstraints = x509Certificate.getExtensionValue("2.5.29.19");
// 名称约束
byte[] NameConstraints = x509Certificate.getExtensionValue("2.5.29.30");
// 策略映射
byte[] PolicyMappings = x509Certificate.getExtensionValue("2.5.29.33");
// 授权密钥识别符
byte[] AuthorityKeyIdentifier = x509Certificate.getExtensionValue("2.5.29.35");
// 策略约束
byte[] PolicyConstraints = x509Certificate.getExtensionValue("2.5.29.36");
}
}
@Test
@DisplayName("生成证书认证机构的签名")
public void getSignature() throws CertificateException, IOException {
String type = "X.509";
CertificateFactory certificateFactory = CertificateFactory.getInstance(type);
String certPath = "D:/Microsoft2010.cer";
FileInputStream fileInputStream = new FileInputStream(certPath);
Certificate certificate = certificateFactory.generateCertificate(fileInputStream);
X509Certificate x509Certificate = (X509Certificate) certificate;
byte[] signature = x509Certificate.getSignature();
System.out.println(Arrays.toString(signature));
}
getPublicKey
除了这些获取信息的API之外,证书的公钥。仔细观察浏览器,只要是https请求,在浏览器某个存储位置,就保存有这个证书,同服务器建立TCP连接之后,他们之间通信都是一种加密通信,也是我们说HTTPS只是TLS+HTTP的原因了。
@Test
@DisplayName("从证书获取访问服务器资源公钥")
public void getPublicKey() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore02.p12");
String type = "PKCS12";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
String alias = "keystore02";
Certificate certificate = keyStore.getCertificate(alias);
PublicKey publicKey = certificate.getPublicKey();
System.out.println(publicKey);
}
verify
证书还携带了CA的签名。验证有没有这个签名存在。
@Test
@DisplayName("验证此证书是否使用与指定公钥对应的私钥进行了签名")
public void verify() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore02.p12");
String type = "PKCS12";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
String alias = "keystore02";
Certificate certificate = keyStore.getCertificate(alias);
PublicKey publicKey = certificate.getPublicKey();
certificate.verify(publicKey);
getEncoded
@Test
@DisplayName("返回此证书的编码形式 encoded as ASN.1 DER")
public void getEncoded() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
FileInputStream fileInputStream = new FileInputStream("D:/keystore02.p12");
String type = "PKCS12";
KeyStore keyStore = KeyStore.getInstance(type);
char[] password = "wangnaixing".toCharArray();
keyStore.load(fileInputStream,password);
String alias = "keystore02";
Certificate certificate = keyStore.getCertificate(alias);
// 转为Base64输出
String content = new String(Base64.getEncoder().encode(certificate.getEncoded()));
System.out.println(content);
}
MIIDUzCCAjugAwIBAgIEcvgr1DANBgkqhkiG9w0BAQsFADBaMQ0wCwYDVQQGEwR0ZXN0MQ0wCwYDVQQIEwR0ZXN0MQ0wCwYDVQQHEwR0ZXN0MQ0wCwYDVQQKEwR0ZXN0MQ0wCwYDVQQLEwR0ZXN0MQ0wCwYDVQQDEwR0ZXN0MB4XDTIyMTIyNTEzMDAzOFoXDTMyMTIyMjEzMDAzOFowWjENMAsGA1UEBhMEdGVzdDENMAsGA1UECBMEdGVzdDENMAsGA1UEBxMEdGVzdDENMAsGA1UEChMEdGVzdDENMAsGA1UECxMEdGVzdDENMAsGA1UEAxMEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJSFLV1dAbefTifo6MV7ZfJKbEsqqO9CP14EBvQ8rSFeHrrFAntz3GNhwjXZZSWVhJJYqyR3wCPZ3F8Oxpen9DlNZH4mRvRcXIisXUZtp/TBUntS0K0428iq09A9hgsxnEX/Bp/YyYeG4YSYQ+xroyiQSdzU8XlIrZT9RUuBmDFtawNeSa0n/2OWGbt/3Df5I9veHDLjBLXW2M6pVmI+zTgRuApGinimIQs6qehwqIrYJXxy4Z+Qf5RPdQoPho/6Mmt6NrKTJWWogwuE2qZk+vWg5oz/eoGt20aZUV7D+izIlrQvmiWdMiZOujhjFWXjtEPq1FUTS49b2nIY4OfN40sCAwEAAaMhMB8wHQYDVR0OBBYEFLJmywmQay8x+356elK3biTmAC6sMA0GCSqGSIb3DQEBCwUAA4IBAQBsXNBCVCgzAWSI3HUm8sxoc237Ml1BzbR/6OoGngaLmDTIoFnflRNUriKcLbytZTGY/V3GkMslLuKPmXGLsqV9swqpdJaBo1YW+jTKrGqItyHgpfP0xRwwTqKC+ZRVGQfwzXB4taOqL/5AUI6eM+Mcm5hR7/7Kj/0ghQ6NkRIEmIyBDcTwNFx46RosBnNa2/e2ShkTGIE8RVNoiYGGpzaJIvE+veuMTkC8gV4ITA6j5KbQsGMtjwUDM+0FDADj+4UqkDSyGmdo0i9iEkWKpmc4wOXq0XaTRBTlokqbajl7fWRbeQuRGlPIBOgmz+2k19n8iMZZJp6OZvrImBswqNbM
CertificateFactory
如果使用KeyTool
生成的证书,可以用KeyStore
加载到Java程序内存,然后从这个内存中拿到证书对象,但是生成证书的方式不止KeyTool
这一种方式吧。比如openssl
呢?为了满足各种情况下证书文件都能被创建出来,Java提供了CertificateFactory
工厂类来生产。
我们打开本地的证书管理器certmgr
。
# 1、通过win + r 输入certmgr.msc 进入到证书管理页面
certmgr.msc
# 2、点击菜单栏的操作选项,并选定查找证书下拉项。
# 3、输入操作内容 比如2010
# 4、 选中某项证书,直接导出就可以。
# 5、给导出的文件指定一个文件名,比如我这边就叫做 Microsoft2010
# 6、最后就可以拿到这个证书了!
文章来源:https://www.toymoban.com/news/detail-810608.html
文章来源地址https://www.toymoban.com/news/detail-810608.html
@Test
@DisplayName("返回实现指定证书类型的证书工厂对象")
public void getInstance() throws CertificateException, FileNotFoundException {
String type = "X.509";
CertificateFactory certificateFactory = CertificateFactory.getInstance(type);
System.out.println(certificateFactory); // java.security.cert.CertificateFactory@358ee631
String certPath = "D:/Microsoft2010.cer";
FileInputStream fileInputStream = new FileInputStream(certPath);
Certificate certificate = certificateFactory.generateCertificate(fileInputStream);
X509Certificate x509Certificate = (X509Certificate) certificate;
}
到了这里,关于38_安全密码学基础的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!