Java-密码学

编码算法

不是加密和解密,是为了在网络间更方便的传输数据/本地存储字节数组而产生。

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(备注:数字证书代码有问题)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值