锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

Java 密码学

时间:2023-02-12 14:30:00 控制电缆kyv绝缘电阻500m

Java 密码学

提示: 本材料仅供个人学习参考,不作为系统学习过程,请注意识别!


Java 密码学

  • Java 密码学
  • 一.编码算法
    • 1.1 base64
    • 1.2 URL编码
  • 二.摘要算法
    • 2.1 定义
    • 2.2 使用场景
    • 2.3 常见的算法
  • 三.对称加密
    • 3.1 定义
    • 3.2 常见算法
    • 3.3 分类
    • 3.4 常用的块加密模式
    • 3.5 块加密常用的填充模式
  • 四.非对称加密
    • 4.1 定义
    • 4.2 为什么会出现?
    • 4.3 常见算法
    • 4.4 应用场景


一.编码算法

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

1.1 base64

由A-Z、a-z、0-9、 、/由64个字符组成,base64以三个字节为一组,如果最后一组不到三个字节,则使用=号补充

Base 64的主要用途不是加密,而是将一些二进制数转换成普通字符,便于在网络上传输。 由于一些二进制字符属于传输协议中的控制字符,不能直接传输,因此需要转换。因为有些系统只能使用ASCII字符,Base64是用来将非的ASCII字符的数据转换成ASCII一种字符的方法,Base64特别适合在http,mime在协议下快速传输数据。例如,图片在网络中的传输。
Base64.不是安全领域的加密解密算法。虽然我们经常遇到所谓的加密解密算法,但我们经常遇到base64加密解密base64只能算是编码算法,适合传输数据内容。base64编码后,原文也变成了看不见的字符格式,但方法初级简单。

java代码:

package com.chuanglan.http;   import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import java.util.Base64;  public class TestString { 
             public static void main(String[] args) throws Exception { 
                 String str = "Base加密解密";         base64(str);         enAndDeCode(str);     }       /** * Base64 */     public static void base64(String str) { 
                 byte[] bytes = str.getBytes();         //Base64 加密         Sring encoded = Base64.getEncoder().encodeToString(bytes);
        System.out.println("Base 64 加密后:" + encoded);
        //Base64 解密
        byte[] decoded = Base64.getDecoder().decode(encoded);
        String decodeStr = new String(decoded);
        System.out.println("Base 64 解密后:" + decodeStr);
        System.out.println();
    }
 
    /** * BASE64加密解密 */
    public static void enAndDeCode(String str) throws Exception { 
        
        String data = encryptBASE64(str.getBytes());
        System.out.println("sun.misc.BASE64 加密后:" + data);
        byte[] byteArray = decryptBASE64(data);
        System.out.println("sun.misc.BASE64 解密后:" + new String(byteArray));
    }
 
    /** * BASE64解密 * @throws Exception */
    public static byte[] decryptBASE64(String key) throws Exception { 
        
        return (new BASE64Decoder()).decodeBuffer(key);
    }
 
    /** * BASE64加密 */
    public static String encryptBASE64(byte[] key) throws Exception { 
        
        return (new BASE64Encoder()).encodeBuffer(key);
    }
}

1.2 URL编码

我们都知道Http协议中参数的传输是"key=value"这种键值对形式的,如果要传多个参数就需要用“&”符号对键值对进行分割。如"?name1=value1&name2=value2",这样在服务端在收到这种字符串的时候,会用“&”分割出每一个参数,然后再用“=”来分割出参数值。

例如: “name1=value1&name2=value2”我们来说一下客户端到服务端的概念上解析过程:
上述字符串在计算机中用ASCII吗表示为:
6E616D6531 3D 76616C756531 26 6E616D6532 3D 76616C756532。
6E616D6531:name1
3D:=
76616C756531:value1
26:&
6E616D6532:name2
3D:=
76616C756532:value2

服务端在接收到该数据后就可以遍历该字节流,首先一个字节一个字节的吃,当吃到3D这字节后,服务端就知道前面吃得字节表示一个key,继续向后,如果遇到26,说明从刚才吃的3D到26子节之间的是上一个key的value,以此类推就可以解析出客户端传过来的参数。

有个问题, 如果我的参数值中就包含=或&这种特殊字符的时候该怎么办。

例如: “name1=value1”,其中value1的值是“va&lu=e1”字符串,那么实际在传输过程中就会变成这样“name1=va&lu=e1”。我们的本意是就只有一个键值对,但是服务端会解析成两个键值对,这样就产生了奇异。

如何解决上述问题带来的歧义呢?解决的办法就是对参数进行URL编码

URL编码只是简单的在特殊字符的各个字节前加上%,例如,我们对上述会产生奇异的字符进行URL编码后结果:“name1=va%26lu%3D”,这样服务端会把紧跟在“%”后的字节当成普通的字节,就是不会把它当成各个参数或键值对的分隔符。

Java URL编码:

  public static void main(String[] args) throws UnsupportedEncodingException { 
        
        String str = "java 学而不思则罔";
        String encode = URLEncoder.encode(str, StandardCharsets.UTF_8.name());
        System.out.println("encode:" + encode);//java+%E5%AD%A6%E8%80%8C%E4%B8%8D%E6%80%9D%E5%88%99%E7%BD%94
        String decode = URLDecoder.decode(encode, StandardCharsets.UTF_8.name());
        System.out.println("decode:" + decode);//学而不思则罔
    }

二.摘要算法

2.1 定义

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

2.2 使用场景

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

2.3 常见的算法

  1. MD5: 结果占128位,16个byte,转成16进制是32位
  public static void main(String[] args) throws UnsupportedEncodingException { 
        
        String str = "java 学而不思则罔";
        byte[] secretBytes = null;
        try { 
        
            secretBytes = MessageDigest.getInstance("md5").digest(str.getBytes());
        } catch (NoSuchAlgorithmException e) { 
        
            throw new RuntimeException("没有这个md5算法!");
        }
        String md5code = new BigInteger(1, secretBytes).toString(16);
        for (int i = 0; i < 32 - md5code.length(); i++) { 
        
            md5code = "0" + md5code;
        }
        System.out.println(md5code);//70eae8276e0b35f1cb67bc5c5f9111ce
    }
  1. SHA: 安全散列算法,如:sha-256、sha-0、sha-1、sha-512
/** * 利用java原生的类实现SHA256加密 * @return */
    public static void main(String[] args) throws UnsupportedEncodingException { 
        
        String str = "java 学而不思则罔";

        MessageDigest messageDigest;
        String encodeStr = "";
        try { 
        
            messageDigest = MessageDigest.getInstance("SHA-256");
            //SHA512加密
            //messageDigest = MessageDigest.getInstance("SHA-512");
            messageDigest.update(str.getBytes("UTF-8"));
            encodeStr = byte2Hex(messageDigest.digest());
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { 
        
            e.printStackTrace();
        }
        System.out.println(encodeStr);//df8312a2f52c5f5e36400a94c688ce278cb5b23026327b8371e7445286405927
    }

    /** * 将byte转为16进制 * * @param bytes * @return */
    private static String byte2Hex(byte[] bytes) { 
        
        StringBuilder sb = new StringBuilder();
        String temp = null;
        for (byte aByte : bytes) { 
        
            temp = Integer.toHexString(aByte & 0xFF);
            if (temp.length() == 1) { 
        
                // 1得到一位的进行补0操作
                sb.append("0");
            }
            sb.append(temp);
        }
        return sb.toString();
    }
  1. MAC:消息认证码,是一种带有秘钥的Hash函数,兼容了MD和SHA算法的特性,并在此基础上加入了密钥。消息的散列值由只有通信双方知道的秘密密钥K来控制,因次,我们也常把MAC称为HMAC(keyed-Hash Message Authentication Code)。

MAC算法主要集合了MD和SHA两大系列消息摘要算法。MD系列的算法有HmacMD2、HmacMD4、HmacMD5三种算法;SHA系列的算法有HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384.HmacSHA512五种算法。

package com.tao.test;
 
import java.security.NoSuchAlgorithmException;
 
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
 
/** * MAC算法工具类 * 对于HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384、HmacSHA512应用的步骤都是一模一样的。具体看下面的代码 */
class MACCoder { 
        
	/** * 产生HmacMD5摘要算法的密钥 */
	public static byte[] initHmacMD5Key() throws NoSuchAlgorithmException { 
        
		// 初始化HmacMD5摘要算法的密钥产生器
		KeyGenerator generator = KeyGenerator.getInstance("HmacMD5");
		// 产生密钥
		SecretKey secretKey = generator.generateKey();
		// 获得密钥
		byte[] key = secretKey.getEncoded();
		return key;
	}
 
	/** * HmacMd5摘要算法 * 对于给定生成的不同密钥,得到的摘要消息会不同,所以在实际应用中,要保存我们的密钥 */
	public static String encodeHmacMD5(byte[] data, byte[] key) throws Exception { 
        
		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(key, "HmacMD5");
		// 实例化Mac
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());
		//初始化mac
		mac.init(secretKey);
		//执行消息摘要
		byte[] digest = mac.doFinal(data);
		return new HexBinaryAdapter().marshal(digest);//转为十六进制的字符串
	}
	
	
	/** * 产生HmacSHA1摘要算法的密钥 */
	public static byte[] initHmacSHAKey() throws NoSuchAlgorithmException { 
        
		// 初始化HmacMD5摘要算法的密钥产生器
		KeyGenerator generator = KeyGenerator.getInstance("HmacSHA1");
		// 产生密钥
		SecretKey secretKey = generator.generateKey();
		// 获得密钥
		byte[] key = secretKey.getEncoded();
		return key;
	}
 
	/** * HmacSHA1摘要算法 * 对于给定生成的不同密钥,得到的摘要消息会不同,所以在实际应用中,要保存我们的密钥 */
	public static String encodeHmacSHA(byte[] data, byte[] key) throws Exception { 
        
		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(key, "HmacSHA1");
		// 实例化Mac
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());
		//初始化mac
		mac.init(secretKey);
		//执行消息摘要
		byte[] digest = mac.doFinal(data);
		return new HexBinaryAdapter().marshal(digest);//转为十六进制的字符串
	}
	
	
	
	/** * 产生HmacSHA256摘要算法的密钥 */
	public static byte[] initHmacSHA256Key() throws NoSuchAlgorithmException { 
        
		// 初始化HmacMD5摘要算法的密钥产生器
		KeyGenerator generator = KeyGenerator.getInstance("HmacSHA256");
		// 产生密钥
		SecretKey secretKey = generator.generateKey();
		// 获得密钥
		byte[] key = secretKey.getEncoded();
		return key;
	}
 
	/** * HmacSHA1摘要算法 * 对于给定生成的不同密钥,得到的摘要消息会不同,所以在实际应用中,要保存我们的密钥 */
	public static String encodeHmacSHA256(byte[] data, byte[] key) throws Exception { 
        
		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(key, "HmacSHA256");
		// 实例化Mac
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());
		//初始化mac
		mac.init(secretKey);
		//执行消息摘要
		byte[] digest = mac.doFinal(data);
		return new HexBinaryAdapter().marshal(digest);//转为十六进制的字符串
	}
	
	
 
	/** * 产生HmacSHA256摘要算法的密钥 */
	public static byte[] initHmacSHA384Key() throws NoSuchAlgorithmException { 
        
		// 初始化HmacMD5摘要算法的密钥产生器
		KeyGenerator generator = KeyGenerator.getInstance("HmacSHA384");
		// 产生密钥
		SecretKey secretKey = generator.generateKey();
		// 获得密钥
		byte[] key = secretKey.getEncoded();
		return key;
	}
 
	/** * HmacSHA1摘要算法 * 对于给定生成的不同密钥,得到的摘要消息会不同,所以在实际应用中,要保存我们的密钥 */
	public static String encodeHmacSHA384(byte[] data, byte[] key) throws Exception { 
        
		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(key, "HmacSHA384");
		// 实例化Mac
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());
		//初始化mac
		mac.init(secretKey);
		//执行消息摘要
		byte[] digest = mac.doFinal(data);
		return new HexBinaryAdapter().marshal(digest);//转为十六进制的字符串
	}
	
	
 
	/** * 产生HmacSHA256摘要算法的密钥 */
	public static byte[] initHmacSHA512Key() throws NoSuchAlgorithmException { 
        
		// 初始化HmacMD5摘要算法的密钥产生器
		KeyGenerator generator = KeyGenerator.getInstance("HmacSHA512");
		// 产生密钥
		SecretKey secretKey = generator.generateKey();
		// 获得密钥
		byte[] key = secretKey.getEncoded();
		return key;
	}
 
	/** * HmacSHA1摘要算法 * 对于给定生成的不同密钥,得到的摘要消息会不同,所以在实际应用中,要保存我们的密钥 */
	public static String encodeHmacSHA512(byte[] data, byte[] key) throws Exception { 
        
		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(key, "HmacSHA512");
		// 实例化Mac
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());
		//初始化mac
		mac.init(secretKey);
		//执行消息摘要
		byte[] digest = mac.doFinal(data);
		return new HexBinaryAdapter().marshal(digest);//转为十六进制的字符串
	}
}
 
public class MACTest { 
        
	public static void main(String[] args) throws Exception { 
        
		String testString = "asdasd";
		
		byte[] keyHmacMD5=MACCoder.initHmacMD5Key();
		System.out.println(MACCoder.encodeHmacMD5(testString.getBytes(),keyHmacMD5));
		
		byte[] keyHmacSHA1=MACCoder.initHmacSHAKey();
		System.out.println(MACCoder.encodeHmacSHA(testString.getBytes(),keyHmacSHA1));
		
		byte[] keyHmacSHA256=MACCoder.initHmacSHA256Key();
		System.out.println(MACCoder.encodeHmacSHA256(testString.getBytes(),keyHmacSHA256));
		
		byte[] keyHmacSHA384=MACCoder.initHmacSHA384Key();
		System.out.println(MACCoder.encodeHmacSHA384(testString.getBytes(),keyHmacSHA384));
		
		byte[] keyHmacSHA512=MACCoder.initHmacSHA512Key();
		System.out.println(MACCoder.encodeHmacSHA512(testString.getBytes(),keyHmacSHA512));
	}
}

其他: MD2、MD4、HAVAL

三.对称加密

3.1 定义

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

3.2 常见算法

  1. DES: 已经过时
  2. AES: 替换DES

那么为什么原来的DES会被取代呢,,原因就在于其使用56位密钥,比较容易被破解。而AES可以使用128、192、和256位密钥,并且用128位分组加密和解密数据,相对来说安全很多。完善的加密算法在理论上是无法破解的,除非使用穷尽法。使用穷尽法破解密钥长度在128位以上的加密数据是不现实的,仅存在理论上的可能性。统计显示,即使使用目前世界上运算速度最快的计算机,穷尽128位密钥也要花上几十亿年的时间,更不用说去破解采用256位密钥长度的AES算法了。

package com.personal.custom.redis.redisson.config;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

public class ZzSecurityHelper { 
        
    /* * 加密用的Key 可以用26个字母和数字组成 使用AES-128-CBC加密模式,key需要为16位。 */
    private static final String key = "hj7x89H$yuBI0456";
    private static final String iv = "NIfb&95GUY86Gfgh";

    public static String encryptAES(String data) throws Exception { 
        
        try { 
        
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            int blockSize = cipher.getBlockSize();
            byte[] dataBytes = data.getBytes();
            int plaintextLength = dataBytes.length;

            if (plaintextLength % blockSize != 0) { 
        
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }

            byte[] plaintext = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());  // CBC模式,需要一个向量iv,可增加加密算法的强度

            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);

            return ZzSecurityHelper.encode(encrypted).trim(); // BASE64做转码。

        } catch (Exception e) { 
        
            e.printStackTrace();
            return null;
        }
    }

    public static String decryptAES(String data) throws Exception { 
        
        try { 
        
            byte[] encrypted1 = ZzSecurityHelper.decode(data);//先用base64解密

            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original);
            return originalString.trim();
        } catch (Exception e) { 
        
            e.printStackTrace();
            return null;
        }
    }

    /** * 编码 * * @param byteArray * @return */
    public static String encode(byte[] byteArray) { 
        
        return new String(new Base64().encode(byteArray));
    }

    /** * 解码 * * @param base64EncodedString * @return */
    public static byte[] decode(String base64EncodedString) { 
        
        return new Base64().decode(base64EncodedString);
    }

    public static void main(String[] args) throws Exception { 
        
        String encode = ZzSecurityHelper.encryptAES("java 学而不思则罔");
        System.out.println(encode);//+FP2MywPzn2vuj9ilUOSOJGpeaDe8eJOX/0xI7PSX0s=

        String decode = ZzSecurityHelper.decryptAES(encode);
        System.out.println(decode);//java 学而不思则罔
    }
}
  1. 其他:3DES、IDEA、RC4、RC5、RC6等

3.3 分类

  1. 分组加密,又叫块加密
  2. 序列加密

3.4 块加密常用的加密模式

参考:对称加密算法常用的五种分组模式

3.5 块加密常用的填充模式

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

  1. NoPadding: 不自动填充
    DES时要求明文必须是8个字节的整数倍,AES时是16个字节的整数倍
  2. PKCS5Padding/PKCS7Padding

四.非对称加密

4.1 定义

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

4.2 为什么会出现

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

4.3 常见算法

  1. RSA
 public static void main(String[] args) throws Exception { 
        
        // rsa 加解密的工具类
        RSAUtil usaUtil = new RSAUtil();
        String privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMBPkejIDlj1bzPZ\n" +
                "cv+UbiwTfqXvi50B3w5jurpRwDROFVPDlm0DOzK13ucx8ddPRB2DKAUCkgLEj+SQ\n" +
                "ptLgpQnwEUG+MIcqMQxWBzwIENAAH8kwmCTrB5HIPyOFOIQXfvfjdtdpz84VfEII\n" +
                "565DGI3AWiKI/x/yuphCtKKEtNYHAgMBAAECgYAzaRIomNK9FV/VDJyhlpydS9jf\n" +
                "o1o/bvCieCbDCHfJg9ZPvknIesSomdYtGDz+wkIDYbyhGj7OXp3ZDJKMbWGw/8fw\n" +
                "vXKXZu3Q8E6JwTQ7nZhlMQpI0PzxsBitItFQIoORzHjKmizdNzSK/x/lJ1QVZWws\n" +
                "ftyW72uaabMseRnegQJBAODMm0026q2NgPlMXLYipHCDpIfLPCgllSRuQzwjyvz1\n" +
                "bnlLdquSyyZDTmnN+zGFKLLDwzg6EnxicvwJz1Vgh+0CQQDbAJwTaQ7no23un180\n" +
                "+EUEckELO9C//jyB/6yvCTll/03MNNwkD8gxl9aT8/8turgktawSNw6whVtwFVfM\n" +
                "V+9DAkARB884MVHkJhVATcW0UrmMgJylYQNEs1wyL1xOoROOyHU/ITVzWCKl2nGF\n" +
                "WIKQRNtJd8VBbDzcSYUWjRO1DyQdAkBnNC5Y51Viy51uqiQPrj+4DK+iP5nsID8b\n" +
                "dAVIpywpaNqctPxY8icBV/CC4KUMQ8WrZwGjw9ZkUTP56dTqMzZJAkAvdmv6n3fv\n" +
                "qhzXe/6yoURoawTzLoP10WXyhMS1K8Qpa8trGX9AvpGPh5XQ5FHXeX7gXTDousM0\n" +
                "74HQuyYAWBxC";
        String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAT5HoyA5Y9W8z2XL/lG4sE36l\n" +
                "74udAd8OY7q6UcA0ThVTw5ZtAzsytd7nMfHXT0QdgygFApICxI/kkKbS4KUJ8BFB\n" +
                "vjCHKjEMVgc8CBDQAB/JMJgk6weRyD8jhTiEF37343bXac/OFXxCCOeuQxiNwFoi\n" +
                "iP8f8rqYQrSihLTWBwIDAQAB";

        String encrypt = usaUtil.encryptByPrivateKey(privateKey, "这个是RSA加密方式!");
        System.out.println("私钥加密后: " + encrypt);//私钥加密后: dhgwuj+tuATooRaErx9qgNwtSjgQdhgerHQKeuvvUzOCCdx6vH0M4UATN0Kul2BUcJ+cbAMlR2a3816RRO/oZtCX9vgFEtyatCNhLtkiFhL98xiugMaHikZxT11iipYUwECrfkIG9J5LUgzXRyQPQ/Nz2yM09aNgXoL9/yPaOLc=

        String decrypt = usaUtil.decryptByPublicKey(publicKey, encrypt);
        System.out.println("公钥解密后: " + decrypt);//公钥解密后: 这个是RSA加密方式!
    }

工具类:

package com.personal;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class RSAUtil { 
        
    private static Logger LOGGER = LoggerFactory.getLogger(RSAUtil.class);
    private static String KEY_ALGORITHM = "RSA";

    public static final int KEY_SIZE = 2048; // 密钥长度, 一般2048
    public static final int RESERVE_BYTES = 11;

    public static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding"; // 加密block需要预留11字节
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";// sign值生成方式
    public static final Charset CHARSET_UTF8 = Charset.forName("UTF-8");

    private String algorithm;// 密钥生成模式
    private String signature;// 签名sign生成模式
    private Charset charset;// 编码格式

    private int keySize;// RSA密钥长度必须是64的倍数,在512~65536之间
    private int decryptBlock; // 默认keySize=2048的情况下, 256 bytes
    private int encryptBlock; // 默认keySize=2048的情况下, 245 bytes

    private static KeyFactory keyFactory;

    static { 
        
        try { 
        
            keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        } catch (NoSuchAlgorithmException e) { 
        
            LOGGER.info("生成RSA密钥对异常,{}", e);
        }
    }

    public RSAUtil() { 
        
        this(CIPHER_ALGORITHM);
    }

    public RSAUtil(String algorithm) { 
        
        this(algorithm, CHARSET_UTF8);
    }

    public RSAUtil(int keySize) { 
        
        this(CIPHER_ALGORITHM, keySize, CHARSET_UTF8, SIGNATURE_ALGORITHM);
    }

    public RSAUtil(String algorithm, Charset charset) { 
        
        this(algorithm, KEY_SIZE, charset, SIGNATURE_ALGORITHM);
    }

    public RSAUtil(String algorithm, int keySize, Charset charset, String signature) { 
        
        this.algorithm = algorithm;
        this.signature = signature;
        this.charset = charset;
        this.keySize = keySize;

        this.decryptBlock = this.keySize / 8;
        this.encryptBlock = decryptBlock - RESERVE_BYTES;
    }

    /** * 公钥解密 * * @param publicKeyStr 公钥字符串 * @param data 需要解密的数据 * @return 解密后字符串 * @throws Exception 异常 */
    public String decryptByPublicKey(String publicKeyStr, String data) throws Exception { 
        
        PublicKey publicKey = restorePublicKey(publicKeyStr);
        return decryptByPublicKey(publicKey, data);
    }

    /** * 公钥解密 * * @param publicKey 公钥 * @param data 需要解密的数据 * @return 解密后字符串 * @throws Exception 异常 */
    public String decryptByPublicKey(PublicKey publicKey, String data) throws Exception { 
        
        byte[] bytes = Base64.decodeBase64(data.getBytes(charset));
        return decryptByPublicKey(publicKey, bytes);
    }

    /** * 公钥解密 * * @param publicKey 公钥 * @param data 需要解密的数据 * @return 解密后字符串 * @throws Exception 异常 */
    public String decryptByPublicKey(PublicKey publicKey, byte[] data) throws Exception { 
        
        byte[] decrypt = null;

        int dataLen = data.length;
        // 计算分段解密的block数 (理论上应该能整除)
        int nBlock = (dataLen / encryptBlock);
        if ((dataLen % encryptBlock) != 0) { 
         // 余数非0,block数再加1
            nBlock += 1;
        }
        // 输出buffer, 大小为nBlock个encryptBlock
        ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * encryptBlock);

        Cipher cipher = getCipher();
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        // 分段解密
        for (int offset = 0; offset < data.length; offset += decryptBlock) { 
        
            // block大小: decryptBlock 或 剩余字节数
            int inputLen = (data.length - offset);
            if (inputLen > decryptBlock) { 
        
                inputLen = decryptBlock;
            }

            // 得到分段解密结果
            byte[] decryptedBlock = cipher.doFinal(data, offset, inputLen);
            // 追加结果到输出buffer中
            outbuf.write(decryptedBlock);
        }
        outbuf.flush();
        decrypt = outbuf.toByteArray();
        outbuf.close();

        return StringUtils.newString(decrypt, charset.name());
    }

    /** * 私钥加密 * * @param privateKeyStr 私钥字符串 * @param data 需要加密的数据 * @return 加密后字符串 */
    public String encryptByPrivateKey(String privateKeyStr, String data) { 
        
        PrivateKey privateKey = restorePrivateKey(privateKeyStr);
        return encryptByPrivateKey(privateKey, data);
    }

    /** * 私钥加密 * * @param privateKey 私钥 * @param data 需要加密的数据 * @return 加密后字符串 */
    public String encryptByPrivateKey(PrivateKey privateKey, String data) { 
        
        byte[] bytes = data.getBytes(charset);
        return encryptByPrivateKey(privateKey, bytes);
    }

    /** * 私钥加密 * * @param privateKey 私钥 * @param data 需要加密的数据 * @return 加密后字符串 */
    public String encryptByPrivateKey(PrivateKey privateKey, byte[] data) { 
        
        byte[] encrypt = null;

        int dataLen = data.length;
        // 计算分段加密的block数 (向上取整)
        int nBlock = (dataLen / encryptBlock);
        if ((dataLen % encryptBlock) != 0) { 
         // 余数非0,block数再加1
            nBlock += 1;
        }
        // 输出buffer, 大小为nBlock个decryptBlock
        ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * decryptBlock);

        try { 
        
            Cipher cipher = getCipher();
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);

            // 分段加密
            for (int offset = 0; offset < dataLen; offset += encryptBlock) { 
        
                // block大小: encryptBlock 或 剩余字节数
                int inputLen = (dataLen - offset);
                if (inputLen > encryptBlock) { 
        
                    inputLen = encryptBlock;
                }

                // 得到分段加密结果
                byte[] encryptedBlock = cipher.doFinal(data, offset, inputLen);
                // 追加结果到输出buffer中
                outbuf.write(encryptedBlock);
            }

            outbuf.flush();
            encrypt = outbuf.toByteArray();
            outbuf.close();
        } catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException | IOException e) { 
        
            LOGGER.info("使用RSA,加密数据异常", e);
        }

        byte[] enData = Base64.encodeBase64(encrypt);
        return StringUtils.newString(enData, charset.name());
    }

    /** * 根据keyFactory生成Cipher * * @return cipher */
    private Cipher getCipher() { 
        
        Cipher cipher = null;
        try { 
        
            cipher = Cipher.getInstance(this.algorithm);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { 
        
            LOGGER.info("生成RSA Cipher异常", e);
        }
        return cipher;
    }

    /** * 公钥还原,将公钥转化为PublicKey对象 * * @param publicKeyStr 公钥字符串 * @return PublicKey对象 */
    public PublicKey restorePublicKey(String publicKeyStr) { 
        
        PublicKey publicKey = null;

        byte[] keyBytes = Base64.decodeBase64(publicKeyStr.getBytes(charset));
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        try { 
        
            publicKey = keyFactory.generatePublic(x509KeySpec);
        } catch (InvalidKeySpecException e) { 
        
            LOGGER.info("公钥还原,将公钥转化为PublicKey对象异常", e);
        }

        return publicKey;
    }

    /** * 私钥还原,将私钥转化为privateKey对象 * * @param privateKeyStr 私钥字符串 * @return PrivateKey对象 */
    public PrivateKey restorePrivateKey(String privateKeyStr) { 
        
        PrivateKey privateKey = null;

        byte[] keyBytes = Base64.decodeBase64(privateKeyStr.getBytes(charset));
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        try { 
        
            privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        } catch (InvalidKeySpecException e) { 
        
            LOGGER.info("私钥还原,将私钥转化为privateKey对象异常", e);
        }

        return privateKey;
    }

    /** * RSA生成sign值 * * @param privateKeyStr 私钥字符串 * @param data 数据 * @return sign值 * @throws Exception 异常 */
    public String generateSign(String privateKeyStr, String data) throws Exception { 
        
        return generateSign(restorePrivateKey(privateKeyStr), data);
    }

    /** * RSA生成sign值 * * @param privateKey 私钥 * @param data 数据 * @return sign值 * @throws Exception 异常 */
    public String generateSign(PrivateKey privateKey, String data) throws Exception { 
        
        Signature signature = Signature.getInstance(this.signature);
        signature.initSign(privateKey);
        signature.update(data.getBytes(charset));
        return Base64.encodeBase64String(signature.sign());
    }

    /** * RSA验签 * * @param publicKeyStr 公钥字符串 * @param data 数据 * @param sign sign值 * @return 验签是否成功 * @throws Exception 异常 */
    public boolean verifyRSA(String publicKeyStr, String data, String sign) throws Exception { 
        
        return verifyRSA(restorePublicKey(publicKeyStr), data, sign);
    }

    /** * RSA验签 * * @param publicKey 公钥 * @param data 数据 * @param sign sign值 * @return 验签是否成功 * @throws Exception 异常 */
    public boolean verifyRSA(PublicKey publicKey, String data, String sign) throws Exception { 
        
        Signature signature = Signature.getInstance(this.signature);
        signature.initVerify(publicKey);
        signature.update(data.getBytes(charset));

        return signature.verify(Base64.decodeBase64(sign));
    }

    public static void main(String[] args) { 
        
        RSAUtil rSAUtil = new RSAUtil();
        String data = "123456789";
        String privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCXIyAYK+NVoJ7DJEgTs3GyXIUu0+HtYm2mArKh8yUIbuiSN4aksNb4xAh0cS1YNg0G7prR+CKV7iUo1f8sA0VtN32XHxd6Fr3Q4QsC/+FH6HHS6GGI9i363j4YGtOwGEOyb9qt9YjFHlVqDyZiNYDazK3f+EZzfputFwVfZSp0M2fQhgBCZM2lycKvq/OJm79NDKMeld/ejpvNAqi7xehKy6eqNjdRiRmLcZvg6dW/A7QwlkQ/vCos5Mtd/kLu6oHgjXnBFiGsKGDScJYBvVFHcupmnE0eqJtnwr8OBIn9naeVaAHE46i6uxZtTe4K6QUM0JtdODL70EEjy7qG0WjpAgMBAAECggEBAIhFmIOS1th3CY8j4IU679IFT+SIERZsADeGCTCyvfpbngFwZUuLU1lbz8/F2D/IBHjynM+jLvQGlKS8RuaVUH0IYonm89EWPjHfJ4Gd269ta2viMUc/yPeAeXZcgfAuAKQb7I2bbKVnE1acsFwup68gi8n83vD2AEHSFvsLiXrZFZr9MmNIIyRfYZJCDPunZOv7XCgsDhdlYAx+Z092MwpEeLPEIpgueMHycU7AsBgq667m4/LP0l9CnCxk/cRUz9w5iZRmlyLLvL8Tox8VCcCtoOb7cxZpm1TYCQSDcVfhtIf4BKWjKBiHiHM17z0t111dEUc+XkbxacnPxDgn/80CgYEA3VgYY/1rPC5CPqmMXXl6iGdjsbSkJWaGigx3QkaUMwYv6xCxCL6zReNqHchbp+mvqNgWD6HPDDZk5dJnJeHvAdNCA6AQhodoPDFpYc70WgTciNv84cVTUSj9RGlRD9jvYN3k+lm8yxvykOc0xBsqE93lyNYEBuPE4cAi4LNBfgsCgYEArsz/94gXyvi/I2yAapkq3l+ieE+L731tc7FqB4QYVxyxUOgEla03++bRAikzOjT9YbGNl85khweNNle5J++3GL+Ak4hXmwHw6e1ZP4dP1d9OiBio96jn5okMD8AIFU/Y/JIoBr1o+k9jBkbOWOyVO5KD1z6ijGDCVMhovuWssVsCgYEApXjQcx/m5QyoFXRnLRI92m+Ahj9HX3ZwKg/7sB5XeHWtqQvHbYQzPZIvqKg6bSM0YQN6KqGKydR4RZ+v4RAwv6qRdWhaMlhUQnumDqrK3ek4fVAIkzgTe18rR9N7+F7zRfVc0xP3Idh41H8kYV71a/i9ahEk3Ym1jBc5e8ZGtdUCgYBv7erVqPp7SM6zsy2DlLKDlD9nxJ/5aZplY6xeRbKETWYpRXhyE2nuzjz1okYgNoAtR1FAbLOoVyiQLJnuPaxDl5SQY9Sc+CA42ne0m0N+0q/pq8i+VRSxZP4pM7C5XNi32irxLeYDqkPhaAOHo25nqAjuEjhppSeqvG1+F3l+UwKBgQC6puG4Xr/w4FSyKgRsDAE2tBKBllqLUh2S/68ptHpB54uO+bN1MI+I4B6CFArHfJlmPO/Vc4dC4IC2byrDd0uLjWT+D+7EzFgygpSxyYW5be/qjY/AZo7LfUbHQa77/uhhvCPZQZgYk+FEIIPIcruWejJmkbHpYE+WBtcFrsfjtw==";
        String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlyMgGCvjVaCewyRIE7NxslyFLtPh7WJtpgKyofMlCG7okjeGpLDW+MQIdHEtWDYNBu6a0fgile4lKNX/LANFbTd9lx8Xeha90OELAv/hR+hx0uhhiPYt+t4+GBrTsBhDsm/arfWIxR5Vag8mYjWA2syt3/hGc36brRcFX2UqdDNn0IYAQmTNpcnCr6vziZu/TQyjHpXf3o6bzQKou8XoSsunqjY3UYkZi3Gb4OnVvwO0MJZEP7wqLOTLXf5C7uqB4I15wRYhrChg0nCWAb1RR3LqZpxNHqibZ8K/DgSJ/Z2nlWgBxOOoursWbU3uCukFDNCbXTgy+9BBI8u6htFo6QIDAQAB";
        String cusPubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtxUeho47bzGLK+IrxCtysogzeLehTZT3xbD8W9P+Na63eA06BnrDWaXpLG5NiTdis/hRHwnIgwiOwcn3OdPNx7OjlzrtvwutUWC6nMOSdDHvZYFaWHIyvyFhljUbTTgW2QSJpX+IR1sopVha0G1SBxNazbs/9TGihjl4tc4Jg2e8D6ScOSJGxo1DzysyLxchw3A+1VVwiBTLEy4xS4nL14z2XSMcRrj5Pl9dO7r1O7G3+d5ubFNoq1/Ql+SzKRaxaMaP87CUJUxCrPffhcTvE4vihPOb7okcRgl3REvXdp9VSCmeTCZ9Gm/mBScazqQoGBbzcTDuCvc1zRG05qsvlwIDAQAB";

        String pub = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh+mOu6wdFTBftNwBpLsc5uw8UsSA0q3o6TCzCdFQXMvGkm27vSLjrZnMiPV+L13D6S2FK53N/Fc/ClqoaAtWTxCVZaoPCV28g296FJbVyIqGKZ6gh6GT2YRiDDtmrZkbRMpqtZzmOPgSuKb1SRpY9JmtLWnN3NSxUhWsi+bcAdzA1mC8Y2WgcMFQOHEDISY3/YDufVj4pXutOs/El0+kIY35so7lO1qnWLgZ9PxSs+Hb+zU2Oe/YMjDHPfGj+OQ7xT0L0s6/VRe7nDalm0vWqZeEk+Gky9oKMqGvlA6MovNo3kS5pWJvFxhaqC88yVdmnJvgKO6FwoxUMh4CQtKreQIDAQAB";
        System.out.println(privateKey.length());
        System.out.println(publicKey.length());
        System.out.println(cusPubKey.length());


        data = rSAUtil.encryptByPrivateKey(privateKey, data);
        try { 
        
            String datas = rSAUtil.decryptByPublicKey(publicKey, data);
            System.out.println(datas.equals("123456789"));
            System.out.println(datas);
            System.out.println(pub.length());

        } catch (Exception e) { 
        
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

  1. ECC、Diffie-Hellman、EI Gamal、DSA

4.4 应用场景

  1. 加解密
    可以通过公钥加密,对应的就是私钥解密; 也可以使用私钥加密, 对应的就是公钥解密
  2. 数字签名
    发送方A: 原始内容 --> 把原始内容用发送方的私钥加密 --> 通过摘要算法获取私钥加密后的hash值str1 --> 数字签名
    接收方B: 对接收到的密文进行摘要算法得到str2,比较str2和str1是否相同(验签,防篡改) --> 验签成功 --> 用发送方的公钥解密接收到的内容,得到原始内容
  3. 数字信封
  4. 数字证书
锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章