密码学-java信息安全,摘要算法,对称加密(AES)/非对称加密(RSA)
创始人
2024-06-03 13:11:37
0

一、编码算法

主要是为了在网络间更方便的传输数据/本地存储字节数组而产生

1、base64

由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@Testpublic 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@Testpublic 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));}
}

2、URL编码

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();@Testpublic 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);}
}

只对中文进行编码

二、摘要算法

1、定义

又叫Hash算法、散列函数、数字摘要、消息摘要。它是一种单向算法,用户可以通过hash算法对目标信息生成一段特定长度的唯一hash值,但不能通过这个hash值重新获得目标信息。

2、应用场景

  • 密码、信息完整性校验、数字签名

3、常见算法

  • MD5:Message-Digest Algorithm,结果占128位==>16个byte
  • SHA(Secure Hash Algorithm):安全散列算法
    • sha-256
    • 其他如:sha-0,sha-1,sha512
  • MAC(Message Authentication Code):消息认证码,是一种带有密钥的hash函数
  • 其他如:MD2、MD4、HAVAL

4、案例

1、使用jdk原生api实现md5,字节数组与16进制字符串之间的转换

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();@Testpublic 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;}@Testpublic void test() throws DecoderException {String hexStr = "abcd";byte[] hex2Btyes = convertHex2Btyes(hexStr);System.out.println(Arrays.toString(hex2Btyes));//使用codecSystem.out.println(Arrays.toString(Hex.decodeHex(hexStr)));}

2、使用commons-codec实现md5

	//import org.apache.commons.codec.digest.*;//使用commons-codec实现md5@Testpublic void test2() throws Exception{String str = "使用commons-codec实现md5";System.out.println(DigestUtils.md5DigestAsHex(str.getBytes(UTF8)));}

3、使用commons-codec实现sha256

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 {@Testpublic void test1(){String str = "使用commons-codec实现sha256";System.out.println(DigestUtils.sha1Hex(str.getBytes(UTF8)));}
}

4、MAC(Message Authentication Code):消息认证码

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对象并初始化macSecretKeySpec 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;}@Testpublic 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@Testpublic 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);}
}

三、对称加密

1、定义

也叫单密钥加密,所谓单密钥,指的是加密和解密的过程使用相同的密钥,相比非对称加密,因只有一把钥匙,因而速度更快,更适合加解密大文件

2、常见算法

  • DES:data encryption standard,已经过时
  • AES:advanced encryption standard,替代des
  • 其他如:3DES、Blowfish、IDEA、RC4、RC5、RC6

3、分类

  • 分组加密,又叫快加密
  • 序列加密

4、块加密常用的加密模式

  • ECB

定义: electronic code book,电码本模式,将整个明文分成若干段相同的小段,然后对每一小段进行加密。特点: 每段之间互不依赖,可以并行处理;同样的明文总是生成同样的密文
在这里插入图片描述

  • CBC

定义: cipher block chaining,密文分组链模式,所谓链,即密文分组之间像链条一样相与连接在一起。先将明文切分成若干小段,然后每一小段与上一段的密文段(第一个块因没有上一个密文段,使用的是IV进行运算后,再与密钥进行加密
特点: 串行处理,同样的明文每次生成的密文不一样。
在这里插入图片描述

5、测试用例

DES测试用例

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进制字符串//这里使用base64return 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);}@Testpublic void test()throws Exception{String str = "对称加密DES模式测试";String encrypt = encrypt(str);String decrypt = decrypt(encrypt);System.out.println("加密后的结果:"+encrypt);System.out.println("解码后的结果:"+decrypt);}
}

AES测试用例

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进制字符串//这里使用base64return 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);}@Testpublic void test()throws Exception{String str = "对称加密AES模式,采用16进制编码测试";String encrypt = encrypt(str);String decrypt = decrypt(encrypt);System.out.println("加密后的结果:"+encrypt);System.out.println("解码后的结果:"+decrypt);}
}

解决AES和DES的key默认固定了长度问题

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,生成一个指定长度的keyKeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);//初始化SecureRandom,指定生成key的算法SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");//指定原始传入的keysecureRandom.setSeed(KEY.getBytes(UTF8));//指定生成新的key的长度keyGenerator.init(128,secureRandom);//通过keyGenerator生成原始keySecretKey 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进制字符串//这里使用base64return 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,生成一个指定长度的keyKeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);//初始化SecureRandom,指定生成key的算法SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");//指定原始传入的keysecureRandom.setSeed(KEY.getBytes(UTF8));//指定生成新的key的长度keyGenerator.init(128,secureRandom);//通过keyGenerator生成原始keySecretKey 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);}@Testpublic 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);}
}

6、块加密常用的填充模式

为什么要有?对于固定的加密算法,每个块有固定大小(BlockSize),比如8个byte,明文分块后,加密前需要保证对最后一个块的大小为8个byte,如果不够则使用特定数据进行填充。

  • NoPadding:不自动填充
    des时要求原文必须是8个字节的整数倍,aes时是16个字节的整数倍
  • PKCS5Padding(限制了块大小为8个byte的PKCS7Padding)/PKCS7Padding
    PKCS: Public-Key Cryptography standards,公钥密码学标准

测试用例

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,生成一个指定长度的keyKeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_TYPE);//初始化SecureRandom,指定生成key的算法SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");//指定原始传入的keysecureRandom.setSeed(KEY.getBytes(UTF8));//指定生成新的key的长度keyGenerator.init(128,secureRandom);//通过keyGenerator生成原始keySecretKey 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进制字符串//这里使用base64return 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,生成一个指定长度的keyKeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_TYPE);//初始化SecureRandom,指定生成key的算法SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");//指定原始传入的keysecureRandom.setSeed(KEY.getBytes(UTF8));//指定生成新的key的长度keyGenerator.init(128,secureRandom);//通过keyGenerator生成原始keySecretKey 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);}@Testpublic void test()throws Exception{String str = "对称加密AES模式,采用16进制编码测试";String encrypt = encrypt(str);String decrypt = decrypt(encrypt);System.out.println("加密后的结果:"+encrypt);System.out.println("解码后的结果:"+decrypt);}
}

四、非对称加密

1、定义

加密和解密使用的是两个不同的密钥 (public key 和 private key)。公钥可以给任何人,私钥总是自己保留。

2、为什么会出现?

对称加解密使用相同的秘钥,但对不同的原始内容加密会采用不同的秘钥,导致秘钥数量巨大,难以维护。

3、常见算法

  • RSA
  • 其他如: ECC,Diffie-Hellman,El Gamal,DSA

4 应用场景

  • 加解密

    可以使用公钥加密,对应的就是私钥解密;也可以使用私钥加密,对应的公钥解密

    测试用例

    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生成器生成keyPairKeyPair 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;}/*生成公钥私钥,密钥对每次生成的都是不一样的,所以我们执行一次之后保存起来,其他时候就不需要在执行了*/@Testpublic void testWriteKey() throws Exception{writeKey2File();}@Testpublic 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";@Testpublic 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));}
    }
  • 数字信封
    对称加密的秘钥分发不安全–>发送方用接收方的公钥进行加密–>接受方用私钥再解开
    注意:为什么会有分发秘钥呢? 不是约定好了吗? 结合我们前面的讲解,秘钥会着每个零件的数期一起发送给接收方。

  • 数字证书

    • Ca
      certificate authority,使用pkipublic key nfrastructure)技术的机构,也可自己内部搭建,如使用ejbca。
    • ca根证书
      Q:B除了存储A的数字证书对应的ca公钥,假设还有N个人给B发信息,难道B都要保存一份他们的数字证书的CA公钥吗?
      A: 不需要,CA认证中心给可以给B一份”根证书”,里面存储的CA公钥可以验证所有CA分中心颁发的数字证书

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...