密码学
编码算法
不是加密和解密,是为了在网络间更方便的传输数据/本地存储字节数组而产生。
base64
base64:由A-Z,A-Z,0-9,+,/共64个字符组成,去掉iIoO +/ Base58
base64以三个字节为一组,如果最后一组不足3个字节,使用=补充
1.jdk1.8之后提供原生实现base64
import java.util.Base64;
@Test
public void test1() throws Exception {
String str = "校验数据";
//编码
String encode = Base64.getEncoder().encodeToString(str.getBytes());
System.out.println("encode:"+encode);
//解码
byte[] decode = Base64.getDecoder().decode(encode.getBytes(StandardCharsets.UTF_8.name()));
System.out.println("decode:"+new String(decode));
}
打印结果:
encode:5qCh6aqM5pWw5o2u
decode:校验数据
2.使用commons工具类实现
导入依赖
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
import org.apache.commons.codec.binary.Base64;
@Test
public void test2() throws Exception {
String str = "校验数据";
//编码
String encode = Base64.encodeBase64String(str.getBytes(StandardCharsets.UTF_8.name()));
System.out.println("encode:"+encode);
//解码
byte[] decode = Base64.decodeBase64(encode.getBytes(StandardCharsets.UTF_8.name()));
System.out.println("decode:"+new String(decode));
}
打印结果同上
url编码
application/x-www-form-urlencoded 前端默认编码对应后端URL编码
@Test
public void test1() throws Exception{
String str = "校验数据cc";
//编码
String encode = URLEncoder.encode(str, StandardCharsets.UTF_8.name());
System.out.println("encode:"+encode);
//解码
String decode = URLDecoder.decode(encode, StandardCharsets.UTF_8.name());
System.out.println("decode:"+decode);
}
结果打印
encode:%E6%A0%A1%E9%AA%8C%E6%95%B0%E6%8D%AEcc
decode:校验数据cc
摘要算法(不可逆)
定义: Hash算法,散列函数,数字摘要,消息摘要,是一种单向算法,用户可以通过Hash算法对目标信息生成一段特定长度的唯一Hash值,但不能通过这个hash值重新获得目标信息
应用场景: 密码,信息完整性校验,数字签名
常见算法: MD5(结果占128位==>16byte),SHA安全散列算法(sha256,sha512),MAC:消息认证码,是一种带有秘钥的hash函数,其他MD2,MD4,HAVAL
工具类
字节数组和十六进制的互相转换
原生API实现
public class HexUtils {
/**
* 把字节数组转为16进制字符串,如果一个字节转为16进制字符后不足两位,前面补0
* toHexString:把字节(转化为int)之后以16进制的方式显示
* &作为位运算时,1&1=1 ,1&0=0,0&0=0
* 0xff 0x表示十六进制,ff是两个十六进制的数,每个f用二进制表示1111,两个f占八位,即一个字节
*首先toHexString传的参数应该是int类型32位,此处传的是byte类型8位,所以前面需要补24个0。然后& 0xff 就是把前面24个0去掉只要后8位。
* toHexString(b & 0xff)相当于做了一次位的与运算,将前24位字符省略,将后8位保留。即只转了后8位。即可得到两个十六进制的值
*/
//把字节数组转为16进制字符串
public static String convertBytes2HexStr(byte[] digestBytes) {
StringBuilder sb = new StringBuilder();
for (byte b : digestBytes) {
//获取b的补码的后八位
String hex = Integer.toHexString((int)b & 0xff);
//15的转换结果 --f=>0f
if(hex.length() == 1){
hex ="0"+hex;
}
sb.append(hex);
}
return sb.toString();
}
//把16进制字符串(偶数位)转换为字符数组
public static byte[] convertHex2Bytes(String hexStr){
//16进制是两个二进制位
//一个字节(8个二进制位)可以转为2个16进制的字符串
int length = hexStr.length()/2;
byte[] result = new byte[length];
for (int i = 0; i < length; i++) {
//hexStr:abcd
//Integer.parseInt:把s转为10进制数,radix指定s是什么进制的数
//获取每个字节的高4位 i=1 (2,3)--c转为16进制
int high4 = Integer.parseInt(hexStr.substring(i*2,i*2+1),16);
//获取每个字节的低4位 (3,4)--d
int low4 = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
//高四位*2^4,+低四位
result[i] =(byte)(high4*16 +low4);
}
return result;
}
//测试commons实现十六进制与字节数组转换
public static void main(String[] args) throws DecoderException {
//System.out.println(Integer.toHexString(15 & 0xff )); //0f
//System.out.println(Integer.toHexString(16 & 0xff )); //10
byte[] bytes = new byte[1];
bytes[0] = 15;
System.out.println(convertBytes2HexStr(bytes));
//使用commons的方法代替自己编写的convertBytes2Hex方法
System.out.println(Hex.encodeHexString(bytes));
String hexStr = "abcd";
System.out.println(Arrays.toString(convertHex2Bytes(hexStr)));
//commons中方法
System.out.println(Arrays.toString(Hex.decodeHex(hexStr)));
}
}
md5:Message-Digest Algorithm
1.jdk实现md5
@Test
public void test1() throws Exception {
String str = "校验数据";
String algorithm="MD5";
//1.获取消息摘要算法对象
MessageDigest md = MessageDigest.getInstance(algorithm);
//2.获取原始内容的字节数组
byte[] originalBytes = str.getBytes(UTF8);
//3.获取到摘要结果
byte[] digestBytes = md.digest(originalBytes);
//当originalBytes比较大时,循环切分
// md.update(originalBytes);
// md.digest();
//System.out.println(new String(digestBytes,UTF8));//乱码
//4.把每一个字节转换为16进制字符,最终在拼接起来这16进制字符
String hexStr = HexUtils.convertBytes2HexStr(digestBytes);
System.out.println("hexStr:"+hexStr);
}
打印结果:
hexStr:cd1d16220084c440aeedf01976344d61
2.使用commons工具类实现md5
import org.apache.commons.codec.digest.DigestUtils;
@Test
public void test2() throws Exception {
String str = "校验数据";
System.out.println(DigestUtils.md5Hex(str.getBytes(UTF8)));
}
打印结果同上
sha256:Secure Hash Algorithm
1.原生jdk实现sha256(转成16进制字符串后是64个)/sha512(转成16进制字符串后是128个)
方法同md5,需将方法名改为SHA-256/SHA-512
打印结果:hexStr:2c72c666472cf27d77e427b3d0e98d4193dd44cf429710150f1da82d356102c5
2.使用commons工具类实现sha256/sha512
@Test
public void test2() throws Exception {
String str = "校验数据";
//2c72c666472cf27d77e427b3d0e98d4193dd44cf429710150f1da82d356102c5
System.out.println(DigestUtils.sha256Hex(str.getBytes(UTF8)));
}
mac算法:Message Authentication Code 消息认证码,一种带有秘钥的hash函数
1.jdk原生实现mac算法(增加一个key)
//实现HmacSHA256/HmacSHA512 修改算法名称就行
@Test
public void test1() throws Exception {
String str = "校验数据";
String algorithm="HmacMD5";
String key = "123";
//获取消息摘要算法对象
Mac mac = Mac.getInstance(algorithm);
//获取key对象并初始化mac
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(UTF8), algorithm);
mac.init(secretKey);
//获取原始内容的字节数组
byte[] originalBytes = str.getBytes(UTF8);
//获取到摘要结果
byte[] digestBytes = mac.doFinal(originalBytes);
//把每一个字节转换为16进制字符,最终在拼接起来这16进制字符
String hexStr = HexUtils.convertBytes2HexStr(digestBytes);
//cd5e846cd5d40f0ba32308e14f1b4ebe
System.out.println("hexStr:"+hexStr);
}
2.使用commons工具类实现mac算法
@Test
public void test2() throws Exception {
String str = "校验数据";
String key = "123";
//获取算法对象,传入要加密的
String hmacMD5Str = new HmacUtils(HmacAlgorithms.HMAC_MD5, key.getBytes(StandardCharsets.UTF_8.name())).hmacHex(str);
System.out.println("hmacMD5Str:"+hmacMD5Str);
String hmacSHA256Str = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, key.getBytes(StandardCharsets.UTF_8.name())).hmacHex(str);
System.out.println("hmacSHA256Str:"+hmacSHA256Str);
String hmacSHA512Str = new HmacUtils(HmacAlgorithms.HMAC_SHA_512, key.getBytes(StandardCharsets.UTF_8.name())).hmacHex(str);
System.out.println("hmacSHA512Str:"+hmacSHA512Str);
}
对称加密:单秘钥加密,指加密和解密的过程使用相同的秘钥,相比非对称加密,因只有一把钥匙,因而速度更快
分类: 分组加密(块加密),序列加密
常见算法: DES,AES,3DES,Blowfiudh,IDEA,RC4,RC5,RC6
DES data encryption standard(已经过时)
public class DesTest {
//字符编码
private static final String UTF8 = StandardCharsets.UTF_8.name();
//算法名字
private static final String ALGORITHM = "DES";
//密钥,默认长度8
private static final String KEY = "12345678";
//测试DES
@Test
public void test1() throws Exception {
String str = "数据测试!";
String encrypt = encrypt(str);
System.out.println("加密后数据:" + encrypt);
String decrypt = decrypt(encrypt);
System.out.println("解密后数据:" + decrypt);
}
/**
* 加密方法
* @param text 待加密的内容
*/
public String encrypt(String text) throws Exception {
//获取实例
Cipher cipher = Cipher.getInstance(ALGORITHM);
//创建加/减密的规则,算法类型
SecretKeySpec secretKey = new SecretKeySpec(KEY.getBytes(UTF8), ALGORITHM);
//初始化,加密模式,规则
cipher.init(Cipher.ENCRYPT_MODE,secretKey);
//加密
byte[] encodeBytes = cipher.doFinal(text.getBytes(UTF8));
/**
* 加密的字节数据展示
* 第一种:Base64 第二种:转为16进制字符串
*/
return Base64.encodeBase64String(encodeBytes);
}
/**
* 解密方法
* @param encodeText 加密后的字符串
*/
public String decrypt(String encodeText) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
SecretKeySpec secretKey = new SecretKeySpec(KEY.getBytes(UTF8), ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE,secretKey);
byte[] decode = cipher.doFinal(Base64.decodeBase64(encodeText.getBytes(UTF8)));
return new String(decode,UTF8);
}
}
结果打印:
加密后数据:3CIZpqL2iAW9HZf3hc+yVQ==
解密后数据:数据测试!
AES advanced encryption standard 替代DES
public class AesTest {
//字符编码
private static final String UTF8 = StandardCharsets.UTF_8.name();
//算法名字
private static final String ALGORITHM = "AES";
//密钥,默认长度16/24/32
private static final String KEY = "1234567869854985";
//测试AES加密
@Test
public void test1() throws Exception {
String str = "数据测试!";
String encrypt = encrypt(str);
//71ef0cf6320139d79192850bc0a2c067
System.out.println("加密后数据:" + encrypt);
String decrypt = decrypt(encrypt);
System.out.println("解密后数据:" + decrypt);
}
/**
* 加密方法
* @param text 待加密的内容
*/
public String encrypt(String text) throws Exception {
Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, KEY);
byte[] encodeBytes = cipher.doFinal(text.getBytes(UTF8));
return HexUtils.convertBytes2HexStr(encodeBytes);
}
/**
* 解密方法
* @param encodeText 加密后的字符串
*/
public String decrypt(String encodeText) throws Exception {
//加密后的字符串进行16进制转换
byte[] hex2Bytes = HexUtils.convertHex2Bytes(encodeText);
//解密
Cipher cipher = getCipher(Cipher.DECRYPT_MODE, KEY);
byte[] decode = cipher.doFinal(hex2Bytes);
return new String(decode,UTF8);
}
/**
* 获取cipher对象,对传入的seed长度有要求
* des:要求seed字节数为8
* aes:要求sed字节数为16/24/32
* @param type 加解密模式
* @param seed 密钥key
* @return
*/
public Cipher getCipherSimple(int type,String seed) throws Exception {
//获取实例
Cipher cipher = Cipher.getInstance(ALGORITHM);
//创建加解密模式
SecretKeySpec secretKey = new SecretKeySpec(seed.getBytes(UTF8), ALGORITHM);
//初始化,加密模式,规则
cipher.init(type,secretKey);
return cipher;
}
结果打印:
加密后数据:71ef0cf6320139d79192850bc0a2c067
解密后数据:数据测试!
/**
*要求传入的key有长度限制的实现方法
* @param type 加解密模式
* @param seed 密钥key
*/
public Cipher getCipher(int type,String seed) throws Exception {
//获取实例
Cipher cipher = Cipher.getInstance(ALGORITHM);
//创建keyGenerator对象,可以根据传入的key生成一个指定长度的key(此处128)
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
//初始化secureRandom,并指定生成指定长度key的算法
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(seed.getBytes(UTF8));
keyGenerator.init(128,secureRandom);
//通过keyGenerator生成新的密钥数组
SecretKey secretKey = keyGenerator.generateKey();
//获取到新密钥的字节数组
byte[] encoded = secretKey.getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(encoded, ALGORITHM);
//加解密模式,规则
cipher.init(type,secretKeySpec);
return cipher;
}
}
结果打印:
加密后数据:a649854986256b926b594d12d2a32f4f
解密后数据:数据测试!
分组加密:
常用的加密模式:
ECB: electronic code book,电码本模式,将整个明文分成若干段相同的小段,然后对每一小段进行加密。
特点:每段之间互不依赖,可以并行处理,同样的明文总是生成同样的密文。
CBC: cipher block chaining,密文分组链模式,所谓链,即密文分组之间像链条一样相互连接在一起,先将明文切分成若干小段,然后每一小段与上一段的密文段(第一个块因没有上一个密文段,使用的是initialization vector)进行运算后,再与密钥进行加密。
特点:穿行处理:同样的明文每次生成的密文不一样。
块加密常用的填充模式:
对于固定的加密算法,每个块都有固定大小(BlockSize),比如8个byte,明文分块后,加密钱需要保证对最后一个块的大小为8个byte,如果不够啧使用特定的数据进行填充。
Nopadding:不自动填充,des时要求原文必须是8个字节的整数倍,aes时是16字节的整数倍。
PKCS5Pading(限制了块大小为8个byte的)/PKCS7Pading
PKCS:Public-Key Cryptography Standards 公钥密码学标准
ECB加密测试:
public class AesTest2 {
//字符编码
private static final String UTF8 = StandardCharsets.UTF_8.name();
/**
* AES默认填充模式:PKCS7Padding,分组的加密模式ECB
*/
//算法名字
private static final String ALGORITHM = "AES/ECB/PKCS7Padding";
private static final String ALGORITHM_TYPE = "AES";
//密钥,默认长度16/24/32
private static final String KEY = "1234567869854985";
/**加密解密算法,测试方法,同上AES算法**
/**
* @param type 加解密模式
* @param seed 密钥key
* @return
*/
public Cipher getCipherSimple(int type,String seed) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
SecretKeySpec secretKey = new SecretKeySpec(seed.getBytes(UTF8), ALGORITHM_TYPE);
cipher.init(type,secretKey);
return cipher;
}
}
打印结果:
加密后数据:71ef0cf6320139d79192850bc0a2c067
解密后数据:数据测试!
CBC加密测试:
public class AesTest2 {
//字符编码
private static final String UTF8 = StandardCharsets.UTF_8.name();
//算法名字
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String ALGORITHM_TYPE = "AES";
//IV 默认长度16
private static final String IV = "1234569874569852";
//密钥,默认长度16/24/32
private static final String KEY = "1234567869854985";
/**加密解密算法,测试方法,同上AES算法**
/**
* @param type 加解密模式
* @param seed 密钥key
* @return
*/
public Cipher getCipherSimple(int type,String seed) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
SecretKeySpec secretKey = new SecretKeySpec(seed.getBytes(UTF8), ALGORITHM_TYPE);
IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes(UTF8));
//初始化(使用CBC加密模式时,需要多传一个ivParameterSpec)
cipher.init(type,secretKey,ivParameterSpec);
return cipher;
}
打印结果:
加密后数据:a8f8aea05b6a885052cc050ad5cd3e3b
解密后数据:数据测试!
非对称加密
非对称加密: 加密和解密使用的是两个不同的密钥(public key和 private key)公钥可以给任何人,私钥是自己保留。
对称加解密使用相同的密钥,但对不同的原始内容加密会采用不同的密钥,导致密钥数量巨大,难以维护。
非对称加密应用场景: 加解密, 数字签名,数字信封,数字证书
**加解密:**可以使用公钥加密,对应就是私钥解密,也可以使用私钥加密,对应就是公钥解密
常见算法: RSA,ECC,DSA
RSA加密
1.再resources目录下创建两个文件rsa.pri,rsa.pub,存放公钥和私钥。
2.生成经过base64编码后的公钥/私钥并存储在文件中
3.从生成好的公钥文件rsa.pub(经过base64编码后存储的)中获取公钥对象
4.从生成好的私钥文件rsa.pri(经过base64编码后存储的)中获取私钥对象
5.执行加密/解密算法
public class RSATest {
//字符编码
private static final String UTF8 = StandardCharsets.UTF_8.name();
//算法名字
private static final String ALGORITHM = "RSA";
//公钥/私钥路径
private static String publicKeyPath;
private static String privateKeyPath;
//rsa单次最大加密的明文大小
private static final int MAX_ENCRYPT_BLOCK = 117;
//rsa单次最大解密的密文大小
private static final int MAX_DECRYPT_BLOCK = 128;
//加载文件
static{
ClassLoader classLoader = RSATest.class.getClassLoader();
publicKeyPath = classLoader.getResource("rsa.pub").getPath();
privateKeyPath = classLoader.getResource("rsa.pri").getPath();
}
/**
* 生成经过base64编码后的公钥/私钥并存储在文件中
* 密钥对每次执行都是生成不一样的,所以执行一次之后保存起来,其他时候就不需要再执行了
*/
private void writeKey2File() throws Exception {
//生成密钥对,获取KeyPairGenerator实例
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
keyPairGenerator.initialize(1024);
//通过KeyPair生成器生成KeyPair
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//生成publicKey
PublicKey aPublic = keyPair.getPublic();
//写入文件-把对象转为字节数组
byte[] publicEncoded = aPublic.getEncoded();
//使用base64编码
String publicKeyBaseStr = Base64.encodeBase64String(publicEncoded);
//生成私钥
PrivateKey aPrivate = keyPair.getPrivate();
//写入文件-把对象转为字节数组
byte[] privateEncoded = aPrivate.getEncoded();
//使用base64编码
String privateKeyBaseStr = Base64.encodeBase64String(privateEncoded);
//把公钥/私钥写入文件
FileUtils.writeStringToFile(new File(publicKeyPath),publicKeyBaseStr,UTF8);
FileUtils.writeStringToFile(new File(privateKeyPath),privateKeyBaseStr,UTF8);
}
/**
* 从生成好的公钥文件rsa.pub(经过base64编码后存储的)中获取公钥对象
* @return
*/
public PublicKey getPublicKey() throws Exception {
//读公钥文件中的内容
String publicKeyBaseStr = FileUtils.readFileToString(new File(publicKeyPath), UTF8);
//解码
byte[] decodeBase64 = Base64.decodeBase64(publicKeyBaseStr);
//公钥的规则就是x509
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(decodeBase64);
//获取公钥
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
return keyFactory.generatePublic(x509EncodedKeySpec);
}
/**
* 从生成好的私钥文件rsa.pri(经过base64编码后存储的)中获取私钥对象
* @return
*/
public PrivateKey getPrivateKey() throws Exception {
String privateKeyBaseStr = FileUtils.readFileToString(new File(privateKeyPath), UTF8);
byte[] decodeBase64 = Base64.decodeBase64(privateKeyBaseStr);
//私钥规则
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decodeBase64);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
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 = deCode(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 = deCode(cipher,decodeBase64,MAX_DECRYPT_BLOCK);
//加密的字节数据展示
return new String(decryptedBytes);
}
/**
* 执行加密/解密 通过cipher确定
* @param cipher
* @param bytes
* @param maxBlockSize
* @return
*/
public byte[] deCode(Cipher cipher, byte[] bytes,int maxBlockSize) throws Exception {
//传入字节大小
int inputLen = bytes.length;
//分块 偏移量
int offset = 0;
//本次完成的加密的字节数组
byte[] cache;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//当前第几次循环
int i = 0;
//循环分段处理
while ((inputLen - offset) > 0){
if((inputLen - offset) > maxBlockSize){
//需要分段处理,第三个参数是需要处理多长
cache = cipher.doFinal(bytes, offset, maxBlockSize);
}else{
//一次处理,处理对象bytes,开始offset,处理inputLen-offset个
cache = cipher.doFinal(bytes,offset,inputLen-offset);
}
//写入cache内容
baos.write(cache,0,cache.length);
//偏移量
i++;
offset = i*maxBlockSize;
}
//加密/解密的内容
byte[] codecBytes = baos.toByteArray();
baos.close();
return codecBytes;
}
@Test
public void testWriteKey2File() throws Exception {
writeKey2File();
}
/**
* 测试RSA
* @throws Exception
*/
@Test
public void testRSa() throws Exception {
String str = "乐之者JAVA";
//测试公钥加密,私钥解密
String encryptStr = encrypt(str, getPublicKey());
System.out.println("公钥加密后数据:"+encryptStr);
String decryptStr = decrypt(encryptStr, getPrivateKey());
System.out.println("私钥解密后数据:"+decryptStr);
//测试私钥加密,公钥解密
String encryptStr1 = encrypt(str, getPrivateKey());
System.out.println("私钥加密后数据:"+encryptStr1);
String decryptStr1 = decrypt(encryptStr1, getPublicKey());
System.out.println("公钥解密后数据:"+decryptStr1);
}
}
打印结果:
公钥加密后数据:Uc7Ufclwijku2W1HD8vZIOQrZpMx9RzCM58mDLpYNIu7JXQwOG8+nO+wiEWUeejoKmYCMBjUOlQOODE10wVhupKmDSZjLtMrcxzYFTrqyNaZzWSaPhE0x9I59uHcxMti+8oF9yySDZkYHqVTPHmNwjVF10Qbl/gi+fxnxoJAwV0=
私钥解密后数据:乐之者JAVA
私钥加密后数据:slju4qfi0+sIaAvnOOecDsDSiPiyguyXCQwWb/xzm6DfFrM1mTk+Iw3lfmoijqzE9qJcn/am2GYZyCwXhj/eNdwKJNu8abS/QDM9znTSQwRt+N+vWxDtI6FOm7G7Rvbq2nA/GESDV6ag+gfoxhOWmmqkEkGnJ1Pw5/txlBGyGQU=
公钥解密后数据:乐之者JAVA
数字签名
发送方A: 原始内容–通过摘要算法获取原始内容的摘要str1–>把str1用发送方的私钥加密–>数字签名
接收方B: 用发送方的公钥验证签名并解密–>对原始内容进行摘要算法str2–>比较str1==str2(保证未被篡改)
注意: 不是用公钥加密hash,如果用公钥,别人没私钥,无法验证。因为签名是先进行摘要再进行rsa,所以在摘要算法一定的情况下,签名后得到的字符串长度总是一样的。
**目的:**验证发送方就是发送方,校验完整性
数字信封: 对称加密的密钥(分发不安全)–>接收放的rsa公钥加密–>接受方用rsa私钥再解开
为什么分发密钥,不是约定好了吗?密钥会随着每个零件的数据一起发送给接收方
数字证书: 有了数字签名就可以证明发送者的身份了,还有问题吗?
A:B这里存储的A的公钥被换成了C的公钥,A发送的信息也换了C的私钥生成的签名,现在需要一种手段来证明”B这里存储的A的公钥“就是A的公钥。
public class RSATest2 {
//字符编码
private static final String SIGNATURE_ALGORITHM = "SHA256WITHRSA";
public static void main(String[] args) throws Exception {
String content ="今晚八点,行动!";
byte[] contentBytes = content.getBytes();
String sign = sign(contentBytes);
System.out.println("签名:" + sign);
boolean status = verify(contentBytes,sign);
System.out.println("验证结果:\r" + status);
}
private static boolean verify(byte[] data, String sign) throws Exception {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(new RSATest().getPublicKey());
signature.update(data);
return signature.verify(HexUtils.convertHex2Bytes(sign));
}
/**
* 对信息使用私钥生成数字签名
* @param data 原始数据
* @return
*/
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 HexUtils.convertBytes2HexStr(signature.sign());
}
}
结果打印:
签名:a3099f05d0381f8d0f555fd24a447fa97da6489f76df4732e8b524c84b97c97e9f54eb872b3bc7356b8e79b8715d2ebd0cf49f6c9a5b8a1e015b2b5ad260eeea4393e8c8a2d233d2efba50d0df3be682fff7ba6d3921e3d2ce4dfa265c8062a2eb234a1fca553c1f20d3b72e0f9f2acef663b1ece45cd079e9db257a74138051
true
代码:https://gitee.com/suisui9857/cryptography(备注:数字证书代码有问题)