Java中常见的密码学知识
现代密码学
- 散列函数
散列函数,也见杂凑函数、摘要函数或哈希函数,可将任意长度的消息经过运算,变成固定长度数值,常见的有MD5、SHA-1、SHA256,多应用在文件校验,数字签名中。
MD5 可以将任意长度的原文生成一个128位(16字节)的哈希值
SHA-1可以将任意长度的原文生成一个160位(20字节)的哈希值
-
对称密码
对称密码应用了相同的加密密钥和解密密钥。对称密码分为:序列密码(流密码),分组密码(块密码)两种。流密码是对信息流中的每一个元素(一个字母或一个比特)作为基本的处理单元进行加密,块密码是先对信息流分块,再对每一块分别加密。
例如原文为1234567890,流加密即先对1进行加密,再对2进行加密,再对3进行加密……最后拼接成密文;块加密先分成不同的块,如1234成块,5678成块,90XX(XX为补位数字)成块,再分别对不同块进行加密,最后拼接成密文。 -
非对称密码
对称密码的密钥安全极其重要,加密者和解密者需要提前协商密钥,并各自确保密钥的安全性,一但密钥泄露,即使算法是安全的也无法保障原文信息的私密性。
在实际的使用中,远程的提前协商密钥不容易实现,即使协商好,在远程传输过程中也容易被他人获取,因此非对称密钥此时就凸显出了优势。
非对称密码有两支密钥,公钥(publickey)和私钥(privatekey),加密和解密运算使用的密钥不同。用公钥对原文进行加密后,需要由私钥进行解密;用私钥对原文进行加密后(此时一般称为签名),需要由公钥进行解密(此时一般称为验签)。公钥可以公开的,大家使用公钥对信息进行加密,再发送给私钥的持有者,私钥持有者使用私钥对信息进行解密,获得信息原文。因为私钥只有单一人持有,因此不用担心被他人解密获取信息原文。
如何设置密码才安全
- 密码不要太常见,不要使用类似于12345678这样的常用密码
- 各应用软件密码建议不要一样,容易被攻击者撞库破解
- 可在设置密码时增加注册时间、注册地点等方法
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
案例如下:
package com.example.test.security;import java.io.UnsupportedEncodingException;public class test {public static void main(String[] args) throws UnsupportedEncodingException {String a = "ab";byte[] bytes = a.getBytes();//byte实际上就是ascii码,一个英文占用一个字节for (byte b : bytes) {int c = b;System.out.println(c);//获取byte对应的bitString s = Integer.toBinaryString(c);System.out.println(s);}System.out.println("========================");a = "中国";bytes = a.getBytes("gbk");//byte实际上就是ascii码.中文的编码格式默认是utf-8,一个中文占用3个字节,gbk格式,一个中文占用2个字节for (byte b : bytes) {int c = b;System.out.println(c);//获取byte对应的bitString s = Integer.toBinaryString(c);System.out.println(s);}}
}
运行截图:
常见的加密方式
对称加密
- 采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
- 示例
- 我们现在有一个原文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使用
DES加解密案例如下
package com.example.test.security;import com.sun.org.apache.xml.internal.security.utils.Base64;import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
public class test {// DES加密算法,key的大小必须是8个字节public static void main(String[] args) throws Exception {String input ="中国";// DES加密算法,key的大小必须是8个字节String key = "12dfdade";String transformation = "DES"; // 9PQXVUIhaaQ=// 指定获取密钥的算法String algorithm = "DES";String encryptDES = encryptDES(input, key, transformation, algorithm);System.out.println("加密:" + encryptDES);String s = decryptDES(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);//如果不使用base64,直接使用new String(bytes)那么输出的结果有可能是乱码,因为对应的字节可能出现负数,没有出现在ascii码中return encode;}/*** 使用DES解密** @param input : 密文* @param key : 密钥* @param transformation : 获取Cipher对象的算法* @param algorithm : 获取密钥的算法* @throws Exception* @return: 原文*/private static String decryptDES(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. 解密,上面使用的base64编码,下面直接用密文byte[] bytes = cipher.doFinal(Base64.decode(input));// 因为是明文,所以直接返回return new String(bytes);}
}
AES加解密案例如下
package com.example.test.security;import com.sun.org.apache.xml.internal.security.utils.Base64;import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
public class test {// DES加密算法,key的大小必须是8个字节public static void main(String[] args) throws Exception {String input ="中国";// AES加密算法,key的大小必须是16个字节String key = "1234567812345678";String transformation = "AES";// 指定获取密钥的算法String algorithm = "AES";String encryptDES = encryptAES(input, key, transformation, algorithm);System.out.println("加密:" + encryptDES);String s = decryptAES(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 encryptAES(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 decryptAES(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. 解密,上面使用的base64编码,下面直接用密文byte[] bytes = cipher.doFinal(Base64.decode(input));// 因为是明文,所以直接返回return new String(bytes);}
}
-
Base64算法简介
Base64是网络上最常见的用于传输8Bit字节码的可读性编码算法之一,可读性编码算法不是为了保护数据的安全性,而是为了可读性;可读性编码不改变信息内容,只改变信息内容的表现形式
所谓Base64,即是说在编码过程中使用了64种字符:大写A到Z、小写a到z、数字0到9、“+”和“/”
Base58是Bitcoin(比特币)中使用的一种编码方式,主要用于产生Bitcoin的钱包地址。相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"i",以及"+“和”/"符号。 -
Base64算法原理
base64 是 3个字节为一组,一个字节 8位,一共 就是24位 ,然后,把3个字节转成4组,每组6位,3 * 8 = 4 * 6 = 24 ,每组6位,缺少的2位,会在高位进行补0 ,这样做的好处在于 ,base取的是后面6位,去掉高2位 ,那么base64的取值就可以控制在0-63位了,所以就叫base64,111 111 = 32 + 16 + 8 + 4 + 2 + 1 。 -
Base64构成原则
- 小写 a-z=26个字母
- 大写A-Z=26个字母
- 数字0-9=10个数字
- +/=2个符号,大家可能发现一个问题,base64有个 = 号,但是在映射表里面没有发现 = 号,这个地方需要注意,等号非常特殊,因为base64是三个字节一组 ,如果当我们的位数不够的时候,会使用等号来补齐。
-
base64补等号测试
public class TestBase64 {public static void main(String[] args) {// 1:MQ== 表示一个字节,不够三个字节,所以需要后面通过 == 号补齐System.out.println(Base64.encode("1".getBytes()));//后面补2个等号==
// System.out.println(Base64.encode("12".getBytes()));//补1个等号=
// System.out.println(Base64.encode("123".getBytes()));//不用补等号
// // 中国:中文占6个字节,6 * 8 = 48 ,刚刚好被整除,所以没有等号
// System.out.println(Base64.encode("中国".getBytes()));}
}
加密模式
填充模式
加密模式和填充模式案例
package com.example.test.security;import com.sun.org.apache.xml.internal.security.utils.Base64;import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;public class test {// DES加密算法,key的大小必须是8个字节public static void main(String[] args) throws Exception {String input = "硅谷12";// DES加密算法,key的大小必须是8个字节String key = "12345678";// 指定获取Cipher的算法,如果没有指定加密模式和填充模式,ECB/PKCS5Padding就是默认值// String transformation = "DES"; // 9PQXVUIhaaQ=//String transformation = "DES/ECB/PKCS5Padding"; // 9PQXVUIhaaQ=// CBC模式,必须指定初始向量,初始向量中密钥的长度必须是8个字节//String transformation = "DES/CBC/PKCS5Padding"; // 9PQXVUIhaaQ=// NoPadding模式,原文的长度必须是8个字节的整倍数 ,所以必须把 硅谷改成硅谷12,PKCS5Padding则不需要,会自动填充String transformation = "DES/CBC/NoPadding";// 指定获取密钥的算法String algorithm = "DES";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: 解密模式// 初始向量,参数表示跟谁进行异或,初始向量的长度必须是8位IvParameterSpec iv = new IvParameterSpec(key.getBytes());// 初始化加密模式和算法cipher.init(Cipher.ENCRYPT_MODE, sks, iv);// 加密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);IvParameterSpec iv = new IvParameterSpec(key.getBytes());cipher.init(Cipher.DECRYPT_MODE, sks, iv);// 3. 解密byte[] bytes = cipher.doFinal(Base64.decode(input));return new String(bytes);}}
在测试 AES 的时候需要注意,key需要16个字节,加密向量也需要16个字节 ,其他方式跟 DES 一样
消息摘要
-
消息摘要(Message Digest)又称为数字摘要(Digital Digest)
-
它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生
-
使用数字摘要生成的值是不可以篡改的,为了保证文件或者值的安全
-
特点:无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。例如应用MD5算法摘要的消息有128个比特位,用SHA-1算法摘要的消息最终有160比特位的输出,只要输入的消息不同,对其进行摘要以后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出。消息摘要是单向、不可逆的。
获取字符串消息摘要
package com.example.test.security;import com.sun.org.apache.xml.internal.security.utils.Base64;import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;public class test {// DES加密算法,key的大小必须是8个字节public static void main(String[] args) throws Exception{// 4124bc0a9335c27f086f24ba207a4912 md5 在线校验// QSS8CpM1wn8IbyS6IHpJEg== 消息摘要使用的是16进制// 原文String input = "aa";// 算法String algorithm = "MD5";// 获取数字摘要对象MessageDigest messageDigest = MessageDigest.getInstance(algorithm);// 消息数字摘要byte[] digest = messageDigest.digest(input.getBytes());
// System.out.println(new String(digest));//会有乱码出现// base64编码
// System.out.println(Base64.encode(digest));//base64编码的形式// 创建对象用来拼接StringBuilder sb = new StringBuilder();for (byte b : digest) {// 转成 16进制String s = Integer.toHexString(b & 0xff);//System.out.println(s);if (s.length() == 1){// 如果生成的字符只有一个,前面补0s = "0"+s;}sb.append(s);}System.out.println(sb.toString());}
}
其他数字摘要算法
public class test {public static void main(String[] args) throws Exception{// 4124bc0a9335c27f086f24ba207a4912 md5 在线校验// QSS8CpM1wn8IbyS6IHpJEg== 消息摘要使用的是16进制// 原文String input = "aa";// 算法String algorithm = "MD5";// 获取数字摘要对象String md5 = getDigest(input, "MD5");System.out.println(md5);String sha1 = getDigest(input, "SHA-1");System.out.println(sha1);String sha256 = getDigest(input, "SHA-256");System.out.println(sha256);String sha512 = getDigest(input, "SHA-512");System.out.println(sha512);}private static String toHex(byte[] digest) throws Exception {// System.out.println(new String(digest));// base64编码
// System.out.println(Base64.encode(digest));// 创建对象用来拼接StringBuilder sb = new StringBuilder();for (byte b : digest) {// 转成 16进制String s = Integer.toHexString(b & 0xff);if (s.length() == 1){// 如果生成的字符只有一个,前面补0s = "0"+s;}sb.append(s);}System.out.println("16进制数据的长度:" + sb.toString().getBytes().length);return sb.toString();}private static String getDigest(String input, String algorithm) throws Exception {MessageDigest messageDigest = MessageDigest.getInstance(algorithm);// 消息数字摘要byte[] digest = messageDigest.digest(input.getBytes());System.out.println("密文的字节长度:" + digest.length);return toHex(digest);}}
获取文件消息摘要
public class DigestDemo {public static void main(String[] args) throws Exception{String algorithm = "MD5";String sha1 = getDigestFile("apache-tomcat-9.0.10-windows-x64.zip", "SHA-1");System.out.println(sha1);String sha512 = getDigestFile("apache-tomcat-9.0.10-windows-x64.zip", "SHA-512");System.out.println(sha512);}private static String getDigestFile(String filePath, String algorithm) throws Exception{FileInputStream fis = new FileInputStream(filePath);int len;byte[] buffer = new byte[1024];ByteArrayOutputStream baos = new ByteArrayOutputStream();while ( (len = fis.read(buffer))!=-1){baos.write(buffer,0,len);}// 获取消息摘要对象MessageDigest messageDigest = MessageDigest.getInstance(algorithm);// 获取消息摘要byte[] digest = messageDigest.digest(baos.toByteArray());System.out.println("密文的字节长度:"+digest.length);return toHex(digest);}private static String toHex(byte[] digest) {// System.out.println(new String(digest));// 消息摘要进行表示的时候,是用16进制进行表示StringBuilder sb = new StringBuilder();for (byte b : digest) {// 转成16进制String s = Integer.toHexString(b & 0xff);// 保持数据的完整性,前面不够的用0补齐if (s.length()==1){s="0"+s;}sb.append(s);}System.out.println("16进制数据的长度:"+ sb.toString().getBytes().length);return sb.toString();}
}
总结
- MD5算法 : 摘要结果16个字节, 转16进制后32个字节
- SHA1算法 : 摘要结果20个字节, 转16进制后40个字节
- SHA256算法 : 摘要结果32个字节, 转16进制后64个字节
- SHA512算法 : 摘要结果64个字节, 转16进制后128个字节
非对称加密
- 非对称加密算法又称现代加密算法。
- 非对称加密是计算机通信安全的基石,保证了加密数据不会被破解。
- 与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(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
生成公钥和私钥
package com.example.test.security;import com.sun.org.apache.xml.internal.security.utils.Base64;import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;public class test {public static void main(String[] args) throws Exception {// 加密算法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);// 打印私钥System.out.println(privateKeyString);// 打印公钥System.out.println(publicKeyString);}
}
利用公钥和私钥进行加解密
package com.example.test.security;import com.sun.org.apache.xml.internal.security.utils.Base64;import javax.crypto.Cipher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;public class test {public static void main(String[] args) throws Exception {// 加密算法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);// 打印私钥
// System.out.println(privateKeyString);// 打印公钥
// System.out.println(publicKeyString);//创建加密对象//参数表示加密算法Cipher cipher = Cipher.getInstance(algorithm);//私钥加密cipher.init(Cipher.ENCRYPT_MODE, privateKey);byte[] bytes = cipher.doFinal("中国".getBytes());System.out.println(Base64.encode(bytes));//公钥解密cipher.init(Cipher.DECRYPT_MODE, publicKey);byte[] bytes1 = cipher.doFinal(bytes);System.out.println(new String(bytes1));//公钥加密cipher.init(Cipher.ENCRYPT_MODE, publicKey);bytes = cipher.doFinal("中国".getBytes());System.out.println(Base64.encode(bytes));//私钥解密cipher.init(Cipher.DECRYPT_MODE, privateKey);bytes1 = cipher.doFinal(bytes);System.out.println(new String(bytes1));}}
数字签名
数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。它是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名是非对称密钥加密技术与数字摘要技术的应用。
数字签名的含义是:在网络中传输数据时候,给数据添加一个数字签名,表示是谁发的数据,而且还能证明数据没有被篡改。
OK,数字签名的主要作用就是保证了数据的有效性(验证是谁发的)和完整性(证明信息没有被篡改)。下面我们就来好好地看一下他的底层实现原理是什么样子的。
-
基本原理
为了理解得清楚,我们通过案例一步一步来讲解。话说张三有俩好哥们A、B。由于工作原因,张三和AB写邮件的时候为了安全都需要加密。于是张三想到了数字签名:整个思路是这个样子的:
第一步:加密采用非对称加密,张三有三把钥匙,两把公钥,送给朋友。一把私钥留给自己。
第二步:A或者B写邮件给张三:A先用公钥对邮件加密,然后张三收到邮件之后使用私钥解密。
第三步:张三写邮件给A或者B:
(1)张三写完邮件,先用hash函数生成邮件的摘要,附着在文章上面,这就完成了数字签名,然后张三再使用私钥对摘要加密。就可以把邮件发出去了。
(2)A或者是B收到邮件之后,先把数字签名取下来,然后使用自己的公钥解密即可。这时候取下来的数字签名中的摘要若和张三的一致,那就认为是张三发来的,再对信件本身使用Hash函数,将得到的结果,与上一步得到的摘要进行对比。如果两者一致,就证明这封信未被修改过。
-
数字证书
上面提到我们对签名进行验证时,需要用到公钥。如果公钥是伪造的,那我们无法验证数字签名了,也就根本不可能从数字签名确定对方的合法性了。这时候证书就闪亮登场了。那么这个证书是如何生成的呢?
关于证书这块,我们通一个实例:“https协议”来讲解。
- 代码实现
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));}}
keytool工具使用
-
常用命令:
生成keypair
keytool -genkeypair keystore xxx.jks
keytool -genkeypair -alias lisi keystore xxx.jks(后面部分是为证书指定别名,否则采用默认的名称为mykey) -
看看keystore中有哪些项目:
keytool -list xxx.jks 或keytool -list -v xxx.jks
keytool -exportcert -alias lisi -file lisi.cer -
生成可打印的证书:
keytool -exportcert -alias lisi -file lisi.cer –rfc -
显示数字证书文件中的证书信息:
keytool -printcert -file lisi.cer
直接双击lisi.cer,用window系统的内置程序打开lisi.cer -
导出公钥
openssl是一个加解密工具包,这里使用openssl来导出公钥信息。
安装 openssl:http://slproweb.com/products/Win32OpenSSL.html
配置完环境变量之后,便可以使用openssl导出密钥的公钥了keytool -list -rfc --keystore xxx.jks | openssl x509 -inform pem -pubkey
相关文章:

Java中常见的密码学知识
现代密码学 散列函数 散列函数,也见杂凑函数、摘要函数或哈希函数,可将任意长度的消息经过运算,变成固定长度数值,常见的有MD5、SHA-1、SHA256,多应用在文件校验,数字签名中。 MD5 可以将任意长度的原文生…...

Leetcode.2171 拿出最少数目的魔法豆
题目链接 Leetcode.2171 拿出最少数目的魔法豆 Rating : 1748 题目描述 给你一个 正 整数数组 beans,其中每个整数表示一个袋子里装的魔法豆的数目。 请你从每个袋子中 拿出 一些豆子(也可以 不拿出),使得剩下的 非空…...

day1 计算机组成与结构考点汇总
一、重点知识点 计算机硬件组成、运算器、控制器奇偶校验码、循环冗余校验码、海明码指令系统:指令操作数寻址方式、CISC和RISC、指令流水线的计算存储系统:分级存储、局部性原理、cache、主存编址计算、磁盘输入输出技术:程序查询方式、中断…...

Java虚拟机的类加载机制
Java虚拟机的类加载机制综述类的生命周期类加载器双亲委派模型---综述 我们编写的Java代码如何能在一个操作系统上运行呢?一般来说,我们使用javac命令将.java文件编译成.class文件,也就是Java字节码文件,然后由JVM将字节码文件加…...

分治法实现合并排序(归并排序),理解分治算法思想,实现分治算法的完美例子合并排序(含码源与解析)
🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨,经典算法的解析✨都在这儿,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏 🪔本系列专栏 -…...

Typescript 类 (class)
基本用法 (通过关键字 class) // 基本用法 class VueService {constructor() {} // 构造器 } 类的约束(通过关键字 implements) // 接口定义属性类型 interface VueProps {name: stringinit: () > void }// 约束类 class VueService implements Vue…...

KDZD程控超低频高压发生器
一、产品概述 本产品接合了现代数字变频技术,采用微机控制,升压、降压、测量、保护自动化。由于电子化,所以体积小重量轻、大屏幕液晶显示,清晰直观、且能显示输出波形、打印试验报告。 设计指标符合《电力设备专用测试仪器通用…...

【华为OD机试 2023最新 】 过滤组合字符串(C++)
文章目录 题目描述输入描述输出描述用例题目解析C++题目描述 数字0、1、2、3、4、5、6、7、8、9分别关联 a~z 26个英文字母。 0 关联 “a”,”b”,”c”1 关联 “d”,”e”,”f”2 关联 “g”,”h”,”i”3 关联 “j”,”k”,”l”4 关联 “m”,”n”,”o”5 关联 “p”,”q”…...

Java笔记034-坦克大战【2】
目录 坦克大战【2】 线程-应用到坦克大战 坦克大战0.3 思路分析: 代码实现: 坦克大战0.4 增加功能 特别说明 思路分析: 代码实现: 坦克大战0.5 增加功能 思路分析: 代码实现: 坦克大战【2】 …...

【算法】【数组与矩阵模块】桶排序思想解决无序数组排序后相邻数间的最大差值
目录前言问题介绍解决方案代码编写java语言版本c语言版本c语言版本思考感悟写在最后前言 当前所有算法都使用测试用例运行过,但是不保证100%的测试用例,如果存在问题务必联系批评指正~ 在此感谢左大神让我对算法有了新的感悟认识! 问题介绍 …...

C语言—函数
函数库函数自定义函数函数的参数函数的调用函数的嵌套调用和链式访问函数的声明和定义函数递归递归与迭代函数递归的经典题目维基百科(台湾方面维护的,翻译形式跟大陆有所差异)中对函数的定义:子程序在计算机科学中,子…...

Autosar模式管理实战系列03-基于Davinci工具的WDGM配置
本文框架 前言1.WdgMConfigSet 配置2. 新建监控实体(SE)2.1 新建检测点(Checkpoint)2.2 设置 WdgMInternalTransitions3. WdgMLocalStatusParams配置4. WdgMAliveSupervision配置5. 代码插入指导前言 前面我们介绍了WdgM(看门狗管理)是一个 AutoSAR 的基础模块,负责管理看门…...

AutoML-sklearn and torch
一、auto-sklearn 1.1 环境依赖 额外安装swig 第三方库 linux 支持, mac,windows不支持 1.2 示例代码 time_left_for_this_task 设定任务最大时间 per_run_time_limit 每个子任务最大训练时间 include 可以限制任务训练的模型 import autosklearn.classific…...

《扬帆优配》算力概念股大爆发,主力资金大扫货
3月22日,9股封单金额超亿元,工业富联、鸿博股份、鹏鼎控股分别为3.01亿元、2.78亿元、2.37亿元。 今日三大指数团体收涨,收盘共34股涨停,首要集中于数字经济方向,其间云核算、CPO大迸发。除去5只ST股,算计2…...

机械臂+底盘三维模型从solidworks到moveit配置功能包
文章目录 导出底盘STEP加载机械臂模型组合机械臂和底盘三维模型导出URDF在moveit中进行配置新建工作目录设置ROS工作空间的环境变量进入moveit setup加载URDF文件self-CollisionsPlanning groupsRobot posesControllersSimulationAuthor information生成配置包在rviz中进行可视…...

高并发系统设计:缓存、降级、限流、(熔断)
高并发系统设计:缓存、降级、限流、(熔断) 在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。 非核心服务可以采用降级、熔断,核心服务采用缓存和限流(隔离流量可以最大限度的保障业务无损)。 缓存 缓…...

《辉煌优配》放量大涨,A股成交额重回万亿!PCB板块继续领跑
多只绩优PCB概念股超跌。 今日A股放量反弹,成交额从头站上万亿关口。芯片板块掀涨停潮,景嘉微、芯原股份20cm涨停,紫光国微、兆易创新、跃岭股份等封板;AI算力、存储器、光模块、云核算等板块全线拉升,板块内个股再度批…...

Vue封装的过度与动画
动画效果 先把样式封装好,然后设置一个动画 不需要vue也能实现的动画的效果,我们只需要判断一下,然后动态的添加和删除类名即可 那能不能不自己写动态,就靠vue 首先我们要靠<transition>标签把需要动画的包裹起来 vue中…...

流量监控-ntopng
目录介绍安装使用介绍 ntopng是原始ntop的下一代版本,ntop是监视网络使用情况的网络流量探测器。ntopng基于libpcap,并且以可移植的方式编写,以便实际上可以在每个Unix平台,MacOSX和Windows上运行。 ntopng(是的&…...

C++ 21 set容器
目录 一、set容器 1.1 简介 1.2 构造和赋值 1.3 大小和交换 1.4 插入和删除 1.5 查找和统计 1.6 set和multiset区别 1.7 内置类型指定排序规则 1.8 自定义数据类型指定排序规则 一、set容器 1.1 简介 ① set容器中所有元素在插入时自动被排序。 ② set容器和multise…...

什么是JWT
JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。 传统的session认证 http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一…...

Gradle7.4安装
前置:本文基于IntelliJ IDEA 2022.2.1 、jdk1.8进行安装 目录 1.挑选Gradle版本 2.系统变量设置 1.挑选Gradle版本 gradle兼容性差, 1.跟idea会有版本问题。 2.跟springboot也有兼容问题Spring Boot Gradle Plugin Reference Guide 首先查询版本&…...

【华为OD机试 2023最新 】 箱子之字形摆放(C++ 100%)
文章目录 题目描述输入描述输出描述备注用例题目解析C++题目描述 有一批箱子(形式为字符串,设为str), 要求将这批箱子按从上到下以之字形的顺序摆放在宽度为 n 的空地,请输出箱子的摆放位置。 例如:箱子ABCDEFG,空地宽度为3,摆放结果如图: 则输出结果为: AFG BE CD …...

Matplotlib库入门
Matplotlib库的介绍 什么是Matplotlib库? Matplotlib是一个Python的数据可视化库,用于绘制各种类型的图表,包括线图、散点图、条形图、等高线图、3D图等等。它是一个非常强大和灵活的库,被广泛用于数据科学、机器学习、工程学、…...

学生党用什么蓝牙耳机比较好?300内高性价比蓝牙耳机排行
随着蓝牙技术的发展,蓝牙耳机越来越普及,不同价位、不同性能的蓝牙耳机数不胜数。那么,学生党用什么蓝牙耳机比较好?下面,我来给大家推荐几款三百内高性价比蓝牙耳机,一起来看看吧。 一、南卡小音舱蓝牙耳…...

Lambda 表达式与函数式接口
函数式接口 如果一个接口,只有一个抽象方法,该接口即为函数式接口。函数式接口,即可使用 Lambda 表达式。 如下面的接口 public interface Translate {void translate();}目前该接口的抽象方法为无参数无返回值 Lambda 表达式 无参无返回值…...

后端代码规范
1、报文入参尽量避免使用实体类(如果用实体类接受参数,一定要写好注解,具体用到了实体类的哪一个属性) /*** * Description: 新增玉米观测记录主表信息* param param params* param return 参数* return Result 返回类型* author…...

web自动化测试:Selenium+Python基础方法封装(建议收藏)
01、目的 web自动化测试作为软件自动化测试领域中绕不过去的一个“香饽饽”,通常都会作为广大测试从业者的首选学习对象,相较于C/S架构的自动化来说,B/S有着其无法忽视的诸多优势,从行业发展趋、研发模式特点、测试工具支持&…...

while实现1到100相加求和-课后程序(JavaScript前端开发案例教程-黑马程序员编著-第2章-课后作业)
【案例2-7】while实现1到100相加求和 一、案例描述 考核知识点 while循环语句 练习目标 掌握while循环语句。 需求分析 1-100之间的数相加求和,本案例通过while循环语句来实现。 案例分析 效果如图2-10所示。1-100所有数的和 具体实现步骤如下: 在&l…...

Thingsboard(2.4 postgresql版)数据库表结构说明
本文描述的表结构是根据thingsboard2.4(postgresql版)数据库中整理出来的,不一定完整,后续有新的发现再补充文档。 一、数据库E-R关系 Thingsboard2.4社区版共22个表,主要包括实体信息表、关系信息表、字典表和系统配…...