共计 13549 个字符,预计需要花费 34 分钟才能阅读完成。
前言
和前端进行数据交互时或者和第三方商家对接时,须要对隐衷数据进行加密。单向加密,对称加密,非对称加密,其对应的算法也各式各样。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; | |
} |
欢送指注释中谬误
参考文章
- JCA-Java 加密框架
- Java 加密框架 (JCA) 简要阐明
- Java 加密解密之 MAC
- 对于 keyGenerator,KeyPairGenerator,SecretKeyFactory 的解析
- JCA 实际记录——SecretKeyFactory
- HttpClient 双向认证
- java 内置可用加密算法文档
- key 解析
正文完