前言

和前端进行数据交互时或者和第三方商家对接时,需要对隐私数据进行加密。单向加密,对称加密,非对称加密,其对应的算法也各式各样。java提供了统一的框架来规范(java.security)安全加密这类API。下面将一一介绍

  • 加密算法概念及分类
  • 秘钥生成
  • 摘要算法工具-MessageDigest
  • 签名算法工具-Signature
  • 常用加密工具类-Cipher
  • Certificate-证书的保存
  • KeyStore-密钥证书的实体类
  • https证书加载

关注公众号,一起交流;微信搜一搜: 潜行前行

1 加密算法概念及分类

常用的加密算法类型有三种,如下:

  • 单向加密:也就是不可逆的加密,例如MD5,SHA,HMAC
  • 对称加密:也就是加密方和解密方利用同一个秘钥对数据进行加密和解密,例如DES,PBE等等
  • 非对称加密:非对称加密分为公钥和秘钥,二者是非对称的,例如用私钥加密的内容需要使用公钥来解密,使用公钥加密的内容需要用私钥来解密,DSA,RSA

2 秘钥生成

对称加密密钥的生成

  • KeyGenerator用于生成对称秘钥(可逆加密),或者一个密码性秘钥
  • 支持算法:AES、ARCFOUR、DES、DESede、HmacMD5、HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384、HmacSHA512、RC2
public static final KeyGenerator getInstance(String algorithm, String provider)
public static final KeyGenerator getInstance(String algorithm)
public final void init(int keysize)
public final void init(int keysize, SecureRandom random)
public final void init(SecureRandom random)
public final void init(AlgorithmParameterSpec params, SecureRandom random)
public final SecretKey generateKey()
  • 示例
public static void main(String[] args) throws  Exception {
SecretKey secretKey = generatorDesKey();
System.out.println(secretKey);
}
public static SecretKey generatorDesKey() throws NoSuchAlgorithmException {
KeyGenerator keyGen = KeyGenerator.getInstance("DES");
SecureRandom random = new SecureRandom();
random.nextBytes(new byte[128]);
keyGen.init(56,random);
SecretKey key = keyGen.generateKey();
return key;
}
------------输出结果------------------
com.sun.crypto.provider.DESKey@185c3

非对称加密秘钥的生成

  • KeyPairGenerator用于生成非对称加密算法的密钥对KeyPair,KeyPair会包括一个公钥和私钥
  • 支持算法:DiffieHellman、DSA、RSA、RSASSA-PSS、EC
//KeyPairGenerator.java
public static KeyPairGenerator getInstance(String algorithm)
public static KeyPairGenerator getInstance(String algorithm, String provider)
public void initialize(int keysize, SecureRandom random)
public void initialize(AlgorithmParameterSpec params, SecureRandom random)
public final KeyPair genKeyPair()
//KeyPair.java
public PublicKey getPublic()
public PrivateKey getPrivate()
  • 示例
public static void main(String[] args) throws Exception {
KeyPair keyPair = generatorRsaKey();
System.out.println(keyPair);
}
public static KeyPair generatorRsaKey() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom random = new SecureRandom();
random.nextBytes(new byte[516]);
keyGen.initialize(516,random);
KeyPair keyPair = keyGen.genKeyPair();
System.out.println(keyPair.getPrivate());
System.out.println(keyPair.getPublic());
return keyPair;
}
  • 输出结果
SunRsaSign RSA private CRT key, 516 bits
params: null
modulus: 126519853979546358862851378153247782379894323767375778571361894186790679401365500006956495592162216057219204240578435837612184688685910973224797092901015673
private exponent: 84346569319697572575234252102165188253262882511583852380907929457860452934243188047935652497010382336410866699832067872276413297543254894848799721123249067
Sun RSA public key, 516 bits
params: null
modulus: 126519853979546358862851378153247782379894323767375778571361894186790679401365500006956495592162216057219204240578435837612184688685910973224797092901015673
public exponent: 3
java.security.KeyPair@5010be6

密钥Key和密钥规格KeySpec的相互转化

If the key is stored on a hardware device, its specification may contain information that helps identify the key on the device

KeySpec是一个接口,用来组成加密密钥的密钥内容的(透明)规范。如果密钥存储在硬件设备上,则其规范可以包含有助于标识该设备上的密钥的信息

  • KeySpec具有规范性,所以一般会根据外部参数生成KeySpec,再根据KeySpec生成对应的Key(个人理解,如有高见,请说出你的见解)。SecretKeyFactory、KeyFactory的作用就是转换Key与KeySpec

SecretKeyFactory:用于对称加密的密钥和密钥规格之间的转换,配合KeyGenerator使用

  • 支持算法:AES、ARCFOUR、DES、DESede、PBEWithMD5AndDES、PBEWithHmacSHA256AndAES_128、PBKDF2WithHmacSHA256
public static final SecretKeyFactory getInstance(String algorithm)
public static final SecretKeyFactory getInstance(String algorithm, String provider)
public final SecretKey translateKey(SecretKey key)
public final SecretKey generateSecret(KeySpec keySpec)
public final KeySpec getKeySpec(SecretKey key, Class<?> keySpec)
  • 示例
public static void main(String[] args) throws Exception {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
byte[] DESKey = "helloWWW".getBytes(StandardCharsets.UTF_8);// 设置密钥
DESKeySpec keySpec = new DESKeySpec(DESKey);// 设置密钥参数
SecretKey key = keyFactory.generateSecret(keySpec);// 得到密钥对象
System.out.println(key);
}
------------输出结果------------------
com.sun.crypto.provider.DESKey@18e49

KeyFactory:用于非对称加密的密钥和密钥规格之间的转换,配合KeyPairGenerator使用

  • 支持算法:DiffieHellman、DSA、RSA、RSASSA-PSS、EC
//KeyFactory.java
public static KeyFactory getInstance(String algorithm)
public static KeyFactory getInstance(String algorithm, String provider)
public final PublicKey generatePublic(KeySpec keySpec)
public final PrivateKey generatePrivate(KeySpec keySpec)
public final <T extends KeySpec> T getKeySpec(Key key, Class<T> keySpec)
  • 示例
public static void main(String[] args) throws Exception {
//生成RSA秘钥对;generatorRsaKey是上面示例提供的函数
KeyPair keyPair = generatorRsaKey();
System.out.println(keyPair);
//PublicKey转KeySpec;KeySpec再转PublicKey
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(keyPair.getPublic().getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
System.out.println(pubKey);
//PrivateKey转KeySpec;KeySpec再转PrivateKey
PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded());
PrivateKey priKey = keyFactory.generatePrivate(priKeySpec);
System.out.println(priKey);
}
  • 输出结果
java.security.KeyPair@78e03bb5
Sun RSA public key, 1024 bits
params: null
modulus: 94134923375030889337699664145116176095803777687781162111756914700229869014912695784710407302811615186395818803402552376808400599961548587586207216709744471870318354813036696801675648731428269930963470277811176883827680414539855481218813862408748594430021606927061565116386180650249935749556615770533203721821
public exponent: 65537
SunRsaSign RSA private CRT key, 1024 bits
params: null
modulus: 94134923375030889337699664145116176095803777687781162111756914700229869014912695784710407302811615186395818803402552376808400599961548587586207216709744471870318354813036696801675648731428269930963470277811176883827680414539855481218813862408748594430021606927061565116386180650249935749556615770533203721821
private exponent: 67868152791098303572124282937222322055125020915630253288684471666171190487123683962152169691286583419399765605089805755591451063493647416931630849589322449230367252892862038338916192807582203337302166911147185956153147905653905702289234855039234840869874793012808454810161546053566242403672442319692325665473

3 摘要算法-MessageDigest和javax.crypto.Mac(HMAC)

  • 单向加密是不可逆的,MD5、SHA、MAC都是属于单向加密算法的一种,也称之为摘要算法
  • MD5、SHA它们会根据明文用哈希算法计算一个固定长度的摘要(哈希值),然后把明文和摘要发送给接收者,接收者根据同样的算法计算出摘要,对比两个摘要是否一样即可验证明文的正确性,它的应用场景是:防止篡改和校验数据
  • MD5、SHA等算法是开源的,容易被试探出来。有没有更安全的摘要算法呢?HMAC-带密钥(密码)的hash函数,用一个密钥和一个明文消息作为输入,生成一个消息摘要。密钥一般使用KeyGenerator创建,相当于一个密码值,其被试探出的概率小
  • MessageDigest支持的算法:MD2、MD5、SHA-1、SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256
  • javax.crypto.Mac支持的算法:HmacMD5、HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384、HmacSHA512、PBEWithHmacSHA1
  • MD5的示例
MessageDigest digest = MessageDigest.getInstance("MD5");
System.out.println(new String(digest.digest("hello world!".getBytes())));
System.out.println(new String(digest.digest("hello world!".getBytes())));
------------输出结果------------------
0���G?�w
0���G?�w
  • MAC的示例
public static void main(String[] args) throws Exception {
// 初始化HmacMD5摘要算法的密钥产生器
KeyGenerator generator = KeyGenerator.getInstance("HmacMD5");
// 产生密钥
SecretKey secretKey = generator.generateKey();
//SecretKeySpec继承于SecretKey和KeySpec,因此可直接用SecretKeySpec初始化Mac
//SecretKey secretKey = new SecretKeySpec("password".getBytes(), "HmacMD5");
Mac mac = Mac.getInstance("HmacMD5");
mac.init(secretKey);
//计算摘要
String data = "hello world";
byte[] result1 = mac.doFinal(data.getBytes());
byte[] result2 = mac.doFinal(data.getBytes());
System.out.println(new String(result1).equals(new String(result2)));
}
------------输出结果------------------
true

4 签名算法工具-Signature

  • 签名算法其实也是加密算法,它加密后的数据具有唯一标识性,就像一个人的签名能代表一个人身份。签名一般是指用非对称加密算法的私钥来加密明文的过程,生成的密文可以被持有公钥的人识别解密,只要你的公钥是准确对应无误的,就能保证你解密的数据是来自持有私钥的一方
  • 如何保证公钥是正确无误,没被篡改的?1:一对一给你,2:获取公钥后通过权威机构认证,相关过程可以看下之前写的一篇文章网络篇:朋友面试之https认证加密过程
  • 支持算法:NONEwithRSA、MD2withRSA、MD5withRSA、SHA512/224withRSA、SHA512/256withRSA、RSASSA-PSS、NONEwithDSA、SHA512withDSA、NONEwithECDSA、SHA512withECDSA、MD5withRSAandMGF1(太多了,选择列举几个)
  • Signature.API示例,配合KeyPairGenerator使用
public static void main(String[] args) throws Exception {
KeyPair keyPair = generatorRsaKey();
Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(keyPair.getPrivate());
//加解密数据
byte[] data = "hello world".getBytes();
//数据签名
signature.update(data);
byte[] digest = signature.sign();
//数据解密加验证
signature.initVerify(keyPair.getPublic());
signature.update(data);
System.out.println("验证结果:"+signature.verify(digest));
}
------------输出结果------------------
验证结果:true

5 常用加密工具类-Cipher

  • 用于加密/解密数据。支持各种类型的算法:对称加密(例如AES),非对称加密(例如RSA)
  • 支持算法:AES、AESWrap、ARCFOUR、Blowfish、DES、DESede、DESedeWrap、ECIES、RSA(太多了,选择列举几个)
  • 示例
public static void main(String[] args) throws Exception {
KeyPair keyPair = generatorRsaKey();
Cipher cipher = Cipher.getInstance("RSA");
// 编码前设定编码方式及密钥
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate());
//加解密数据
byte[] data = "hello world".getBytes();
//数据签名
byte[] enData = cipher.doFinal(data);
//数据解密
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPublic());
byte[] newData = cipher.doFinal(enData);
System.out.println("验证结果:"+new String(newData));
}
------------输出结果------------------
验证结果:hello world

6 Certificate-证书存储

  • CertificateFactory:用于创建公钥证书(Certificate)和证书吊销列表(CRL)
  • Certificate及其子类X509Certificate
  • CertPath和CertPathBuilder:用于构建证书链(也称为证书路径)
  • CertPathValidator:用于验证证书链
  • CRL:证书吊销列表
  • CertStore:用于存储检索证书和CRL
  • CertificateFactory和Certificate的示例
  • 示例
//certificateStream是证书的输入流
public static PublicKey getPublicKeyByCer(InputStream certificateStream) throws Exception{
CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
Certificate certificate = certificateFactory.generateCertificate(certificateStream);
return certificate.getPublicKey();
}

7 KeyStore-密钥证书的实体类

  • KeyStore用于存储私钥和证书(公钥在证书Certificate里面)
  • 公钥:是一个详细的实体的数字关联,并有意让所有想同这个实体发生信任关系的其他实体知道.公共钥匙用来检验签名;
  • 私钥:是一些数字,私有和公共钥匙存在所有用公共钥匙加密的系统的钥匙对中.公共钥匙用来加密数据,私有钥匙用来计算签名.公钥加密的消息只能用私钥解密,私钥签名的消息只能用公钥检验签名。
  • 示例
public static void main(String[] args) throws Exception {
InputStream certificateStream = null;
//根据Certificate生成KeyStore
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null);
keyStore.setCertificateEntry("certificate", certificateFactory.generateCertificate(certificateStream));
//加载jks文件,并生成KeyStore
KeyStore trustKeyStore = KeyStore.getInstance("jks");
FileInputStream trustKeyStoreFile = new FileInputStream("/root/trustKeyStore.jks");
trustKeyStore.load(trustKeyStoreFile, "password".toCharArray());
}

8 java.https加载证书的API

  • KeyManagerFactory、TrustManagerFactory => KeyManager、TrustManager => SSLContext => SSLEngine、SSLSocketFactory、SSLSocket

一般的证书加载过程

  • 用Certificate、KeyStore生成创建KeyManagerFactory和TrustManagerFactory
  • KeyManagerFactory和TrustManagerFactory用来创建KeyManager和TrustManager
  • 而KeyManager和TrustManager用来初始化SSLContext
  • 然后使用SSLContext,创建实际实现SSL/TLS协议的对象(SSLSocketFactory、SSLSocket或者SSLEngine)
  • SSLSocket和SSLEngine可以直接在通信对象中使用
  • KeyManager和TrustManager作用:
    • KeyManager负责向对等端显示使用的凭证(使用的密码标准、加密算法、证书、公钥、签名等)
    • TrustManager负责验证从对等端收到的凭证,验证凭证有多种方式:其中之一是创建CertPath对象,并让JDK的内置公钥基础结构(PKI)框架处理验证。 在内部,CertPath实现可能会创建一个Signature对象,并使用它来验证证书链中的每个签名
  • 示例:生成SSLContext,并使用SSLContext初始化apache-httpClient
public static String postWithSSL(String url, String jsonBody) throws Exception {
SSLContext sslContext = getSslContext();
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
sslContext, new String[]{"TLSv1.2", "TLSv1.1", "TLSv1"}, null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(3000)
.setSocketTimeout(3000)
.build();
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(sslConnectionSocketFactory)
.setDefaultRequestConfig(config).build();
HttpPost httpPost = new HttpPost(url);
//httpPost.setHeaders(headers);
httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
httpPost.setHeader("Accept", "application/json");
httpPost.setEntity(new StringEntity(jsonBody, StandardCharsets.UTF_8));
HttpResponse response = client.execute(httpPost);
HttpEntity responseEntity = response.getEntity();
String result = EntityUtils.toString(responseEntity, "UTF-8");
return result;
}
//双向加密 SSLContext
private static SSLContext getSslContext() throws Exception {
//自身私钥
KeyStore identityKeyStore = KeyStore.getInstance("jks");
FileInputStream identityKeyStoreFile = new FileInputStream("/root/myServer.jks");
identityKeyStore.load(identityKeyStoreFile, "password1".toCharArray());
//服务端信任证书
KeyStore trustKeyStore = KeyStore.getInstance("jks");
FileInputStream trustKeyStoreFile = new FileInputStream("/root/trustKeyStore.jks");
trustKeyStore.load(trustKeyStoreFile, "password".toCharArray());
//构建SSLContexts
return SSLContexts.custom()
.loadKeyMaterial(identityKeyStore, "password1".toCharArray()) // load identity keystore
.loadTrustMaterial(trustKeyStore, null) // load trust keystore
.build();
}
//双向加密 SSLContext 方式二
private static SSLContext getSslContext2() throws Exception{
//自身私钥
KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore keystore = KeyStore.getInstance("jks");
keystore.load(new FileInputStream(new File("/root/myServer.jks")), "password".toCharArray());
keyFactory.init(keystore, "password".toCharArray());
KeyManager[] keyManagers = keyFactory.getKeyManagers();
//服务端信任证书
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance("SunX509");
KeyStore tsStore = KeyStore.getInstance("jks");
tsStore.load(new FileInputStream(new File("/root/trustKeyStore.jks")), "password".toCharArray());
trustFactory.init(tsStore);
TrustManager[] trustManagers = trustFactory.getTrustManagers();
//初始化SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
return sslContext;
}

欢迎指正文中错误

参考文章

基础篇:java.security框架之签名、加密、摘要及证书的更多相关文章

  1. HTML基础篇之内嵌框架和表单

    内嵌框架: <iframe src="http://www.baidu.com" name="d"></iframe> 网页会显示一个这 ...

  2. Eclipse插件开发之基础篇(4) OSGi框架

    转载出处:http://www.cnblogs.com/liuzhuo. 1. 什么是OSGi框架 OSGi(Open Service Gateway Initiative)框架是运行在JavaVM环 ...

  3. 基础篇-java开发

    开局必知 1.变量 在java中,以{}为作用域,所以就存在成员变量和局部变量之说 由于java是强类型语言,所以在申明变量的时候,必须指定类型 java里,一个变量有声明过程和初始化过程(也就是赋值 ...

  4. C#基础篇之语言和框架介绍

    1.如何描述C#和.NET的关系? .Net的是平台,C#是为了微软公司为了.NET平台开发的面向对象语言. 2.C#能做什么? (1)C#.NET做窗体应用开发,Web开发中可以通过WCF编写Web ...

  5. java学习笔记(基础篇)--java关键字与数据类型

    java关键字与数据类型 Java语言的关键字是程序代码中的特殊字符.包括: . 类和接口的声明--class, extends, implements, interface . 包引入和包声明--i ...

  6. java学习笔记(基础篇)—java数组

    一:什么是数组,什么时候使用数组? 数组是用来保存一组数据类型相同的元素的有序集合,数组中的每个数据称为元素.有序集合可以按照顺序或者下标取数组中的元素. 在Java中,数组也是Java对象.数组中的 ...

  7. [转]Eclipse插件开发之基础篇(4) OSGi框架

    原文地址:http://www.cnblogs.com/liuzhuo/archive/2010/08/18/eclipse_plugin_1_2_1.html 1. 什么是OSGi框架 OSGi(O ...

  8. Java学习 (四)基础篇 Java基础语法

    注释&标识符&关键字 注释 注释并不会被执行,其主要目的用于解释当前代码 书写注释是一个非常好的习惯,大厂要求之一 public class hello { public static ...

  9. android基础篇------------java基础(12)(多线程操作)

    <一>基本概念理解 1.什么是进程? 进程就是在某种程度上相互隔离,独立运行的程序.一般来说,系统都是支持多进程操作的,这所谓的多进程就是让系统好像同时运行多个程序. 2.什么是线程呢? ...

随机推荐

  1. codeforces 1009D Relatively Prime Graph【欧拉函数】

    题目:戳这里 题意:要求构成有n个点,m条边的无向图,满足每条边上的两点互质. 解题思路: 显然1~n这n个点能构成边的条数,就是2~n欧拉函数之和(x的欧拉函数值代表小于x且与x互质的数的个数. 因 ...

  2. 容器之List接口下各实现类(Vector,ArrayList 和LinkedList)的线程安全问题

    Vector .ArrayList 和LinkedList都是List接口下的实现类,但是他们之间的区别和联系是什么呢? 首先: 然后: 如果您仅仅想知道结论,那么可以关闭了. 下面我讨论讨论为什么. ...

  3. 牛客多校第八场E Explorer(左开右闭线段树+可撤回并查集)题解

    题意: 传送门 有\(n\)个点构成一个无向图,每条边有\(L_i,R_i\)表示这条边只能允许编号为\(L_i\dots R_i\)的人通过,现在问你最多有几个人能从\(1\)走到\(n\). 思路 ...

  4. WSL2+Terminal+VScode配置调试

    最近几天一直想找个方法把VMware虚拟机和远程连接工具MobaXterm这一组配合替换掉,因为每次开启虚拟机操作Ubuntu都需要占用很大的内存,而且要等好久好久才能开启!!!后面还要使用MobaX ...

  5. 读写 LED 作业 台灯的 频闪研究1

    读写 LED 作业 台灯的 频闪研究: 核心提示: 随着科技的持续发展,目前已经商业化的照明产品从第一代的白炽灯: 第二代的荧光灯.卤灯: 第三代的高强度气体放电灯; 以及当下主流的, 第四代的发光二 ...

  6. project generators & project scaffold

    project generators & project scaffold how to write a node cli & Project Scaffold https://www ...

  7. uniapp 滑动切换

    说明:本案例的样式基于colorui组件库 感兴趣的小伙伴可以看下教程 colorui组件库开发文档或者csdn的文档,顺便再分享下 colorui的群资源 最近项目中需要用到滑动切换的效果,自己懒得 ...

  8. Java HashMap源码分析(含散列表、红黑树、扰动函数等重点问题分析)

    写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...

  9. dotnet core TargetFramework 解析顺序测试

    dotnet core TargetFramework 解析顺序测试 Intro 现在 dotnet 的 TargetFramework 越来越多,抛开 .NET Framework 不谈,如果一个类 ...

  10. TkMybatis添加对象后返回数据的id

    在实体类的id属性上加上下面的注解 //导入的包import javax.persistence.GeneratedValue; @GeneratedValue(generator = "J ...