主要是为了在网络间更方便的传输数据/本地存储字节数组而产生
由A-Z、a-z、0-9、+、/共64个字符组成,去掉i、I、o、O、+、/即base58
注意:base64以三个字节为一组,如果最后一组不足三个字节,则使用=号补充
案例:
package com.yypttest.Utils;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Base64Test {
//指定字符集
private static final String UTF8 = StandardCharsets.UTF_8.name();
//使用jdk原生来实现base64
@Test
public void test1() throws Exception {
String str = "base64测试";
//getEncoder:编码
//jdk1.8之后才能使用
String encode = Base64.getEncoder().encodeToString(str.getBytes(UTF8));
System.out.println("encodeStr: "+encode);
byte[] decode = Base64.getDecoder().decode(encode.getBytes(UTF8));
System.out.println("decoder: "+new String(decode,UTF8));
}
//使用commons-codec来实现base64
@Test
public void test2() throws Exception {
String str = "使用commons-codec来实现base64";
//getEncoder:编码
//jdk1.8之后才能使用
String encodeStr = org.apache.commons.codec.binary.Base64.encodeBase64String(str.getBytes(UTF8));
System.out.println("encodeStr: "+encodeStr);
byte[] decodeStr = org.apache.commons.codec.binary.Base64.decodeBase64(encodeStr.getBytes(UTF8));
System.out.println("decoder: "+new String(decodeStr,UTF8));
}
}
application/x-www-from-urlencoded
前端默认使用这种方式编码传递到后端
测试案例:
package com.yypttest.Utils;
import org.junit.Test;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class UrlTest {
//指定字符集
private static final String UTF8 = StandardCharsets.UTF_8.name();
@Test
public void test1() throws Exception{
String str = "测试applicationxwwfurlened";
String encode = URLEncoder.encode(str,UTF8);
System.out.println("编码后:"+encode);
String decode = URLDecoder.decode(encode, UTF8);
System.out.println("解码后:"+decode);
}
}
只对中文进行编码
又叫Hash算法、散列函数、数字摘要、消息摘要。它是一种单向算法,用户可以通过hash算法对目标信息生成一段特定长度的唯一hash值,但不能通过这个hash值重新获得目标信息。
package com.yypttest.Utils;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
public class MD5Test {
//使用jdk原生api实现md5
//指定字符集
private static final String UTF8 = StandardCharsets.UTF_8.name();
@Test
public void test1() throws Exception{
String str = "使用jdk原生api实现md5";
String algorithm = "MD5";
//获取消息摘要算法对象
MessageDigest md = MessageDigest.getInstance(algorithm);
//获取原始内容的自己数组
byte[] bytes = str.getBytes(UTF8);
//获取到摘要结果,这里的结果还是字节数
byte[] digest = md.digest(bytes);
// 当digest比较大的时候,循环的进行update()
// md.update(bytes);
// md.digest();
/**
* MD5:Message-Digest Algorithm,结果占128位==>16个byte
* 每个字节占8位2进制数,每四位二进制可转换为一个十六进制数,所以这里16个字节就可以用32位十六进制数表示。
* 把每一个字节转为16进制字符,最终再来拼接起来这些16进制字符
*/
String hexStr = convertBytes2HexStr(digest);
System.out.println(hexStr);
}
/*
* 把字节数组转为16进制字符串,如果一个字节转为16进制字符后不足两位,则前面补0
* */
private String convertBytes2HexStr(byte[] digest) {
StringBuilder buffer = new StringBuilder();
for (byte b:digest){
//获取b的补码后8位
String hex = Integer.toHexString(((int)b)&0xff);
if (hex.length()==1){
hex="0"+hex;
}
buffer.append(hex);
}
return buffer.toString();
}
}
/**
* 把16进制字符串(这里的字符串一定是偶数,因为在convertBytes2HexStr中已经处理过了)转为字节数组
* @param hexStr 16进制字符串
* @return 字节数组
*/
public static byte[] convertHex2Btyes(String hexStr){
//一个字符可以转为2个16进制字符
int length = hexStr.length()/2;
//16进制的个数
byte[] result = new byte[length];
for (int i = 0; i < length; i++) {
//假如:hexStr=abcd
//Integer.parseInt:将指定的进制数转成10进制数(参数1:指定字符,参数2:字符的是什么进制数)
//获取每个字节的高4位二进制数,也就是第一位16进制数,substring指定字符串范围截取字符串,含头不含尾(]
int high4 = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
//获取每个字节的低4位二进制数,也就是第二位16进制数,substring指定字符串范围截取字符串,含头不含尾(]
int low4 = Integer.parseInt(hexStr.substring(i * 2+1, i * 2 + 2), 16);
result[i] = (byte) (high4*16+low4);
}
return result;
}
@Test
public void test() throws DecoderException {
String hexStr = "abcd";
byte[] hex2Btyes = convertHex2Btyes(hexStr);
System.out.println(Arrays.toString(hex2Btyes));
//使用codec
System.out.println(Arrays.toString(Hex.decodeHex(hexStr)));
}
//import org.apache.commons.codec.digest.*;
//使用commons-codec实现md5
@Test
public void test2() throws Exception{
String str = "使用commons-codec实现md5";
System.out.println(DigestUtils.md5DigestAsHex(str.getBytes(UTF8)));
}
package com.yypttest.Utils;
import org.junit.Test;
import org.apache.commons.codec.digest.*;
import static com.alibaba.fastjson.util.IOUtils.UTF8;
public class Sha256 {
@Test
public void test1(){
String str = "使用commons-codec实现sha256";
System.out.println(DigestUtils.sha1Hex(str.getBytes(UTF8)));
}
}
mac摘要和digest算法(MD5,sha)不同的地方就是加了盐
package com.yypttest.Utils;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.junit.Test;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
public class MacTest {
//指定字符集
private static final String UTF8 = StandardCharsets.UTF_8.name();
/**
* 获取mac的消息摘要
* @param originalContent 原始内容
* @param key mac算法的key
* @param algorithm 算法名称,如HmacMD5
* @return
*/
public static String doMacDigest(String originalContent,String key,String algorithm){
try {
//获取消息摘要算法对象
Mac mac = Mac.getInstance(algorithm);
//获取key对象并初始化mac
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(UTF8), algorithm);
//初始化
mac.init(secretKeySpec);
//获取原始内容的字节数组
byte[] bytes = originalContent.getBytes(UTF8);
//获取到摘要结果
byte[] doFinal = mac.doFinal(bytes);
//把每一个字节转换位16进制字符,最终在拼接成16进制
MD5Test md5Test = new MD5Test();
return md5Test.convertBytes2HexStr(doFinal);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
@Test
public void test1() throws Exception{
String str = "使用jdk原始api实现mac";
//指定密钥,mac摘要和digest算法(MD5,sha)不同的地方就是加了盐
String key = "123";
String algorithm = "HmacMD5";
String macMD5Digest = doMacDigest(str, key, algorithm);
System.out.println("macMD5Digest: "+macMD5Digest);
String str1 = "使用jdk原始api实现mac";
//指定密钥,mac摘要和digest算法(MD5,sha)不同的地方就是加了盐
String key1 = "123";
String algorithm1 = "HmacSHA256";
String macSHA265Digest = doMacDigest(str1, key1, algorithm1);
System.out.println("macSHA265Digest: "+macSHA265Digest);
String str2 = "使用jdk原始api实现mac";
//指定密钥,mac摘要和digest算法(MD5,sha)不同的地方就是加了盐
String key2 = "123";
String algorithm2 = "HmacSHA512";
String macSHA512Digest = doMacDigest(str2, key2, algorithm2);
System.out.println("macSHA512Digest: "+macSHA512Digest);
}
//使用codec实现mac
@Test
public void test2() throws UnsupportedEncodingException {
String str2 = "使用codec实现mac";
String key2 = "123";
String macMD5 = new HmacUtils(HmacAlgorithms.HMAC_MD5, key2.getBytes(UTF8)).hmacHex(str2.getBytes(UTF8));
String macSHA256 = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, key2.getBytes(UTF8)).hmacHex(str2.getBytes(UTF8));
String macSHA512 = new HmacUtils(HmacAlgorithms.HMAC_SHA_512, key2.getBytes(UTF8)).hmacHex(str2.getBytes(UTF8));
System.out.println("macMD5: "+macMD5);
System.out.println("macSHA256: "+macSHA256);
System.out.println("macSHA512: "+macSHA512);
}
}
也叫单密钥加密,所谓单密钥,指的是加密和解密的过程使用相同的密钥,相比非对称加密,因只有一把钥匙,因而速度更快,更适合加解密大文件
定义: electronic code book,电码本模式,将整个明文分成若干段相同的小段,然后对每一小段进行加密。特点: 每段之间互不依赖,可以并行处理;同样的明文总是生成同样的密文
定义: cipher block chaining,密文分组链模式,所谓链,即密文分组之间像链条一样相与连接在一起。先将明文切分成若干小段,然后每一小段与上一段的密文段(第一个块因没有上一个密文段,使用的是IV进行运算后,再与密钥进行加密
特点: 串行处理,同样的明文每次生成的密文不一样。
package com.yypttest.Utils;
import org.apache.commons.codec.binary.Base64;
import org.junit.Test;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
public class DesTest {
//算法类型
private static final String ALGORITHM = "DES";
//密钥 DES的秘钥默认是8位秘钥
private static final String KEY = "12345677";
//字符集
private static final String UTF8 = StandardCharsets.UTF_8.name();
/**
* 加密
* @param text 待加密的内容
* @return
*/
public String encrypt(String text) throws Exception{
//获取实例
Cipher instance = Cipher.getInstance(ALGORITHM);
/*
创建加解密的规则
*/
SecretKey secretKey = new SecretKeySpec(KEY.getBytes(UTF8), ALGORITHM);
/*
初始化
第一个参数:加解密模式(加密模式)
第二个参数:规则
*/
instance.init(Cipher.ENCRYPT_MODE,secretKey);
//获取加密数组
byte[] encodedBytes = instance.doFinal(text.getBytes(UTF8));
//展示字节数组,1:使用base64,2:使用转成16进制字符串
//这里使用base64
return Base64.encodeBase64String(encodedBytes);
}
/**
* 解密
* @param text 加密后的字符串
* @return
* @throws Exception
*/
public String decrypt(String text) throws Exception{
//获取实例
Cipher instance = Cipher.getInstance(ALGORITHM);
/*
创建加解密的规则
*/
SecretKey secretKey = new SecretKeySpec(KEY.getBytes(UTF8), ALGORITHM);
/*
初始化
第一个参数:加解密模式(解密模式)
第二个参数:规则
*/
instance.init(Cipher.DECRYPT_MODE,secretKey);
byte[] decryptedBytes = instance.doFinal(Base64.decodeBase64(text.getBytes(UTF8)));
return new String(decryptedBytes,UTF8);
}
@Test
public void test()throws Exception{
String str = "对称加密DES模式测试";
String encrypt = encrypt(str);
String decrypt = decrypt(encrypt);
System.out.println("加密后的结果:"+encrypt);
System.out.println("解码后的结果:"+decrypt);
}
}
package com.yypttest.Utils;
import org.apache.commons.codec.binary.Base64;
import org.junit.Test;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
public class AesTest {
//算法类型
private static final String ALGORITHM = "AES";
//密钥,AES的秘钥规定是16、24、32位秘钥
private static final String KEY = "12345678qwerasdf";
//字符集
private static final String UTF8 = StandardCharsets.UTF_8.name();
/**
* 加密
* @param text 待加密的内容
* @return
*/
public String encrypt(String text) throws Exception{
//获取实例
Cipher instance = Cipher.getInstance(ALGORITHM);
/*
创建加解密的规则
*/
SecretKey secretKey = new SecretKeySpec(KEY.getBytes(UTF8), ALGORITHM);
/*
初始化
第一个参数:加解密模式(加密模式)
第二个参数:规则
*/
instance.init(Cipher.ENCRYPT_MODE,secretKey);
//获取加密数组
byte[] encodedBytes = instance.doFinal(text.getBytes(UTF8));
//展示字节数组,1:使用base64,2:使用转成16进制字符串
//这里使用base64
return MD5Test.convertBytes2HexStr(encodedBytes);
}
/**
* 解密
* @param text 加密后的字符串
* @return
* @throws Exception
*/
public String decrypt(String text) throws Exception{
byte[] bytes = MD5Test.convertHex2Btyes(text);
//获取实例
Cipher instance = Cipher.getInstance(ALGORITHM);
/*
创建加解密的规则
*/
SecretKey secretKey = new SecretKeySpec(KEY.getBytes(UTF8), ALGORITHM);
/*
初始化
第一个参数:加解密模式(解密模式)
第二个参数:规则
*/
instance.init(Cipher.DECRYPT_MODE,secretKey);
byte[] decryptedBytes = instance.doFinal(bytes);
return new String(decryptedBytes,UTF8);
}
@Test
public void test()throws Exception{
String str = "对称加密AES模式,采用16进制编码测试";
String encrypt = encrypt(str);
String decrypt = decrypt(encrypt);
System.out.println("加密后的结果:"+encrypt);
System.out.println("解码后的结果:"+decrypt);
}
}
package com.yypttest.Utils;
import org.junit.Test;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
public class AesTest {
//算法类型
private static final String ALGORITHM = "AES";
//密钥
private static final String KEY = "12345678qsdf";
//字符集
private static final String UTF8 = StandardCharsets.UTF_8.name();
/**
* 加密
* @param text 待加密的内容
* @return
*/
public String encrypt(String text) throws Exception{
//获取实例
Cipher instance = Cipher.getInstance(ALGORITHM);
/*
解决aes和des默认固定key的字符长度问题
*/
//创建keyGenerator,可以根据传入的key,生成一个指定长度的key
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
//初始化SecureRandom,指定生成key的算法
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
//指定原始传入的key
secureRandom.setSeed(KEY.getBytes(UTF8));
//指定生成新的key的长度
keyGenerator.init(128,secureRandom);
//通过keyGenerator生成原始key
SecretKey secretKey = keyGenerator.generateKey();
//获取到新密钥的字节数组
byte[] encoded = secretKey.getEncoded();
/*
创建加解密的规则
*/
SecretKey secretKeySpec = new SecretKeySpec(encoded, ALGORITHM);
/*
初始化
第一个参数:加解密模式(加密模式)
第二个参数:规则
*/
instance.init(Cipher.ENCRYPT_MODE,secretKeySpec);
//获取加密数组
byte[] encodedBytes = instance.doFinal(text.getBytes(UTF8));
//展示字节数组,1:使用base64,2:使用转成16进制字符串
//这里使用base64
return MD5Test.convertBytes2HexStr(encodedBytes);
}
/**
* 解密
* @param text 加密后的字符串
* @return
* @throws Exception
*/
public String decrypt(String text) throws Exception{
byte[] bytes = MD5Test.convertHex2Btyes(text);
//获取实例
Cipher instance = Cipher.getInstance(ALGORITHM);
/*
解决aes和des默认固定key的字符长度问题
*/
//创建keyGenerator,可以根据传入的key,生成一个指定长度的key
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
//初始化SecureRandom,指定生成key的算法
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
//指定原始传入的key
secureRandom.setSeed(KEY.getBytes(UTF8));
//指定生成新的key的长度
keyGenerator.init(128,secureRandom);
//通过keyGenerator生成原始key
SecretKey secretKey = keyGenerator.generateKey();
//获取到新密钥的字节数组
byte[] encoded = secretKey.getEncoded();
/*
创建加解密的规则
*/
SecretKey secretKeySpec = new SecretKeySpec(encoded, ALGORITHM);
/*
初始化
第一个参数:加解密模式(解密模式)
第二个参数:规则
*/
instance.init(Cipher.DECRYPT_MODE,secretKeySpec);
byte[] decryptedBytes = instance.doFinal(bytes);
return new String(decryptedBytes,UTF8);
}
@Test
public void test()throws Exception{
String str = "对称加密AES模式,采用16进制编码测试,key可以任意长度";
String encrypt = encrypt(str);
String decrypt = decrypt(encrypt);
System.out.println("加密后的结果:"+encrypt);
System.out.println("解码后的结果:"+decrypt);
}
}
为什么要有?对于固定的加密算法,每个块有固定大小(BlockSize),比如8个byte,明文分块后,加密前需要保证对最后一个块的大小为8个byte,如果不够则使用特定数据进行填充。
package com.yypttest.Utils;
import org.junit.Test;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
/**
* @Author Jiangjinlong
* @Date 2023/3/14 11:12
* @PackageName:com.yypttest.Utils
* @ClassName: AesTestTwo
* @Description: 测试加密模式和填充模式
*/
public class AesTestTwo {
/*
AES加密默认的加密默认就是:ECB,默认的填充模式就是:PKCS5Padding
*/
/*
AES使用CBC加密模式时需要传入IV
*/
//算法类型
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String ALGORITHM_TYPE = "AES";
//密钥
private static final String KEY = "12345678qsdf";
//定义IV,默认规定16个字节
private static final String IV = "12345678qsdfasdf";
//字符集
private static final String UTF8 = StandardCharsets.UTF_8.name();
/**
* 加密
* @param text 待加密的内容
* @return
*/
public String encrypt(String text) throws Exception{
//获取实例
Cipher instance = Cipher.getInstance(ALGORITHM);
/*
解决aes和des默认固定key的字符长度问题
*/
//创建keyGenerator,可以根据传入的key,生成一个指定长度的key
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_TYPE);
//初始化SecureRandom,指定生成key的算法
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
//指定原始传入的key
secureRandom.setSeed(KEY.getBytes(UTF8));
//指定生成新的key的长度
keyGenerator.init(128,secureRandom);
//通过keyGenerator生成原始key
SecretKey secretKey = keyGenerator.generateKey();
//获取到新密钥的字节数组
byte[] encoded = secretKey.getEncoded();
/*
创建加解密的规则
*/
SecretKey secretKeySpec = new SecretKeySpec(encoded, ALGORITHM_TYPE);
IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes(UTF8));
/*
初始化
第一个参数:加解密模式(加密模式)
第二个参数:规则
第三个参数:使用CBC加密模式时需要多传一个ivParameterSpec向量
*/
instance.init(Cipher.ENCRYPT_MODE,secretKeySpec,ivParameterSpec);
//获取加密数组
byte[] encodedBytes = instance.doFinal(text.getBytes(UTF8));
//展示字节数组,1:使用base64,2:使用转成16进制字符串
//这里使用base64
return MD5Test.convertBytes2HexStr(encodedBytes);
}
/**
* 解密
* @param text 加密后的字符串
* @return
* @throws Exception
*/
public String decrypt(String text) throws Exception{
byte[] bytes = MD5Test.convertHex2Btyes(text);
//获取实例
Cipher instance = Cipher.getInstance(ALGORITHM);
/*
解决aes和des默认固定key的字符长度问题
*/
//创建keyGenerator,可以根据传入的key,生成一个指定长度的key
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_TYPE);
//初始化SecureRandom,指定生成key的算法
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
//指定原始传入的key
secureRandom.setSeed(KEY.getBytes(UTF8));
//指定生成新的key的长度
keyGenerator.init(128,secureRandom);
//通过keyGenerator生成原始key
SecretKey secretKey = keyGenerator.generateKey();
//获取到新密钥的字节数组
byte[] encoded = secretKey.getEncoded();
/*
创建加解密的规则
*/
SecretKey secretKeySpec = new SecretKeySpec(encoded, ALGORITHM_TYPE);
IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes(UTF8));
/*
初始化
第一个参数:加解密模式(解密模式)
第二个参数:规则
第三个参数:使用CBC加密模式时需要多传一个ivParameterSpec向量
*/
instance.init(Cipher.DECRYPT_MODE,secretKeySpec,ivParameterSpec);
byte[] decryptedBytes = instance.doFinal(bytes);
return new String(decryptedBytes,UTF8);
}
@Test
public void test()throws Exception{
String str = "对称加密AES模式,采用16进制编码测试";
String encrypt = encrypt(str);
String decrypt = decrypt(encrypt);
System.out.println("加密后的结果:"+encrypt);
System.out.println("解码后的结果:"+decrypt);
}
}
加密和解密使用的是两个不同的密钥 (public key 和 private key)。公钥可以给任何人,私钥总是自己保留。
对称加解密使用相同的秘钥,但对不同的原始内容加密会采用不同的秘钥,导致秘钥数量巨大,难以维护。
加解密
可以使用公钥加密,对应的就是私钥解密;也可以使用私钥加密,对应的公钥解密
package com.yypttest.Utils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* @Author JiangJinlong
* @Date 2023/3/14 13:22
* @PackageName:com.yypttest.Utils
* @ClassName: RsaTest
* @Description: TODO
*/
public class RsaTest {
private static final String ALGORITHM = "RSA";
private static final String UTF8 = StandardCharsets.UTF_8.name();
private static final String publicKeyPath = "E:\\Work_file\\YyptTest\\src\\main\\resources\\rsa.pub";
private static final String privateKeyPath = "E:\\Work_file\\YyptTest\\src\\main\\resources\\rsa.pri";
/**
* rsa单次最大加密的文明大小
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/**
* rsa单次最大解密的密文大小
*/
private static final int MAX_DECRYPT_BLOCK = 128;
//获取公私钥文件位置,怀疑路径不能含中文
/*
private static final String publicKeyPath;
private static final String privateKeyPath;
static {
ClassLoader cl = RsaTest.class.getClassLoader();
publicKeyPath = cl.getResource("rsa.pub").getPath();
privateKeyPath = cl.getResource("rsa.pri").getPath();
System.out.println("公钥路径:"+publicKeyPath);
System.out.println("私钥路径:"+privateKeyPath);
}*/
/**
* 生成经过base64编码后的密钥对(公钥和私钥)并存储在文件中
*/
private void writeKey2File() throws Exception{
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
keyPairGenerator.initialize(1024);
//通过keyPair生成器生成keyPair
KeyPair keyPair = keyPairGenerator.generateKeyPair();
/*
公钥
*/
PublicKey publicKey = keyPair.getPublic();
//把对象转成字节数组
byte[] encoded = publicKey.getEncoded();
String publicKeyBase64Stri = Base64.encodeBase64String(encoded);
/*
私钥
*/
PrivateKey privateKey = keyPair.getPrivate();
//把对象转成字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
String privateKeyBase64Stri = Base64.encodeBase64String(privateKeyEncoded);
//分别把公钥字符串和私钥字符串写入文件
FileUtils.writeStringToFile(new File(publicKeyPath),publicKeyBase64Stri,UTF8);
FileUtils.writeStringToFile(new File(privateKeyPath),privateKeyBase64Stri,UTF8);
}
/**
* 从生成好的公钥文件rsa.pub(这里的公钥文件是通过Base64编码之后存储的数据)获取公钥对象
* @return
*/
private PublicKey getPulickey()throws Exception{
//读取通过Base64编码之后的公钥文件
String publicKeyBase64Str = FileUtils.readFileToString(new File(publicKeyPath), UTF8);
//base64解码,获取字节数组
byte[] decodeBase64 = Base64.decodeBase64(publicKeyBase64Str);
//传入算法名字”RAS“
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
//传入key的规则,对于公钥的规则就是x509,将公钥的字符串传入公钥规则里面
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(decodeBase64);
return keyFactory.generatePublic(x509EncodedKeySpec);
}
/**
* 从生成好的私钥文件rsa.pri(这里的公钥文件是通过Base64编码之后存储的数据)获取私钥对象
* @return
*/
private PrivateKey getPrivateKey()throws Exception{
//读取通过Base64编码之后的公钥文件
String privateKeyBase64Str = FileUtils.readFileToString(new File(privateKeyPath), UTF8);
//base64解码,获取字节数组
byte[] decodeBase64 = Base64.decodeBase64(privateKeyBase64Str);
//传入算法名字”RAS“
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
//传入key的规则,对于私钥的规则就是PKCS8,将公钥的字符串传入公钥规则里面
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decodeBase64);
return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
}
/**
* 加密
* @param originalCont 原始内容
* @param key 公钥或者私钥
* @return base64编码后的加密内容
* @throws Exception
*/
public String encrypt(String originalCont, Key key) throws Exception{
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE,key);
byte[] bytes = doCodec(cipher,originalCont.getBytes(UTF8),MAX_ENCRYPT_BLOCK);
return Base64.encodeBase64String(bytes);
}
/**
* 解密
* @param encryptedStr 加密后内容
* @param key 公钥或者私钥
* @return 原始内容
* @throws Exception
*/
public String decrypt(String encryptedStr, Key key) throws Exception{
byte[] decodeBase64 = Base64.decodeBase64(encryptedStr);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE,key);
byte[] decryptedBytes = doCodec(cipher,decodeBase64,MAX_DECRYPT_BLOCK);
return new String(decryptedBytes);
}
/**
* 执行加密或者解密
* @param cipher
* @param decodeBase64
* @param maxDecryptBlock
* @return
*/
private byte[] doCodec(Cipher cipher, byte[] decodeBase64, int maxDecryptBlock) throws Exception{
//获取字节长度
int inputLen = decodeBase64.length;
//迁移量
int offset = 0;
//本次完成加密之后的数组
byte[] cache;
//循环的次数
int i= 0;
//
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((inputLen-offset)>0){
if ((inputLen - offset)>maxDecryptBlock){
//第三个参数,要处理的长度
cache = cipher.doFinal(decodeBase64,offset,maxDecryptBlock);
}else {
cache = cipher.doFinal(decodeBase64,offset,inputLen-offset);
}
baos.write(cache,0,cache.length);
i++;
offset = i*maxDecryptBlock;
}
//加密或者解密的结果
byte[] bytes = baos.toByteArray();
baos.close();
return bytes;
}
/*
生成公钥私钥,密钥对每次生成的都是不一样的,所以我们执行一次之后保存起来,其他时候就不需要在执行了
*/
@Test
public void testWriteKey() throws Exception{
writeKey2File();
}
@Test
public void testRsa()throws Exception{
String str = "测试公钥加密————私钥解密";
//测试公钥加密————私钥解密
System.out.println("公钥加密结果:"+encrypt(str, getPulickey()));
System.out.println("私钥解密结果:"+decrypt(encrypt(str, getPulickey()), getPrivateKey()));
// 测试私钥加密————公钥解密
String str1 = "测试私钥加密————公钥解密";
System.out.println("私钥加密结果:"+encrypt(str1, getPrivateKey()));
System.out.println("公钥解密结果:"+decrypt(encrypt(str1, getPrivateKey()), getPulickey()));
}
}
数字签名
发送方A: 原始内容-.->通过摘要算法获取原始内容的摘要str1–>把hash用发送方的私钥加密–>数字签名。
接收方B: 用发送方的公钥验证签名并解密—>对原始内容进行摘要算法str2–.>比较str1==str2(保证未被篡改)
注意:不是用公钥加密hash,如果用公钥,别人没你的私钥,怎么验证呢?
因为签名是先进行摘要再进行rsa,所以在摘要算法一定的情况下,签名后得到的字符串长度总是一样的
package com.yypttest.Utils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* @Author JiangJinlong
* @Date 2023/3/14 13:22
* @PackageName:com.yypttest.Utils
* @ClassName: RsaTest
* @Description: TODO
*/
public class SignatureTest {
private static final String SIGNATURE_ALGORITHM = "sha256withrsa";
@Test
public void testRsa()throws Exception{
String str = "测试数字签名SignatureTest";
byte[] strBytes = str.getBytes();
String sign = sign(strBytes);
System.out.println("签名:"+sign);
//校验
boolean verify = verify(strBytes, sign);
System.out.println("验证结果:"+verify);
}
/**
* 对消息使用私钥生成数字签名
* @param data 原始数据
* @return
* @throws Exception
*/
private static String sign(byte[] data)throws Exception{
//获取私钥
PrivateKey privateKey = new RsaTest().getPrivateKey();
//用指定的算法初始化签名对象,先进行摘要再进行加密
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateKey);
signature.update(data);
return MD5Test.convertBytes2HexStr(signature.sign());
}
/**
* 校验数字签名
* @param data 原始数据
* @param sign 数字签名
* @return
* @throws Exception
*/
private static boolean verify(byte[] data,String sign) throws Exception{
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(new RsaTest().getPulickey());
signature.update(data);
return signature.verify(MD5Test.convertHex2Btyes(sign));
}
}
数字信封
对称加密的秘钥分发不安全–>发送方用接收方的公钥进行加密–>接受方用私钥再解开
注意:为什么会有分发秘钥呢? 不是约定好了吗? 结合我们前面的讲解,秘钥会着每个零件的数期一起发送给接收方。
数字证书