☆* o(≧▽≦)o *☆嗨~我是小奥
个人博客:小奥的博客
CSDN:个人CSDN
Github:传送门
面经分享(牛客主页):传送门
文章作者技术和水平有限,如果文中出现错误,希望大家多多指正!
如果觉得内容还不错,欢迎点赞收藏关注哟! ❤️
数据加密的基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码为“密文”,使其只能在输入相应的密钥之后才能显示出原容,通过这样的途径来达到保护数据不被非法人窃取、阅读的目的。 该过程的逆过程为解密,即将该编码信息转化为其原来数据的过程。
采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。亦指加密和解密使用相同密钥的加密算法。
对称加密算法的优点在于加解密的高速度和使用长密钥时的难破解性。
假设两个用户需要使用对称加密方法加密然后交换数据,则用户最少需要2个密钥并交换使用,如果企业内用户有n个,则整个企业共需要n×(n-1) 个密钥,密钥的生成和分发将成为企业信息部门的恶梦。对称加密算法的安全性取决于加密密钥的保存情况,但要求企业中每一个持有密钥的人都保守秘密是不可能的,他们通常会有意无意的把密钥泄漏出去——如果一个用户使用的密钥被入侵者所获得,入侵者便可以读取该用户密钥加密的所有文档,如果整个企业共用一个加密密钥,那整个企业文档的保密性便无从谈起。
常见的对称加密算法:DES、3DES、DESX、Blowfish、IDEA、RC4、RC5、RC6和AES
指加密和解密使用不同密钥的加密算法,也称为公私钥加密。
假设两个用户要加密交换数据,双方交换公钥,使用时一方用对方的公钥加密,另一方即可用自己的私钥解密。如果企业中有n个用户,企业需要生成n对密钥,并分发n个公钥。由于公钥是可以公开的,用户只要保管好自己的私钥即可,因此加密密钥的分发将变得十分简单。同时,由于每个用户的私钥是唯一的,其他用户除了可以可以通过信息发送者的公钥来验证信息的来源是否真实,还可以确保发送者无法否认曾发送过该信息。非对称加密的缺点是加解密速度要远远慢于对称加密,在某些极端情况下,甚至能比非对称加密慢上1000倍。
常见的非对称加密算法:RSA、ECC(移动设备用)、Diffie-Hellman、El Gamal、DSA(数字签名用)
Hash算法特别的地方在于它是一种单向算法,用户可以通过Hash算法对目标信息生成一段特定长度的唯一的Hash值,却不能通过这个Hash值重新获得目标信息。因此Hash算法常用在不可还原的密码存储、信息完整性校验等。
常见的Hash算法:MD2、MD4、MD5、HAVAL、SHA、SHA-1、HMAC、HMAC-MD5、HMAC-SHA1
Java 利用 DES / 3DES / AES 三种算法分别实现 对称加密 - 掘金 (juejin.cn)
(1)混淆(confusion):使密文与密钥的关系尽可能复杂化,使得对手即使获取了许多明文和对应的密文,以及关于密文的一些统计特性,也无法推测密钥。
(2)扩散(diffusion):让明文中的每一位影响密文中的许多位,或者说让密文中的每一位受明文中的许多位的影响。这样可以隐蔽明文的统计特性。当然,理想的情况是让明文中的每一位影响密文中的所有位,或者说让密文中的每一位受明文中所有位的影响。
(3)分组(block cipher):可以看成经典的电报密码本加密技术的现代传承,其中由密钥来决定电报密码本的选择。一次加密一组数据,密钥长度为一组数据的长度。
DES(Data Encryption Standard)采用分组加密。使用64位的分组长度和56位的密钥长度,将64位的输入经过一系列变换得到64位的输出。DES算法利用多次组合替代算法和换位算法,通过混淆和扩散的相互作用,把明文编制成密码强度很高的密文。解密则使用了相同的步骤和相同的密钥。
DES的压缩、扩展和置换操作使其具有很强的雪崩效应。即输入明文或密钥中一个比特的变化会导致输出中至少一半比特的密文发生变化,这使得穷举试凑寻找其中规律变得不可能。
DES算法产生密钥的方式简单,密钥一般也比较短。
DES算法加密解密速度快,效率很高,适合对大数据量的数据进行加密。
DES算法的安全性依赖于密钥的高度保密,通信双方必须有方法能保证安全的分享密钥,并定期更换DES密钥。
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class DES {
private static final String KEY = "Hello Ketty";
/**
* DES 加密
*
* @param content
* @return
*/
public static byte[] desEncrypt(String content) {
try {
KeyGenerator kGen = KeyGenerator.getInstance("DES");
kGen.init(56, new SecureRandom(KEY.getBytes()));
SecretKey secretKey = kGen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "DES");
Cipher cipher = Cipher.getInstance("DES");// 创建密码器
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(byteContent);
return result; // 加密
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
/**
* DES 解密
*
* @param content
* @return
*/
public static byte[] desDecrypt(byte[] content) throws Exception {
KeyGenerator kGen = KeyGenerator.getInstance("DES");
kGen.init(56, new SecureRandom(KEY.getBytes()));
SecretKey secretKey = kGen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "DES");
Cipher cipher = Cipher.getInstance("DES"); // 创建密码器
cipher.init(Cipher.DECRYPT_MODE, key); // 初始化
byte[] result = cipher.doFinal(content); // 解密
return result;
}
/**
* 将二进制转换成16进制
*
* @param buf
* @return
*/
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
/**
* 将16进制转换为二进制
*
* @param hexStr
* @return
*/
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / 2; i++) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
/**
* 主函数
*/
public static void main(String[] args) throws Exception {
String content = "Hello World!";
//加密
System.out.println("加密前:" + content);
byte[] encryptResult = desEncrypt(content);
String result = parseByte2HexStr(encryptResult);
System.out.println("加密后:" + result);
//解密
byte[] bytes = parseHexStr2Byte(result);
byte[] decryptResult = desDecrypt(bytes);
System.out.println("解密后:" + new String(decryptResult));
}
}
RSA算法基于数论构造,其算法具体实现基于一个十分简单的数论事实:将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
(1)任意选择两个大的质数(素数)p和q,p不等于q,计算N=pq
(2)根据欧拉函数,不大于N且与N互质的整数个数为(p-1)(q-1)
(3)选择一个整数e与(p-1)(q-1)互质,并且e小于(p-1)(q-1)
(4)用以下这个公式计算d:d×e ≡ 1 (mod (p-1)(q-1))
(5)将p和q销毁后,(N,e)是公钥,(N,d)是私钥
假设Bob想给Alice送一个消息,他知道Alice产生的公钥N和e
将原始信息分为多段,每一段(假定为n)分别用以下公式计算出c:
n ^ e ≡ c (mod N)
Alice得到Bob的消息c后就可以利用她的密钥(N,d)来解码。她可以用以下这个公式来将c转换为n:
e ^ d ≡ n (mod N)
得到n后,她可以将原来的信息m重新复原。
比起DES和其它对称算法来说,RSA的运算速度要慢得多。
实际使用时,RSA算法不用来加密消息,而是用来加密传输密钥,加密消息用对称算法,如DES。
将RSA算法反向使用(私钥加密公钥解密),对消息摘要加密,可以实现数字签名的功能。
数字签名可以防止数据篡改、数据抵赖和数据伪造发生。
package com.vip.file;
import java.math.BigInteger;
import java.util.Random;
public class RSA {
private final static int numLength = 1024;//素数长度
private final static int accuracy = 100;//素数的准确率为1-(2^(-accuracy))
//获取最大公约数
private BigInteger getGCD(BigInteger a, BigInteger b) {
if (b.byteValue() == 0) return a;
return getGCD(b, a.mod(b));
}
//扩展欧几里得方法,计算 ax + by = 1中的x与y的整数解(a与b互质)
private static BigInteger[] extGCD(BigInteger a, BigInteger b) {
if (b.signum() == 0) {
return new BigInteger[]{a, new BigInteger("1"), new BigInteger("0")};
} else {
BigInteger[] bigIntegers = extGCD(b, a.mod(b));
BigInteger y = bigIntegers[1].subtract(a.divide(b).multiply(bigIntegers[2]));
return new BigInteger[]{bigIntegers[0], bigIntegers[2], y};
}
}
//超大整数超大次幂然后对超大的整数取模,利用蒙哥马利乘模算法,
//(base ^ exp) mod n
//依据(a * b) mod n=(a % n)*(b % n) mod n
private static BigInteger expMode(BigInteger base, BigInteger exp, BigInteger mod) {
BigInteger res = BigInteger.ONE;
//拷贝一份防止修改原引用
BigInteger tempBase = new BigInteger(base.toString());
//从左到右实现简答
/*
D=1
WHILE E>=0
IF E%2=0
C=C*C % N
E=E/2
ELSE
D=D*C % N
E=E-1
RETURN D
*/
for (int i = 0; i < exp.bitLength(); i++) {
if (exp.testBit(i)) {//判断对应二进制位是否为1
res = (res.multiply(tempBase)).mod(mod);
}
tempBase = tempBase.multiply(tempBase).mod(mod);
}
return res;
}
//产生公钥与私钥
public static SecretKey generateKey(BigInteger p, BigInteger q) {
//令n = p * q。取 φ(n) = (p-1) * (q-1)。
BigInteger n = p.multiply(q);
//计算与n互质的整数个数 欧拉函数
BigInteger fy = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
//取 e ∈ [1 < e < φ(n) ] ,( n , e )作为公钥对,这里取65537
BigInteger e = new BigInteger("65537");
//计算ed与fy的模反元素d。令 ed mod φ(n) = 1,计算d,然后将( n , d ) 作为私钥对
BigInteger[] bigIntegers = extGCD(e, fy);
//计算出的x不能是负数,如果是负数,则进行x=x+fy。使x为正数,但是x
BigInteger x = bigIntegers[1];
if (x.signum() == -1) {
x = x.add(fy);
}
//返回计算出的密钥
return new SecretKey(n, e, x);
}
public static SecretKey generateKey() {
BigInteger[] pq = getRandomPQ();
return generateKey(pq[0], pq[1]);
}
//加密
public static BigInteger encrypt(BigInteger text, SecretKey.PublicKey publicKey) {
return expMode(text, publicKey.e, publicKey.n);
}
//解密
public static BigInteger decrypt(BigInteger cipher, SecretKey.PrivateKey privateKey) {
return expMode(cipher, privateKey.d, privateKey.n);
}
//加密
public static String encrypt(String text, SecretKey.PublicKey publicKey) {
return encrypt(new BigInteger(text.getBytes()), publicKey).toString();
}
//解密
public static String decrypt(String chipper, SecretKey.PrivateKey privateKey) {
BigInteger bigInteger = expMode(new BigInteger(chipper), privateKey.d, privateKey.n);
byte[] bytes = new byte[bigInteger.bitLength() / 8 + 1];
for (int i = 0; i < bytes.length; i++) {
for (int j = 0; j < 8; j++) {
if (bigInteger.testBit(j + i * 8)) {
bytes[bytes.length - 1 - i] |= 1 << j;
}
}
}
return new String(bytes);
}
//产生两个随机1024位大质数
public static BigInteger[] getRandomPQ() {
BigInteger p = BigInteger.probablePrime(numLength, new Random());
while (!p.isProbablePrime(accuracy)) {
p = BigInteger.probablePrime(numLength, new Random());
}
BigInteger q = BigInteger.probablePrime(numLength, new Random());
while (!q.isProbablePrime(accuracy)) {
q = BigInteger.probablePrime(numLength, new Random());
}
return new BigInteger[]{p, q};
}
//密匙对
static class SecretKey {
BigInteger n, e, d;
public SecretKey(BigInteger n, BigInteger e, BigInteger d) {
this.n = n;
this.e = e;
this.d = d;
}
public PublicKey getPublicKey() {
return new PublicKey(n, e);
}
public PrivateKey getPrivateKey() {
return new PrivateKey(n, d);
}
//密钥
static class PrivateKey {
public BigInteger n, d;
public PrivateKey(BigInteger n, BigInteger d) {
this.n = n;
this.d = d;
}
}
//公钥
static class PublicKey {
public BigInteger n, e;
public PublicKey(BigInteger n, BigInteger e) {
this.n = n;
this.e = e;
}
}
}
public static void main(String[] args) {
SecretKey secretKey = RSA.generateKey();
//明文内容不要超过1024位,超过后需要分段加密
String text = "Hello world";
String chipper = RSA.encrypt(text, secretKey.getPublicKey());
System.out.println("加密后:\n" +
//密文长度可能会随着随机密钥的改变而改变,最长不超过2048位
"密文二进制长度:" + new BigInteger(chipper).bitLength()
+ "\n"
+ chipper);
String origin = RSA.decrypt(chipper, secretKey.getPrivateKey());
System.out.println("解密后:\n" + origin);
}
}
(13条消息) MD5算法的Java实现_JankingWon的博客-CSDN博客
MD5码以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
MD5,是一种信息摘要算法。简单来说,就是把一段信息(明文),通过一种有损的方式压缩成定长的字符(32位密文)。因为这种压缩方式是会损失信息的,所以是无法还原出“明文”的。虽然无法从数学上破解MD5算法,但由于现在计算机具备强大的计算能力,还是可以通过“穷举法”破解该算法。如果想用该算法加密,还可以通过“加盐”的方式提高解密难度。该算法允许多传一个参数"salt",指定通过MD5加密的次数,这样是能够提高解密难度的。
数字签名是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串类似于写在纸上的物理签名,用于鉴别数字信息的真伪,同时也是对信息的发送者身份真实性的一个有效证明。
数字签名是公钥加密技术与消息摘要技术的结合应用。
Java 实现 MD5 加密算法 - 掘金 (juejin.cn)