加密模式:https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.htmlhtml
ECB
ECB : Electronic codebook, 电子密码本. 须要加密的消息按照块密码的块大小被分为数个块,并对每一个块进行独立加密
java
CBC
CBC : Cipher-block chaining, 密码块连接. 每一个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每一个密文块都依赖于它前面的全部明文块git
当须要按块处理的数据, 数据长度不符合块处理需求时, 按照必定的方法填充满块长的规则
NoPadding算法
不填充.
-在DES加密算法下, 要求原文长度必须是8byte的整数倍
-在AES加密算法下, 要求原文长度必须是16byte的整数倍
PKCS5Paddingapache
数据块的大小为8位, 不够就补足windows
Tipsapi
加密模式和填充模式数组
AES/CBC/NoPadding (128) AES/CBC/PKCS5Padding (128) AES/ECB/NoPadding (128) AES/ECB/PKCS5Padding (128) DES/CBC/NoPadding (56) DES/CBC/PKCS5Padding (56) DES/ECB/NoPadding (56) DES/ECB/PKCS5Padding (56) DESede/CBC/NoPadding (168) DESede/CBC/PKCS5Padding (168) DESede/ECB/NoPadding (168) DESede/ECB/PKCS5Padding (168) RSA/ECB/PKCS1Padding (1024, 2048) RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048) RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)
加密模式和填充模式例子tomcat
package com.atguigu.desaes; 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 DesDemo { // DES加密算法,key的大小必须是8个字节 public static void main(String[] args) throws Exception { String input ="硅谷"; // 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 String transformation = "DES/CBC/NoPadding"; // 9PQXVUIhaaQ= // 指定获取密钥的算法 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); // 加密 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); // 3. 解密 byte[] bytes = cipher.doFinal(Base64.decode(input)); return new String(bytes); } }
运行程序:
安全
不管输入的消息有多长,计算出来的消息摘要的长度老是固定的。例如应用MD5算法摘要的消息有128个比特位,用SHA-1算法摘要的消息最终有160比特位的输出
只要输入的消息不一样,对其进行摘要之后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出
消息摘要是单向、不可逆的
常见算法 :
- MD5 - SHA1 - SHA256 - SHA512
百度搜索 tomcat ,进入官网下载 ,会常常发现有 sha1,sha512 , 这些都是数字摘要
public class DigestDemo1 { public static void main(String[] args) throws Exception{ // 原文 String input = "aa"; // 算法 String algorithm = "MD5"; // 获取数字摘要对象 MessageDigest messageDigest = MessageDigest.getInstance(algorithm); // 获取消息数字摘要的字节数组 byte[] digest = messageDigest.digest(input.getBytes()); System.out.println(new String(digest)); } }
运行:
public class DigestDemo1 { public static void main(String[] args) throws Exception{ // 原文 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转码格式的基础上再转成16进制。
public class DigestDemo1 { 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)); // 建立对象用来拼接 StringBuilder sb = new StringBuilder(); for (byte b : digest) { // 转成 16进制 String s = Integer.toHexString(b & 0xff); //System.out.println(s); if (s.length() == 1){ // 若是生成的字符只有一个,前面补0 s = "0"+s; } sb.append(s); } System.out.println(sb.toString()); } }
public class DigestDemo1 { 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){ // 若是生成的字符只有一个,前面补0 s = "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 input = "aa"; String algorithm = "MD5"; // sha1 能够实现秒传功能 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); String md5 = getDigest("aa", "MD5"); System.out.println(md5); String md51 = getDigest("aa ", "MD5"); System.out.println(md51); } 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 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); } 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(); } }
运行程序 ,获取 sha-1 和 sha-512 的值:
查看 tomcat 官网上面 sha-1 和 sha-512 的值:
使用 sha-1 算法,能够实现秒传功能,无论我们如何修改文件的名字,最后获得的值是同样的
总结:
简介:
① 非对称加密算法又称现代加密算法。
② 非对称加密是计算机通讯安全的基石,保证了加密数据不会被破解。
③ 与对称加密算法不一样,非对称加密算法须要两个密钥:公开密钥(publickey) 和私有密(privatekey)
④ 公开密钥和私有密钥是一对
⑤ 若是用公开密钥对数据进行加密,只有用对应的私有密钥才能解密。
⑥ 若是用私有密钥对数据进行加密,只有用对应的公开密钥才能解密。
⑦ 由于加密和解密使用的是两个不一样的密钥,因此这种算法叫做非对称加密算法。
示例:
public class RSAdemo { 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); } }
运行程序:先打印的是私钥 , 后面打印的是公钥。
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)); } }
运行结果:
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)); } }
运行程序 ,由于私钥加密,只能公钥解密:
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)); } }
运行程序:
数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人没法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证实。它是一种相似写在纸上的普通的物理签名,可是使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。一套数字签名一般定义两种互补的运算,一个用于签名,另外一个用于验证。数字签名是非对称密钥加密技术与数字摘要技术的应用。
相信咱们都写过信,在写信的时候落款处老是要留下本身的名字,用来表示写信的人是谁。咱们签的这个字就是生活中的签名。
而数字签名呢?其实也是一样的道理,他的含义是:在网络中传输数据时候,给数据添加一个数字签名,表示是谁发的数据,并且还能证实数据没有被篡改。
OK,数字签名的主要做用就是保证了数据的有效性(验证是谁发的)和完整性(证实信息没有被篡改)。下面咱们就来好好地看一下他的底层实现原理是什么样子的。
为了理解得清楚,咱们经过案例一步一步来说解。话说张三有俩好哥们A、B。因为工做缘由,张三和AB写邮件的时候为了安全都须要加密。因而张三想到了数字签名:
整个思路是这个样子的:
第一步:加密采用非对称加密,张三有三把钥匙,两把公钥,送给朋友。一把私钥留给本身。
第二步:A或者B写邮件给张三:A先用公钥对邮件加密,而后张三收到邮件以后使用私钥解密。
第三步:张三写邮件给A或者B:
(1)张三写完邮件,先用hash函数生成邮件的摘要,附着在文章上面,这就完成了数字签名,而后张三再使用私钥加密。就能够把邮件发出去了。
(2)A或者是B收到邮件以后,先把数字签名取下来,而后使用本身的公钥解密便可。这时候取下来的数字签名中的摘要若和张三的一致,那就认为是张三发来的,再对信件自己使用Hash函数,将获得的结果,与上一步获得的摘要进行对比。若是二者一致,就证实这封信未被修改过。
上面的流程咱们使用一张图来演示一下:
首先把公钥送给朋友A和B:
还有就是最后一个比较麻烦的,张三给A或者B发邮件:
上面提到咱们对签名进行验证时,须要用到公钥。若是公钥是伪造的,那咱们没法验证数字签名了,也就根本不可能从数字签名肯定对方的合法性了。这时候证书就闪亮登场了。咱们可能都有考各类证书的经历,好比说普通话证书,四六级证书等等,可是归根结底,到任何场合咱们都能拿出咱们的证书来证实本身确实已经考过了普通话,考过了四六级。这里的证书也是一样的道理。
若是不理解证书的做用,咱们能够举一个例子,好比说咱们的毕业证书,任何公司都会认可。为何会认可?由于那是国家发得,你们都信任国家。也就是说只要是国家的认证机构,咱们都信任它是合法的。
那么这个证书是如何生成的呢?咱们再来看一张图:
此时即便张三的朋友A把公钥弄错了,张三也能够经过这个证书验证。
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)); } }