java密码安全
目录
密码学
古典密码学
现代密码学
ASCII编码
Byte和bit
常见加密方式
加密模式
填充模式
非对称加密
数字签名
密码学
密码学是网络安全、信息安全、区块链等产品的基础,常见的非对称加密、对称加密、散列函数等,都属于密码学范畴。
密码学有数千年的历史,从最开始的替换发到如今的非对称加密算法,经历了古典密码学,近代密码学和现代密码学三个阶段。
古典密码学
1. 替换法
替换法就是用固定的信息将原文替换成无法直接阅读的密文信息,有单表替换和多表替换两种形式。
单表替换即只有一张原文密文对照表单,发送者和接收者用这张表单来加密解密。
多表替换即有多张原文密文对照表单,不同字母可以用不同表单的内容替换。
2. 移位法
移位法就是将原文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后得出密文,典型的移位法应用有 “ 恺撒密码 ”。
现代密码学
1. 散列函数
散列函数也称哈希函数,可将任意长度的消息经过运算,变成固定长度数值,常见的有MD5、SHA-1、SHA256,多应用在文件校验、数字签名中。
MD5可以将任意长度的原文件生成一个128位(16字节)的哈希值。
SHA-1可以将任意长度的原文件生成一个160位(20字节)的哈希值。
2. 对称密码
对称加密应用了相同的加密密钥和解密密钥。对称密码分为:序列密码(流密码)、分组密码(块密码)两种。
流密码是对信息流中的每一个元素(一个字母或一个比特)作为基本的处理单元进行加密,块密码是先对信息流分块。再对每一块分别加密。
3. 非对称密码
对称密码的密钥安全极其重要,加密这和解密这需要提前协商密钥,并各自确保密钥的安全性,一旦密钥泄露,即使算法是安全的也无法保障原文信息的私密性。
在世界的使用中,远程的提前协商密钥不容易实现,及时协商好,在远程传输过程中也容易被他人获取,因此非对称密钥就凸显出了优势。
非对称密码有两支密钥,公钥和私钥,加密和解密运算使用的密钥不同。用公钥对原文进行加密后,由私钥进行解密;用私钥对原文进行加密后(此时一般称为签名),需要由公钥进行解密(此时一般称为验签)。公钥可以公开的,大家使用公钥对信息进行加密,再发送给私钥持有者,私钥持有者使用私钥对信息进行解密,获得信息原文。因此私钥只有单一人持有,故不用担心被他人解密获取信息原文。
ASCII编码
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统,并等同于国际标准ISO/IEC 646。
Byte和bit
Byte : 字节. 数据存储的基本单位,比如移动硬盘1T , 单位是byte
bit : 比特, 又叫位. 一个位要么是0要么是1. 数据传输的单位 , 比如家里的宽带100MB,下载速度并没有达到100MB,一般都是12-13MB,那么是因为需要使用 100 / 8
关系: 1Byte = 8bit
获取字符串byte
public class ByteBit {public static void main(String[] args) {String a = "a";byte[] bytes = a.getBytes();for (byte b : bytes) {int c=b;// 打印发现byte实际上就是ascii码System.out.println(c);}}
}
97
byte对应bit
public class ByteBit {public static void main(String[] args) {String a = "a";byte[] bytes = a.getBytes();for (byte b : bytes) {int c=b;// 打印发现byte实际上就是ascii码System.out.println(c);// 我们在来看看每个byte对应的bit,byte获取对应的bitString s = Integer.toBinaryString(c);System.out.println(s);}}
}
97
1100001
运行程序
打印出来应该是8个bit,但前面是0,没有打印 ,从打印结果可以看出来,一个英文字符 ,占一个字节。
在中文情况下:
如果使用的是utf-8编码格式,一个中文对应3个字节。
如果使用的是gbk编码格式,一个中文对应2个字节。
在英文情况下
英文在不同的编码格式占用的字节是一样的。没有编码的概念,全部对应的是一个字节。
常见加密方式
1. 对称加密
- 采用单钥密码系统的加密方法,统一密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称单密钥加密。
- 示例
- 我们现在有一个原文3要发送给B
- 设置密钥为108, 3 * 108 = 324, 将324作为密文发送给B
- B拿到密文324后, 使用324/108 = 3 得到原文
- 常见加密算法
- DES : Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
- AES : Advanced Encryption Standard, 高级加密标准 .在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
- 特点
- 加密速度快, 可以加密大文件
- 密文可逆, 一旦密钥文件泄漏, 就会导致数据暴露
- 加密后编码表找不到对应字符, 出现乱码
- 一般结合Base64使用,解决乱码的问题
2. DES/AES加解密
注意:
DES加密算法的密钥key必须是8个字节。
AES加密算法的秘钥key必须是16个字节。
package com.atguigu.desaes;
import com.sun.org.apache.xml.internal.security.utils.Base64;import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;public class AesDemo {// DES加密算法,key的大小必须是8个字节public static void main(String[] args) throws Exception {String input ="测试";// AES加密算法,比较高级,所以key的大小必须是16个字节String key = "1234567812345678";String transformation = "AES"; // 9PQXVUIhaaQ=// 指定获取密钥的算法String algorithm = "AES";// 先测试加密,然后在测试解密String encryptDES = encryptDES(input, key, transformation, algorithm);System.out.println("加密:" + encryptDES);String s = dncryptDES(encryptDES, key, transformation, algorithm);System.out.println("解密:" + s);}/*** 使用DES加密数据** @param input : 原文* @param key : 密钥(DES,密钥的长度必须是8个字节)* @param transformation : 获取Cipher对象的算法* @param algorithm : 获取密钥的算法* @return : 密文* @throws Exception*/private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {// 获取加密对象Cipher cipher = Cipher.getInstance(transformation);// 创建加密规则// 第一个参数key的字节// 第二个参数表示加密算法SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);// ENCRYPT_MODE:加密模式// DECRYPT_MODE: 解密模式// 初始化加密模式和算法cipher.init(Cipher.ENCRYPT_MODE,sks);// 加密byte[] bytes = cipher.doFinal(input.getBytes());// 输出加密后的数据String encode = Base64.encode(bytes);return encode;}/*** 使用DES解密** @param input : 密文* @param key : 密钥* @param transformation : 获取Cipher对象的算法* @param algorithm : 获取密钥的算法* @throws Exception* @return: 原文*/private static String dncryptDES(String input, String key, String transformation, String algorithm) throws Exception {// 1,获取Cipher对象Cipher cipher = Cipher.getInstance(transformation);// 指定密钥规则SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);cipher.init(Cipher.DECRYPT_MODE, sks);// 3. 解密byte[] bytes = cipher.doFinal(Base64.decode(input));return new String(bytes);}
}
toString()与new String ()用法区别
str.toString是调用了这个object对象的类的toString方法。一般是返回这么一个String:[class name]@[hashCode]
new String(str)是根据parameter是一个字节数组,使用java虚拟机默认的编码格式,将这个字节数组decode为对应的字符。若虚拟机默认的编码格式是ISO-8859-1,按照ascii编码表即可得到字节对应的字符。
什么时候用什么方法呢?
new String()一般使用字符转码的时候,byte[]数组的时候
toString()对象打印的时候使用
加密模式
Cipher (Java Platform SE 8 )
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
ECB : Electronic codebook, 电子密码本. 需要加密的消息按照块密码的块大小被分为数个块,并对每个块进行独立加密

- 优点 : 可以并行处理数据
- 缺点 : 同样的原文生成同样的密文, 不能很好的保护数据
- 同时加密,原文是一样的,加密出来的密文也是一样的
CBC
CBC : Cipher-block chaining, 密码块链接. 每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块

- 优点 : 同样的原文生成的密文不一样
- 缺点 : 串行处理数据.
填充模式
- 当需要按块处理的数据, 数据长度不符合块处理需求时, 按照一定的方法填充满块长的规则
NoPadding
- 不填充.
- 在DES加密算法下, 要求原文长度必须是8byte的整数倍
- 在AES加密算法下, 要求原文长度必须是16byte的整数倍
PKCS5Padding
数据块的大小为8位, 不够就补足
Tips
- 默认情况下, 加密模式和填充模式为 : ECB/PKCS5Padding
- 如果使用CBC模式, 在初始化Cipher对象时, 需要增加参数, 初始化向量IV : IvParameterSpec iv = new IvParameterSpec(key.getBytes());
非对称加密
简介:
① 非对称加密算法又称现代加密算法。
② 非对称加密是计算机通信安全的基石,保证了加密数据不会被破解。
③ 与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey) 和私有密(privatekey)
④ 公开密钥和私有密钥是一对
⑤ 如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密。
⑥ 如果用私有密钥对数据进行加密,只有用对应的公开密钥才能解密。
⑦ 因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
- 示例
- 首先生成密钥对, 公钥为(5,14), 私钥为(11,14)
- 现在A希望将原文2发送给B
- A使用公钥加密数据. 2的5次方mod 14 = 4 , 将密文4发送给B
- B使用私钥解密数据. 4的11次方mod14 = 2, 得到原文2
- 特点
- 加密和解密使用不同的密钥
- 如果使用私钥加密, 只能使用公钥解密
- 如果使用公钥加密, 只能使用私钥解密
- 处理数据的速度较慢, 因为安全级别高
- 常见算法
- RSA
- ECC
私钥加密公钥解密
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.io.FileUtils;import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
/*** RSAdemo** @Author: * @CreateTime: 2022-02-13* @Description:*/
public class RSAdemo {public static void main(String[] args) throws Exception {String input = "测试";// 加密算法String algorithm = "RSA";// 创建密钥对生成器对象KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);// 生成密钥对KeyPair keyPair = keyPairGenerator.generateKeyPair();// 生成私钥PrivateKey privateKey = keyPair.getPrivate();// 生成公钥PublicKey publicKey = keyPair.getPublic();// 获取私钥字节数组byte[] privateKeyEncoded = privateKey.getEncoded();// 获取公钥字节数组byte[] publicKeyEncoded = publicKey.getEncoded();// 对公私钥进行base64编码String privateKeyString = Base64.encode(privateKeyEncoded);String publicKeyString = Base64.encode(publicKeyEncoded);// 创建加密对象// 参数表示加密算法Cipher cipher = Cipher.getInstance(algorithm);// 初始化加密// 第一个参数:加密的模式// 第二个参数:使用私钥进行加密cipher.init(Cipher.ENCRYPT_MODE,privateKey);// 私钥加密byte[] bytes = cipher.doFinal(input.getBytes());System.out.println(Base64.encode(bytes));// 公钥进行解密cipher.init(Cipher.DECRYPT_MODE,publicKey);// 对密文进行解密,不需要使用base64,因为原文不会乱码byte[] bytes1 = cipher.doFinal(bytes);System.out.println(new String(bytes1));}
}
保存公钥和私钥至本地,进行读取并实现加解密
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.io.FileUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;/*** RSAdemo** @Author: * @CreateTime: 2022-02-13* @Description:*/
public class RSAdemo {public static void main(String[] args) throws Exception {String input = "测试";// 加密算法String algorithm = "RSA";PrivateKey privateKey = getPrivateKey("a.pri", algorithm);PublicKey publicKey = getPublicKey("a.pub", algorithm);String s = encryptRSA(algorithm, privateKey, input);String s1 = decryptRSA(algorithm, publicKey, s);System.out.println(s1);}//读取公钥public static PublicKey getPublicKey(String pulickPath,String algorithm) throws Exception{// 将文件内容转为字符串String publicKeyString = FileUtils.readFileToString(new File(pulickPath), Charset.defaultCharset());// 获取密钥工厂KeyFactory keyFactory = KeyFactory.getInstance(algorithm);// 构建密钥规范 进行Base64解码X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decode(publicKeyString));// 生成公钥return keyFactory.generatePublic(spec);}//读取私钥public static PrivateKey getPrivateKey(String priPath,String algorithm) throws Exception{// 将文件内容转为字符串String privateKeyString = FileUtils.readFileToString(new File(priPath), Charset.defaultCharset());// 获取密钥工厂KeyFactory keyFactory = KeyFactory.getInstance(algorithm);// 构建密钥规范 进行Base64解码PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyString));// 生成私钥return keyFactory.generatePrivate(spec);}/*** 生成密钥对并保存在本地文件中** @param algorithm : 算法* @param pubPath : 公钥保存路径* @param priPath : 私钥保存路径* @throws Exception*/public static void generateKeyToFile(String algorithm, String pubPath, String priPath) throws Exception {// 获取密钥对生成器KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);// 获取密钥对KeyPair keyPair = keyPairGenerator.generateKeyPair();// 获取公钥PublicKey publicKey = keyPair.getPublic();// 获取私钥PrivateKey privateKey = keyPair.getPrivate();// 获取byte数组byte[] publicKeyEncoded = publicKey.getEncoded();byte[] privateKeyEncoded = privateKey.getEncoded();// 进行Base64编码String publicKeyString = Base64.encode(publicKeyEncoded);String privateKeyString = Base64.encode(privateKeyEncoded);// 保存文件FileUtils.writeStringToFile(new File(pubPath), publicKeyString, Charset.forName("UTF-8"));FileUtils.writeStringToFile(new File(priPath), privateKeyString, Charset.forName("UTF-8"));}/*** 解密数据** @param algorithm : 算法* @param encrypted : 密文* @param key : 密钥* @return : 原文* @throws Exception*/public static String decryptRSA(String algorithm,Key key,String encrypted) throws Exception{// 创建加密对象// 参数表示加密算法Cipher cipher = Cipher.getInstance(algorithm);// 私钥进行解密cipher.init(Cipher.DECRYPT_MODE,key);// 由于密文进行了Base64编码, 在这里需要进行解码byte[] decode = Base64.decode(encrypted);// 对密文进行解密,不需要使用base64,因为原文不会乱码byte[] bytes1 = cipher.doFinal(decode);return new String(bytes1);}/*** 使用密钥加密数据** @param algorithm : 算法* @param input : 原文* @param key : 密钥* @return : 密文* @throws Exception*/public static String encryptRSA(String algorithm,Key key,String input) throws Exception{// 创建加密对象// 参数表示加密算法Cipher cipher = Cipher.getInstance(algorithm);// 初始化加密// 第一个参数:加密的模式// 第二个参数:使用私钥进行加密cipher.init(Cipher.ENCRYPT_MODE,key);// 私钥加密byte[] bytes = cipher.doFinal(input.getBytes());// 对密文进行Base64编码return Base64.encode(bytes);}
}
数字签名
数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。它是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名是非对称密钥加密技术与数字摘要技术的应用。
数字签名的主要作用就是保证了数据的有效性(验证是谁发的)和完整性(证明信息没有被篡改)。
import java.security.*;
import com.sun.org.apache.xml.internal.security.utils.Base64;
public class SignatureDemo {public static void main(String[] args) throws Exception {String a = "123";PublicKey publicKey = RsaDemo.loadPublicKeyFromFile("RSA", "a.pub");PrivateKey privateKey = RsaDemo.loadPrivateKeyFromFile("RSA", "a.pri");String signaturedData = getSignature(a, "sha256withrsa", privateKey);boolean b = verifySignature(a, "sha256withrsa", publicKey, signaturedData);}/*** 生成签名** @param input : 原文* @param algorithm : 算法* @param privateKey : 私钥* @return : 签名* @throws Exception*/private static String getSignature(String input, String algorithm, PrivateKey privateKey) throws Exception {// 获取签名对象Signature signature = Signature.getInstance(algorithm);// 初始化签名signature.initSign(privateKey);// 传入原文signature.update(input.getBytes());// 开始签名byte[] sign = signature.sign();// 对签名数据进行Base64编码return Base64.encode(sign);}/*** 校验签名** @param input : 原文* @param algorithm : 算法* @param publicKey : 公钥* @param signaturedData : 签名* @return : 数据是否被篡改* @throws Exception*/private static boolean verifySignature(String input, String algorithm, PublicKey publicKey, String signaturedData) throws Exception {// 获取签名对象Signature signature = Signature.getInstance(algorithm);// 初始化签名signature.initVerify(publicKey);// 传入原文signature.update(input.getBytes());// 校验数据return signature.verify(Base64.decode(signaturedData));}}
国密算法:SM2 、SM3、SM4;这些加密强度更高,安全性更强,传输速度更快。需要继续的学习探索,欢迎大佬留言指正,望不吝赐教。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
