urlencode,base64, aes, des, hex

news/2024/7/12 2:57:13 标签: http, https, ssl, tls
http://www.w3.org/2000/svg" style="display: none;">

文章目录

  • 古代密码学
      • 替换法
      • 移位法
      • 频率分析破解
  • 近代密码学
  • 现代密码学
    • 散列函数
      • MD5
      • SHA1
      • SHA256
      • SHA512
      • HMAC
      • 转成16进制
      • 获取文件信息摘要
    • 消息认证码
    • 重放攻击
      • 预防重放攻击
      • 挑战一应答机制
      • 一次性口令机制
    • DDoS
    • 对称加密
      • DES
      • AES
      • 加密模式
        • ==ECB==
        • ==CBC==
        • CTR
        • CFB
        • OFB
      • 填充模式
        • NoPadding
        • PKCS5Padding
    • Base64
      • 使用场景
      • 什么是可打印字符?
      • 编码规则
      • 为什么http是文本传输协议还能传图片和文件?
    • 非对称加密
      • 公钥加密私钥解密
      • 保存共钥、私钥 读取公钥、私钥
    • 混合加密
    • 认证
    • 数字签名代码实现
    • Keytool的使用
    • 撞库
    • URLENCODE

网络安全,信息安全、区块链学科的基础

古代密码学

替换法

例如:bee,b替换为w,e替换为p,单词就变为了wpp

单表替换:原文和密文使用的是同一张表

多表替换:多张表,原文和密文对比

例子:

表1:abcde - swtrp,表2:abcde-chfhk,表3:abcde-jftou

原文:bee

密钥:312

密文:b从表3获取,第二个e从表1获取,第三个3e从表2获取,即fpk

移位法

恺撒加密

例如:移动3位,原文:abc,密文:def

频率分析破解

英文 频率从高到低 e> t > a

近代密码学

恩尼格玛密码机 核心:使用移位法和替换法

被图灵破解了

电影《模仿游戏》

现代密码学

散列函数

MD5

MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的 密码散列函数,可以产生出一个128位(16字节)的散列值(hash value)可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等

public static void main(String[] args) throws Exception {
    MessageDigest md5 = MessageDigest.getInstance("MD5");
    // 可以使用 DigestUtils.md5Digest("abc".getBytes());
    byte[] digest = md5.digest("123".getBytes());
    System.out.println(digest.length);
    
}

SHA1

SHA-1(英语:Secure Hash Algorithm 1,中文名:安全散列算法1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准(FIPS)。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个 十六进制数

正式名称为 SHA 的家族第一个成员发布于 1993年。然而人们给它取了一个非正式的名称 SHA-0 以避免与它的后继者混淆。两年之后, SHA-1,第一个 SHA 的后继者发布了。 另外还有四种变体,曾经发布以提升输出的范围和变更一些细微设计: SHA-224, SHA-256, SHA-384 和 SHA-512 (这些有时候也被称做 SHA-2):

public static void main(String[] args) Exception {
        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
        byte[] digest = sha1.digest("123".getBytes());
        System.out.println(digest.length);
}

SHA256

256

public static void main(String[] args) throws Exception {
    MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
    byte[] digest = sha256.digest("123".getBytes());
    System.out.println(digest.length);
}

SHA512

512

public static void main(String[] args) throws Exception {
    MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
    byte[] digest = sha512.digest("123".getBytes());
    System.out.println(digest.length);
}

安全性更高的加密方式

HMAC

MAC message authentication code,

HMACMAC值 + hash值,

下载的软件的md5和官网的md5对比,如果一样,则是安全的,即没有被修改

String input = "琉衣";
String algorithm = "MD5";
MessageDigest md5 = MessageDigest.getInstance(algorithm);
byte[] digest = md5.digest();
// 因为ascii是0-127,但是此数组里有负数,解析不出来
/*
for (byte b : digest) {
    System.out.println(b);
}
System.out.println(new String(digest));// 乱码
*/
String encode = Base64.encode(digest);
System.out.println(encode);

// 1B2M2Y8AsgTpgAmY7PhCfg==
// 发现和 网上的在线md5加密,得出的结果不一样,那是因为消息摘要不是使用base64进行编码的,所以我们需要把值转成16进制

转成16进制

要注意,要有补0操作,原因在Integer.toHexString(b)这个语句,如果字节是00001111只得到了F

String s = Integer.toHexString(0x00001111);
System.out.println(s);
// 1111

0xff,表示十六进制的两个f,一个f即1111,所以也就是八位,一个字节

toHexString方法传入的参数是int类型32位,此处传入的是byte类型8位,所以需要在前面补24个0,然后 &ff,与上11111111就是把前面的24个0去掉,只要最后8位,int是由4组byte组成的,并且java中本身就以byte读取

String input = "琉衣";
        String algorithm = "MD5";
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        byte[] digest = messageDigest.digest(input.getBytes());
        StringBuilder sb = new StringBuilder();

        for (byte b : digest) {
            String s = Integer.toHexString(b & 0xff);
            if (s.length() == 1){
                s = "0" + s;
            }
            sb.append(s);
        }
        System.out.println(sb.toString());

获取文件信息摘要

import com.sun.org.apache.xml.internal.security.utils.Base64;
   public static void main(String[] args) throws Exception {
        String sha1 = getDigestFile("D:\\study.txt", "SHA1");
        // String sha1 = getDigestFile("D:\\study.txt", "SHA-256");
        System.out.println(sha1);
    }
    
 private static String getDigestFile(String filePath, String algorithm) throws Exception {
        FileInputStream fis = new FileInputStream(filePath);
        int len;
        byte[] bytes = new byte[1024];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while ((len = fis.read(bytes)) != -1) {
            baos.write(bytes, 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) {
        StringBuilder stringBuilder = new StringBuilder();
        for (byte b : digest) {
            String s = Integer.toHexString(b & 0xff);
            if (s.length() == 1) {
                s = "0" + s;
            }
            stringBuilder.append(s);
        }
        return stringBuilder.toString();
    }

消息认证码

typora ctrl + . 即可中文符号输出

  1. 首先A 将加解密的密钥k1和生成消息验证码的密钥k2 都发送给B(安全的方式:比如 非对称加密)
  2. A将明文通过k1加密得到密文s,然后将密文和k2通过hash得到 消息认证码m
  3. A将sm一起发送给B,然后B将sk2通过hash得到m'mm'进行比对,一样则,则用k1进行解密

重放攻击

将A发送给B的消息认证码和密文被X截取,但是不做任何修改,而是重新发送,并且是多次。比如A向B借100,但是X发10次给B

预防重放攻击

  1. 加随机数
    该方法优点是认证双方不需要时间同步,双方记住使用过的随机数,如发现报文中有以前使用过的随机数,就认为是重放攻击。缺点是需要额外保存使用过的随机数,若记录的时间段较长,则保存和查询的开销较大
  2. 加时间戳
    该方法优点是不用额外保存其他信息。缺点是认证双方需要准确的时间同步,同步越好,受攻击的可能性就越小。但当系统很庞大,跨越的区域较广时,要做到精确的时间同步并不是很容易。
  3. 加流水号
    就是双方在报文中添加一个逐步递增的整数,只要接收到一个不连续的流水号报文(太大或太小),就认定有重放威胁。该方法优点是不需要时间同步,保存的信息量比随机数方式小。缺点是一旦攻击者对报文解密成功,就可以获得流水号,从而每次将流水号递增欺骗认证端

在实际中,常将方法(1)和方法(2)组合使用,这样就只需保存某个很短时间段内的所有随机数,而且时间戳的同步也不需要太精确。对付重放攻击除了使用本以上方法外,还可以使用挑战一应答机制和一次性口令机制,而且似乎后面两种方法在实际中使用得更广泛

挑战一应答机制

一次性口令机制

DDoS

我开了一家有五十个座位的重庆火锅店,由于用料上等,童叟无欺。平时门庭若市,生意特别红火,而对面二狗家的火锅店却无人问津。二狗为了对付我,想了一个办法,叫了五十个人来我的火锅店坐着却不点菜,让别的客人无法吃饭。

上面这个例子讲的就是典型的 DDoS 攻击,全称是 Distributed Denial of Service,翻译成中文就是分布式拒绝服务。一般来说是指攻击者利用“肉鸡”对目标网站在较短的时间内发起大量请求,大规模消耗目标网站的主机资源,让它无法正常服务。在线游戏、互联网金融等领域是 DDoS 攻击的高发行业

对称加密

加密和解密的方式,使用同一把密钥

假设A手握一把密钥 key1,那么A需要克隆一把相同的密钥 key1’
在第一次通信中,A将报文连同 key1’一起发送给B

例如:需要发送原文3

  • 设置密钥为108,则发送 3 * 108 = 324,发送给对方
  • 对方收到之后,使用304 / 108 = 3得到原文

特点:

  • 机密速度快,可以加密大文件
  • 密文可逆,一旦密钥泄露,会导致数据泄露
  • 加密后编码表找不到对应的字符,出现乱码
  • 一般结合base64使用

核心原理:

  • 流加密:对每一个字母或比特加密,然后组成一起
  • 块加密:对信息流分块,不够可以补位,然后再对每一块分别加密,最后拼到一起

DES

高级加密标准

64位为分组对数据加密,它的密钥长度64位,实际参与运算56位,加密解密用同一算法

数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准,并授权在非密级政府通信中使用

private static String strDefaultKey = "hc";
private Key key;

public DES() throws Exception {
    this(strDefaultKey);
}

public DES(String strKey) throws Exception {
    DESKeySpec dks = new DESKeySpec(strKey.getBytes());
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
    this.key = keyFactory.generateSecret(dks);
}

public String encrypt(String strIn) throws Exception {
    Cipher encryptCipher = Cipher.getInstance("DES");
    encryptCipher.init(Cipher.ENCRYPT_MODE, this.key);
    byte[] bytes = encryptCipher.doFinal(strIn.getBytes(StandardCharsets.UTF_8));
    return Hex.encodeHexString(bytes);
}

public String decrypt(String strIn) throws Exception {
    Cipher decryptCipher = Cipher.getInstance("DES");
    decryptCipher.init(Cipher.DECRYPT_MODE, this.key);
    byte[] bytes = decryptCipher.doFinal(Hex.decodeHex(strIn.toCharArray()));
    return new String(bytes);
}

public static void main(String[] args) throws Exception {
    String newPassword = (new DES()).encrypt("sa");
    System.out.println(newPassword);
    System.out.print((new DES()).decrypt(newPassword));
}

AES

密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府 采用的一种区块加密标准

密钥128


private static String strDefaultKey = "hc";
private Key key;

public AES(String strKey) throws Exception {
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    // 生成一个128位的随机源,根据传入的字节数组
    SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
    secureRandom.setSeed(strKey.getBytes());
    kgen.init(128, secureRandom);
    this.key = new SecretKeySpec(kgen.generateKey().getEncoded(), "AES");
}

public AES() throws Exception {
    this(strDefaultKey);
}

public String encrypt(String content) throws Exception {
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, this.key);
    byte[] decryptBytes = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
    return Hex.encodeHexString(decryptBytes);
}

public String decrypt(String strIn) throws Exception {
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, this.key);
    byte[] decryptBytes = cipher.doFinal(Hex.decodeHex(strIn.toCharArray()));
    return new String(decryptBytes);
}

public static void main(String[] args) throws Exception {
    String newPassword = (new AES()).encrypt("marconi123");
    System.out.println(newPassword);
    System.out.print((new AES()).decrypt("38998bba637d32decbbb7064e2233194"));

}

使用CBC加密模式

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 T {
    public static void main(String[] args) throws Exception {
        String input = "琉衣";
        String key = "0123456789123456";
        String transformation = "AES/CBC/PKCS5Padding";
        String algorithm = "AES";
        String encryptAES = encryptAES(input, key, transformation, algorithm);
        System.out.println("加密:" + encryptAES);
        String s = decryptAES(encryptAES, key, transformation, algorithm);
        System.out.println("解密:" + s);
    }

    private static String encryptAES(String input, String key, String transformation, String algorithm) throws Exception {
        Cipher cipher = Cipher.getInstance(transformation);
        SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
        
        // 创建iv向量,iv向量,是使用到CBC加密模式
        IvParameterSpec iv = new IvParameterSpec(key.getBytes());
        
        cipher.init(Cipher.ENCRYPT_MODE,sks,iv);
        byte[] bytes = cipher.doFinal(input.getBytes());
        return Base64.encode(bytes);
    }
    private static String decryptAES(String encryptAES, String key, String transformation, String algorithm) throws Exception{
        Cipher cipher = Cipher.getInstance(transformation);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(),algorithm);
        
        // 创建iv向量 //CBC加密模式需要有IV向量
        IvParameterSpec iv = new IvParameterSpec(key.getBytes());
        
        cipher.init(Cipher.DECRYPT_MODE,secretKeySpec,iv);
        byte[] bytes = cipher.doFinal(Base64.decode(encryptAES));
        return new String(bytes);
    }
}
加密:CCEsmebPjdSoUoZ+hMFLJg==
解密:琉衣

加密模式

ECB

Electronic codebook,电子密码本,按照块密码的块大小被分为数个块,并对每个块进行独立加密

  • 优点: 可以并行处理数据
  • 缺点:同样的原文生成同样的密文,不能很好保护数据

CBC

Cipher-block chaining,密码块链接,每个明文块先与前一个密文块进行异或后,在进行加密,第一个明文和IV异或再加密

  • 优点:同样的原文生成的密文不一样
  • 缺点:串行处理数据

CTR

CFB

OFB

填充模式

当需要按块处理的数据,数据长度不合符块处理需求时,按照一定的方法填充满块长 的规则

NoPadding

  • 不填充
  • 在DES加密算法下, 要求原文长度必须是8byte的整数倍
  • 在AES加密算法下, 要求原文长度必须是16byte的整数倍

PKCS5Padding

数据块的大小为8位,不足就补位

Base64

  • Base64是一种用64个字符来表示任意二进制数据的方法,完成了数据在HTTP协议上的传输

  • 它是一种编码方式,而非加密方式

  • 数据编码之后,数据量会变大,变大1/3左右

使用场景

简单说,因为某些场合并不能传输或者储存二进制流。
比如,如果一个传输协议是基于ASCII文本的,那么它就不能传输二进制流,那你要将二进制流传输就得编码。常见的诸如 http 协议的 url 就是纯文本的,不能直接放二进制流。
特别的,大多数现代语言的 String 类型,都不能直接储存二进制流,但可以储存BASE64编码的字符串。如果你希望用 String 类型操作一切数据,那就没法直接用二进制流。
当然,另外还有一个原因,就是某些协议会对二进制流中的特定字符进行特殊处理(比如ASCII的0~32编码的字符在某些传输介质中,是会被当作特殊含义处理的),这种时候就需要通过编码来避开这些特定字符了。

什么是可打印字符?

在ASCII码中规定,0-31、128这33个字符属于控制字符,32-127这95个字符属于可打印字符,也就是说网络传输只能传输这95个字符,不在这个范围内的字符无法传输。那么该怎么才能传输其他字符呢?其中一种方式就是使用Base64。

既然可打印的只有95,而比95小的整数最大的2的次方的数字就是64了。2的6次方

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAb4AAABYCAYAAACK55LCAAAgAElEQVR4Xuy9aXNc2ZEleN4e+wIgsO8ACXAnk8mUUqoqldQ9Yz3W/an/wsxvnA/TM2NWizKVmczkTgIg9h0IxL6+fez4DbCo7JQqpZLZfGBARjEJBOK95/de9+PHj3toO//7f40x/BpaYGiBoQWGFhha4BOxgDYMfJ/ISg8fc2iBoQWGFhhaQCwwDHzDjTC0wNACQwsMLfBJWWAY

这就是 base64 格式的数据,代表着一张图片的数据,专业术语叫:Data URI scheme。

编码规则

  • 首先将待编码的内容转换成8位二进制,每3个字符为一组

  • 如果编码前的长度是3n+1,编码后的内容最后面补上2个 =,如果编码前的长度是3n+2,内容最后面补上1个 =。3的倍数则不用

  • 再将每一组的二进制内容拆分成6位的二进制,不足6位的后面补足0

  • 每个6进制的数字前面补足0,保证变成8位二进制

  • 将补足后的内容根据base64编码表转换成base64内容输出

(不足四个字符的时候会用 =来补足,下面会说明)

编码前 “hb”

**1.**根据ascii码转换成8位二进制,3个为一组:

  01101000,01100010

**2.**编码前长度是3n+2,所以后面补1个 ‘=’:

  01101000,01100010,=

**3.**拆分成6位二进制,不足6位的在后面补足0,0010补足变成001000:

  011010,000110,001000,=

**4.**每个6进制的数字前面补足0:

  0011010,00000110,00000010,=

**5.**根据base64编码表输出:

  aGI=

The Base64 Alphabet

索引对应字符索引对应字符索引对应字符索引对应字符
0A17R34i51z
1B18S35j520
2C19T36k531
3D20U37l542
4E21V38m553
5F22W39n564
6G23X40o575
7H24Y41p586
8I25Z42q597
9J26a43r608
10K27b44s619
11L28c45t62+
12M29d46u63/
13N30e47v
14O31f48w
15P32g49x
16Q33h50y
public class Base64Test {

    public static void main(String[] args) {
        // 获取编码器
        Base64.Encoder encoder = Base64.getEncoder();
        byte[] encode = encoder.encode("abc".getBytes());
        System.out.println(new String(encode));

        // 获取解码器
        Base64.Decoder decoder = Base64.getDecoder();
        byte[] decode = decoder.decode(encode);
        System.out.println(new String(decode));
        
        // 或者使用
        byte[] encode2 = BaseUtils.encode("abc".getBytes());
    }
}

base58 一般用在比特币里面的一种编码方式,没有数字0,字母O,大写I,小写i+/

http_626">为什么http是文本传输协议还能传图片和文件?

参考:https://segmentfault.com/q/1010000006670932
HTTP传输的是字符串,到了TCP层以二进制传输。
因为其增加了MIME。http/1.0之后版本在传输非文本文件的时候,比如图片,其实是使用MIME转换成ASCII传输的
具体参考 MIME和计算机网络

非对称加密

假设A手握公钥 key2 和私钥 key2’
首先,A将报文主体连同 key2 一起发送给B
此后,B向A发送的报文都将使用 key2 加密
而被 key2 上锁的报文 只有A手上的 key2’ 才能解密

同理:当A想要回复B时,正好相反
此时A就使用B的公钥对数据进行加密
而B就用自己的私钥进行解密

非对称加密可以解决密钥交换问题

也称公钥加密使用公钥加密的文本只能用私钥解密,使用私钥加密的文本也可以使用公钥解密

公钥不需要具有安全性,因为公钥需要在网络间传输,非对称加密可以解决密钥交换的问题

非对称加密算法,常见的比如RSAECC、DH,DSA等

  • RSA加密算法:RSA加密算法是一种非对称加密算法。在公开密钥加密和电子商业中RSA被广泛使用。RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的

  • RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用

  • 解密者拥有私钥,并且将由私钥计算生成的公钥发布给加密者。加密都使用公钥进行加密,并将密文发送到解密者,解密者用私钥解密将密文解码为明文。

公钥加密私钥解密

public class T {
    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();

        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] bytes = cipher.doFinal(input.getBytes());
        String encode = Base64.encode(bytes);
        System.out.println("密文: " + encode);

        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] bytes1 = cipher.doFinal(bytes);
        System.out.println("原文: " + new String(bytes1));
    }
}

保存共钥、私钥 读取公钥、私钥

package com.example.demo;

import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.io.FileUtils;

import javax.crypto.Cipher;
import java.io.File;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Objects;

public class T {

    private static final String PUBLIC_KEY = "publicKey";
    private static final String PRIVATE_KEY = "privateKey";
    private static final String RSA = "RSA";

    public static void main(String[] args) throws Exception {
        String input = "琉衣";
        String algorithm = "RSA";
        generateKeyToFile(algorithm, "D:/a.pub", "D:/a.pri");

        Key key = getKey("D:/a.pub", RSA, PUBLIC_KEY);
        System.out.println(Base64.encode(key.getEncoded()));

        Key key1 = getKey("D:/a.pri", RSA, PRIVATE_KEY);
        System.out.println(Base64.encode(key1.getEncoded()));
    }

    private static void generateKeyToFile(String algorithm, String publicPath, String privatePath) throws Exception {

        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();
        String encode = Base64.encode(privateKeyEncoded);
        String encode1 = Base64.encode(publicKeyEncoded);

        // 保存文件
        FileUtils.writeStringToFile(new File(publicPath), encode1, Charset.defaultCharset());
        FileUtils.writeStringToFile(new File(privatePath), encode, Charset.defaultCharset());
    }

    // 读取公钥/私钥
    private static Key getKey(String keyPath, String algorithm, String keyType) throws Exception {
        String s = FileUtils.readFileToString(new File(keyPath), Charset.defaultCharset());

        // 密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);


        if (Objects.equals(keyType, PUBLIC_KEY)) {
            // 密钥规范
            // 此类表示根据 ASN.1 类型 SubjectPublicKeyInfo 进行编码的公用密钥的 ASN.1 编码。X.509 标准中定义的 SubjectPublicKeyInfo 语法
            X509EncodedKeySpec specification = new X509EncodedKeySpec(Base64.decode(s));
            return keyFactory.generatePublic(specification);
        }
        // 该类代表私有密钥的ASN.1编码,根据ASN.1类型PrivateKeyInfo进行编码。PrivateKeyInfo语法在PKCS#8标准中
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decode(s));
        return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    }

    private 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());
        return Base64.encode(bytes);
    }

    private static String decryptRSA(String algorithm, Key key, String encode) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] decode = cipher.doFinal(Base64.decode(encode));
        return new String(decode);
    }
}

混合加密

TLS是使用对称加密和非对称加密的混合加密方式来实现的

完整性通过信息摘要算法实现
在交换密钥的环节使用非对称加密,之后的通信则使用对称加密

例如:A手握一对非对称密钥(公钥key3、私钥key3’),B手握一对对称密钥(key4、key4’)

阶段1:交换密钥-使用非对称加密

1、A将公钥 key3 发送给B
2、B将下一阶段加密/解密用的 key4’ 放在报文中,并使用公钥 key3 对报文进行加密
3、A使用私钥 key3’ 解密报文,得到 key4’

阶段2:数据通信-使用对称加密

A和B分别使用 key4’ 和 key4对接收/发送的报文进行解密/加密


一方面,第一阶段的非对称加密 保证了对称密钥的安全性
另一方面,第二阶段的对称加密 可以提高加密/解密处理的速度,提高数据传输的效率

加密和完整性都搞定之后就差认证了

认证

认证中心 CA(Certificate Authority) 是认证中心,全世界具有认证的CA就几家,分别颁布了DV,OV,EV,区别在于可信度,DV是最低的,只是域名级别的可信,EV是最高的,经过了法律和审计的严格核查,可以证明网络拥有者的身份(在浏览器地址会显示出公司的名字,比如Apple,Github的网站)。不同的信任等级的机构一起形成了层级关系

形成 证书链,最高的是RCA(root CA)RSA还会相互给对方签名

可以访问https的网站查看证书的路径,就能看到层级关系

发布认证过的公钥,才能解决公钥的信任问题

  1. 根据RSA算法生成公钥和私钥,然后将文件进行hash,得到哈希值.然后将哈希值和私钥解密运算->得到数字签名

  2. 公钥,文件,数字签名一起发送给另外一个人,然后使用公钥根据RSA对数字签名通过加密算法 得到哈希值,

  3. 然后再对文件哈希运算得到哈希值,看是否和上面的哈希值一样
    https://img-blog.csdnimg.cn/f9a0c6af217c43a3bad8166c331992fa.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAQ2h1YW5nLTI=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center" alt="在这里插入图片描述" />

  4. 如果被冒充了,https://img-blog.csdnimg.cn/29ce121a71374a37aba10394a12e9b48.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAQ2h1YW5nLTI=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center" alt="在这里插入图片描述" />

  5. 那么该如何确定就是小明发送的呢?这就涉及到了数字证书,小明将公钥和个人身份发送给权威公证的证书颁发机构CA,核实了小明的身份之后,将颁发一个数字证书,该证书包含了小明的身份信息和公钥,就可以保证了

  6. 但如何保证数字证书不会被伪造呢?因为CA机构自己也生成一套公钥和私钥,使用私钥 对小明的身份和公钥 生成数字签名,然后将该数字签名放到小明的数字证书中。每个人的电脑是有安装默认的根证书,根证书记录了可以信赖的CA机构信息及其公钥
    https://img-blog.csdnimg.cn/87884cddf9ae4e1590a20c21118fb0e7.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAQ2h1YW5nLTI=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center" alt="在这里插入图片描述" />

通过以上方式就可以确保某一文件被谁签署并且没有被篡改

运行中输入certlm.msc ,打开证书管理器

数字签名代码实现

public class T {

    private static final String PUBLIC_KEY = "publicKey";
    private static final String PRIVATE_KEY = "privateKey";
    private static final String RSA = "RSA";

    public static void main(String[] args) throws Exception {
        String input = "琉衣";
        String algorithm = "RSA";
        generateKeyToFile(algorithm, "D:/a.pub", "D:/a.pri");

        Key key = getKey("D:/a.pub", RSA, PUBLIC_KEY);
        System.out.println(Base64.encode(key.getEncoded()));

        Key key1 = getKey("D:/a.pri", RSA, PRIVATE_KEY);
        System.out.println(Base64.encode(key1.getEncoded()));

        String signaturedData = generateSignature(input, "sha256withrsa", (PrivateKey) key1);
        boolean isTrue = verifySignature(input, "sha256withrsa", (PublicKey)key, signaturedData);
        System.out.println(isTrue);
        
    }

    private static void generateKeyToFile(String algorithm, String publicPath, String privatePath) throws Exception {

        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();
        String encode = Base64.encode(privateKeyEncoded);
        String encode1 = Base64.encode(publicKeyEncoded);

        // 保存文件
        FileUtils.writeStringToFile(new File(publicPath), encode1, Charset.defaultCharset());
        FileUtils.writeStringToFile(new File(privatePath), encode, Charset.defaultCharset());
    }

    // 读取公钥/私钥
    private static Key getKey(String keyPath, String algorithm, String keyType) throws Exception {
        String s = FileUtils.readFileToString(new File(keyPath), Charset.defaultCharset());

        // 密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);


        if (Objects.equals(keyType, PUBLIC_KEY)) {
            // 密钥规范
            X509EncodedKeySpec specification = new X509EncodedKeySpec(Base64.decode(s));
            return keyFactory.generatePublic(specification);
        }
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decode(s));
        return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    }

    // RSA加密
    private 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());
        return Base64.encode(bytes);
    }

    // RSA解密
    private static String decryptRSA(String algorithm, Key key, String encode) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] decode = cipher.doFinal(Base64.decode(encode));
        return new String(decode);
    }

    // 生成签名
    private static String generateSignature(String input, String algorithm, PrivateKey privateKey) throws Exception {
        Signature signature = Signature.getInstance(algorithm);
        // 初始化签名
        signature.initSign(privateKey);
        // 传入原文
        signature.update(input.getBytes());
        // 生成签名
        byte[] sign = signature.sign();
        return Base64.encode(sign);
    }

    // 校验签名
    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的使用


撞库

撞库是黑客通过收集互联网已泄露的用户和密码信息,生成对于的字典表,尝试批量登录其他网站后,得到一系列可以登录的用户。因为很多用户在不同网站使用的账号密码大多是相同的,因此黑客可以通过获取用户在A网站的账户从而尝试登录B网站

不要每个网站的密码都设置成一样的

URLENCODE

为什么使用

  1. url发送给服务器,不允许出现空格和特殊字符
  2. url转义其实也只是为了符合url的规范而已。因为在标准的url规范中中文和很多的字符是不允许出现在url中的

除了 杠-,下划线_,点 . ,数字,字母 之外字符都将被替换成%后跟两位十六进制数

需要转换:

  • ASCII中,0-3112833个字符属于控制字符,32-12795个字符属于可打印字符,网络传输只能传输这95个字符
  • 一些非ASCII字符
  • 一些保留字符 比如 &
  • 不安全的字符,比如 空格,会变为+加号
public class UrlEncode {

    public static void main(String[] args) throws UnsupportedEncodingException {
        String encode = URLEncoder.encode("http://xx.cn/中文   , . ? + ",  String.valueOf(StandardCharsets.UTF_8));
        System.out.println(encode);

        String decode = URLDecoder.decode(encode, "UTF-8");
        System.out.println(decode);
    }
}

对于get请求

浏览器会先把空格变成%20 而+号还保留,然后接收到请求之后decode的时候,会把+号变成空格,然后这个字符串中原来的空格(%20)解码之后还是空格

因为get请求是只能是ascii编码,显示的只有95个可打印字符

对于post请求,urlencode,会把空格变成+号,+变成%2B,然后解码的时候+号变为空格,%2B变为+号


http://www.niftyadmin.cn/n/1162672.html

相关文章

http超详细讲解

HTTP相关 web浏览器 web server,web是world wide web(万维网) 万维网 万维网是一个大规模的、联机式的信息存储所,简称web 万维网客户端程序 就是 浏览器,服务器返回的万维网文档就是 浏览器窗口显示的页面 CDN …

Vue进阶

二.Vue进阶 App.vue相当于乐高的底板,每个页面都会显示App.vue的内容,其他组件在地板上添加 1.组件化 1.1什么是组件化 一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护…

vue cli4的使用

vue-cli 2.Vue CLI https://cli.vuejs.org/zh/ 什么是Vue cli 如果你在开发大型项目, 那么你需要, 并且必然需要使用Vue CLI 使用Vue.js开发大型应用时,我们需要考虑代码目录结构、项目结构和部署、热加载、代码单元测试等事情。 如果每个项目都要手动完成这些…

VueX,mock,axios的使用

VueX vuex是什么 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-…

mybatis plus中的${ew.sqlSegment},${ew.sqlSelect},${ew.customSqlSegment},${ew.sqlSet}使用

ew是mapper方法里的Param(Constants.WRAPPER) Wrapper queryWrapper对象 首先判断ew.emptyOfWhere是否存在where条件,有的话再拼接上去,ew.customSqlSegment是WHERE sql语句 没有where的时候加上 false 使用${ew.sqlSegment} 如果是连表查询且查询条…

springboot异步 邮件 定时任务

异步 定时 邮件任务 异步处理还是非常常用的,比如我们在网站上发送邮件,后台会去发送邮件,此时前台会造成响应不动,直到邮件发送完毕,响应才会成功,所以我们一般会采用多线程的方式去处理这些任务。 编写…

springboot日志框架的使用

文章目录JUL例子独立的配置文件过滤器打印堆栈信息log4jlayout配置存到数据库配置文件logbacklog4j 2slf4j简单例子绑定其他日志桥接旧日志JCLspringboot封装日志框架配置文件控制台颜色输出动态修改日志级别调试模式日志分组不同环境下的日志配置logback扩展loback-spring.xml…

@TableName使用在Mapper接口上

TableName 一般用在entity类上面,数据库中 表的名字 也可以加到 Mapper接口上面,可用于 应用到 历史表,历史表和原表字段一摸一样 例子: public class Payment {private Long id;private String serial;public Long getId() {…