秒懂编码和加密

一、URLENCODE

(1)为什么使用

1、是因为当字符串数据以url的形式传递给web服务器时,字符串中是不允许出现空格和特殊字符的。

2、因为 url 对字符有限制,比如把一个邮箱放入 url,就需要使用 urlencode 函数,因为 url 中不能包含 @ 字符。

3、url转义其实也只是为了符合url的规范而已。因为在标准的url规范中中文和很多的字符是不允许出现在url中的。

(2)转换规则

urlencode:

返回字符串,此字符串中除了-_.之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号(+)
按照每个字符对应的字符编码,不是符合我们范围的,统统的转化为%的形式也就是了。自然也是16进制的形式。

那哪些字符是需要转化的呢

ASCII 的控制字符
这些字符都是不可打印的,自然需要进行转化。

什么是可打印字符?

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

一些非ASCII字符

这些字符自然是非法的字符范围。转化也是理所当然的了。

一些保留字符

很明显最常见的就是“&”了,这个如果出现在url中了,那你认为是url中的一个字符呢,还是特殊的参数分割用的呢?

就是一些不安全的字符了。

例如:空格。为了防止引起歧义,需要被转化为“+”。

和编码无关

通过urlencode的转化规则和目的,我们也很容易的看出,urleocode是基于字符编码的。同样的一个汉字,不同的编码类型,肯定对应不同的urleocode的串。gbk编码的有gbk的encode结果。

apache等服务器,接受到字符串后,可以进行decode,但是还是无法解决编码的问题。编码问题,还是需要靠约定或者字符编码的判断解决。

因此,urleocode只是为了url中一些非ascii字符,可以正确无误的被传输,至于使用哪种编码,就不是eocode所关心和解决的问题了。

(3)java 实现

/**

  • @author itnanls

  • @date 2021/3/15
    */
    public class UrlEncode {

    public static void main(String[] args) throws UnsupportedEncodingException {
    String encode = URLEncoder.encode(“http://itnanls.cn/中文 ,。?”, “UTF-8”);
    System.out.println(encode);

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

    }
    }

二、Base64编解码

1、Base64是什么

Base64是一种用64个字符来表示任意二进制数据的方法。
它是一种编码方式,而非加密方式。
它通过将二进制数据转变为64个“可打印字符”,完成了数据在HTTP协议上的传输。

2、使用场景

Base64一般用于在HTTP协议下传输二进制数据,由于HTTP协议是文本协议,所以在HTTP协议下传输二进制数据需要将二进制数据转换为字符数据。然而直接转换是不行的。因为网络传输只能传输可打印字符。

什么是可打印字符?

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

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

3、加密规则

关于这个编码的规则:

首先将待编码的内容转换成8位二进制,每3个字符为一组;
如果编码前的长度是3n+1,编码后的内容最后面补上2个 ‘=’,如果编码前的长度是3n+2,编码后 的内容最后面补上1个 ‘=’。
再将每一组的二进制内容拆分成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=

Table 1: The Base64 Alphabet
索引 对应字符 索引 对应字符 索引 对应字符 索引 对应字符
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w
15 P 32 g 49 x
16 Q 33 h 50 y

4、总结

可以将任意的二进制数据进行Base64编码。
数据加密之后,数据量会变大,变大1/3左右。
编码后有个非常显著的特点,末尾有个=号。
可进行反向解密。
Base64编码具有不可读性

5、Base64程序

/**

  • @author itnanls

  • @date 2021/3/15
    */
    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));
    

    }
    }

三、hash摘要算法
1、MD5

MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。

/**

  • @author itnanls
  • @date 2021/3/15
    */
    public static void main(String[] args) throws Exception {
    MessageDigest md5 = MessageDigest.getInstance(“MD5”);
    byte[] digest = md5.digest(“123”.getBytes());
    System.out.println(digest.length);
    }

2、SHA1

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

SHA 家族

正式名称为 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);
}

20

3、SHA256

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);
}

32

4、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);
}

64

四、对称加密

1、DES算法

DES 加密算法是一种 分组密码,以 64 位为 分组对数据 加密,它的密钥长度是56位,加密解密用同一算法。

数据加密标准(Data Encryption Standard),简称DES,是由IBM公司提交,美国政府于1977年1月5日颁布的一种加密算法。

DES的设计目标是,用于加密保护静态存储和传输信道中的数据,安全使用10~15年。
DES综合运用了置换、代替、代数等多种密码技术。它设计精巧、实现容易、使用方便,堪称是适应计算机环境的近代分组密码的一个典范。DES的设计充分体现了Shannon所阐述的设计密码的思想,标志着密码的设计与分析达到了新的水平。
DES是一种分组密码。明文、密文和密钥的分组长度都是64位。
DES是面向二进制的密码算法。因而能够加解密任何形式的计算机数据。
DES是对合运算,因而加密和解密共用同一算法,从而使工程实现的工作量减半。
DES的密码结构属于Feistel结构。

(1)DES的加密过程

64位密钥经子密钥产生算法产生出16个48位子密钥:K1,K2,…,K16,分别供第1次,第2次,…,第16次加密迭代使用。
64位明文首先经过初始置换IP,将数据打乱重新排列并分成左右两半,左边32位构成L0,右边32位构成R0。
第i次加密迭代:由轮函数f实现子密钥Ki对Ri-1的加密,结果为32位的数据组f ( Ri-1 , Ki )。f ( Ri-1 , Ki )再与Li-1模2相加,又得到一个32位的数据组Li-1 ⊕ f ( Ri-1 , Ki )。以Li ⊕ f ( Ri-1 , Ki )作为下一次加密迭代的Ri,以Ri-1作为下一次加密迭代的Li ( i = 1,2,…,16)。
按照上一步的规则进行16次加密迭代。
第16次加密迭代结束后,以R16为左,L16为右,合并产生一个64位的数据组。再经过逆初始置换IP-1,将数据重新排列,便得到64位密文。

(2)DES的解密过程

64位密钥经子密钥产生算法产生出16个48位子密钥:K1,K2,…,K16,分别供第1次,第2次,…,第16次解密迭代使用。
64位密文首先经过初始置换IP,将数据打乱重新排列并分成左右两半,左边32位构成R16,右边32位构成L16。
第17-i次解密迭代:由轮函数f实现子密钥Ki对Li的解密,结果为32位的数据组f ( Li , Ki )。f ( Li , Ki )再与Ri模2相加,又得到一个32位的数据组Ri ⊕ f ( Li , Ki )。以Ri ⊕ f ( Li , Ki )作为下一次解密迭代的Li-1,以Li作为下一次解密迭代的Li-1 ( i = 16,15,…,1)。
按照上一步的规则进行16次解密迭代。
第16次解密迭代结束后,以L0为左,R0为右,合并产生一个64位的数据组。再经过逆初始置换IP-1,将数据重新排列,便得到64位明文。

(3)java实现

/**

  • @author itnanls

  • @date 2021/3/15
    */
    public class DES {

    /**

    • 算法秘钥
      */
      private static final byte[] DES_KEY = {11, 21, 1, -110, 82, -32, -85, -128, -65 ,44,-2};

    /**

    • 算法名字
      /
      private static final String ALGORITHM_NAME = “DES”;
      /
      *
    • 数据加密,算法(DES)
    • @param data
    • 要进行加密的数据
    • @return 加密后的数据
      */
      public static byte[] encryptDes(byte[] data) {
      try {
      // DES算法要求有一个可信任的随机数源
      DESKeySpec desKey = new DESKeySpec(DES_KEY);
      // 创建一个密匙工厂,然后用它把DESKeySpec转换成一个SecretKey对象
      SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM_NAME);
      SecretKey key = keyFactory.generateSecret(desKey);
      // 加密对象
      Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
      cipher.init(Cipher.ENCRYPT_MODE, key);
      // 加密
      return cipher.doFinal(data);
      } catch (Exception e) {
      throw new RuntimeException(“加密错误,错误信息:”, e);
      }
      }

    /**

    • 解密
    • @param cryptData
    • @return
      */
      public static byte[] decryptBasedDes(byte[] cryptData) {
      try {
      // DES算法要求有一个可信任的随机数源
      DESKeySpec desKey = new DESKeySpec(DES_KEY);
      // 创建一个密匙工厂,然后用它把DESKeySpec转换成一个SecretKey对象
      SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM_NAME);
      SecretKey key = keyFactory.generateSecret(desKey);
      // 解密对象
      Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
      cipher.init(Cipher.DECRYPT_MODE, key);
      // 把字符串解码为字节数组,并解密
      return cipher.doFinal(cryptData);
      } catch (Exception e) {
      throw new RuntimeException(“解密错误,错误信息:”, e);
      }
      }

    public static void main(String[] args) {

     String data = "itnanls is so shuai";
    
     byte[] result = encryptDes(data.getBytes());
     System.out.println("加密后:" + Base64.getEncoder().encode(result));
    
     // DES数据解密
     byte[] source = decryptBasedDes(result);
     System.err.println("解密后:" + new String(source));
    

    }
    }

2、AES

(1)介绍

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

这个标准用来替代原先的DES(Data Encryption Standard),已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院 (NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一 [1] 。

(2)java实现

/**

  • /**

  • @author itnanls

  • @date 2021/3/15
    */
    public class AESUtil {

    /**

    • 算法秘钥
      */
      private static final byte[] AES_KEY = {11, 21, 1, -110, 82, -32, -85,
      -128, -65, 44, -2, 23, 12, 32, 23, 45};

    /**

    • 算法名字
      */
      private static final String ALGORITHM_NAME = “AES”;

    public static byte[] encrypt(byte[] src) {
    try {
    // 根据秘钥和加密算法,生成秘钥对象
    SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY, ALGORITHM_NAME);
    // 得到加密器的实例
    Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
    // 初始化密码器
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
    return cipher.doFinal(src);
    } catch (Exception e) {
    e.printStackTrace();
    }
    return null;
    }

    public static byte[] decrypt(byte[] src) {
    try {
    SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY, ALGORITHM_NAME);
    Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
    return cipher.doFinal(src);
    } catch (Exception e) {
    e.printStackTrace();
    }
    return null;
    }

    public static void main(String[] args) {

     String src = "itnanls is so cool!";
    
     byte[] encrypt = encrypt(src.getBytes());
     assert encrypt != null;
     System.out.println(new String(encrypt));
    
     byte[] decrypt = decrypt(encrypt);
     assert decrypt != null;
     System.out.println(new String(decrypt));
    

    }
    }

五、非对称加密

(1)RSA介绍

RSA加密算法:RSA加密算法是一种非对称加密算法。在公开密钥加密和电子商业中RSA被广泛使用。RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。
RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用。
解密者拥有私钥,并且将由私钥计算生成的公钥发布给加密者。加密都使用公钥进行加密,并将密文发送到解密者,解密者用私钥解密将密文解码为明文。

(2)java实现

/**

  • @author itnanls

  • @date 2021/3/15
    */
    public class RSATest {

    private static final String PUBLIC_KEY = “publicKey”;

    private static final String PRIVATE_KEY = “privateKey”;

    private static final String ALGORITHM_NAME = “RSA”;

    private static final Integer KEY_SIZE = 1024;

    public static final RsaKeyPair RSA_KEY_PAIR = new RsaKeyPair();

    /**

    • 存放密钥对的静态内部类
      */
      private static final class RsaKeyPair {
      public RSAPublicKey publicKey;
      public RSAPrivateKey privateKey;
      }

    public static void main(String[] args) throws Exception {

     initKey();
    
     // 公钥加密,私钥解密,别人加密给我看,就是加密
     byte[] publicKeyEncrypt = encryptByPublicKey("123".getBytes());
     byte[] privateKeyDecrypt = decryptByPrivateKey(publicKeyEncrypt);
     System.out.println(new String(privateKeyDecrypt));
     // 私钥加密,公钥解密,我加密了,别人拿公钥解密看,能证明文件是我的,数字签名
     byte[] privateKeyEncrypt = encryptByPrivateKey("123".getBytes());
     byte[] publicKeyDecrypt = decryptByPublicKey(privateKeyEncrypt);
     System.out.println(new String(publicKeyDecrypt));
    

    }

    // 初始化密钥对
    public static void initKey(){
    // 实例化密钥对生成器
    KeyPairGenerator keyPairGen = null;
    try {
    keyPairGen = KeyPairGenerator.getInstance(ALGORITHM_NAME);
    } catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
    }

     // 初始化密钥对生成器
     assert keyPairGen != null;
     keyPairGen.initialize(KEY_SIZE);
    
     // 生成密钥对
     KeyPair keyPair = keyPairGen.generateKeyPair();
    
     // 公钥
     RSA_KEY_PAIR.publicKey = (RSAPublicKey) keyPair.getPublic();
    
     // 私钥
     RSA_KEY_PAIR.privateKey = (RSAPrivateKey) keyPair.getPrivate();
    

    }

    /**

    • 公钥解密

    • @param data

    • 待解密数据

    • @return byte[] 解密数据

    • @throws Exception
      */
      public static byte[] decryptByPublicKey(byte[] data)
      throws Exception {

      // 取得公钥
      // 此类表示根据 ASN.1 类型 SubjectPublicKeyInfo 进行编码的公用密钥的 ASN.1 编码。
      // X.509 标准中定义的 SubjectPublicKeyInfo 语法
      X509EncodedKeySpec x509KeySpec =
      new X509EncodedKeySpec(RSA_KEY_PAIR.publicKey.getEncoded());
      KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);

      // 生成公钥
      PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);

      // 对数据解密
      Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());

      cipher.init(Cipher.DECRYPT_MODE, publicKey);

      return cipher.doFinal(data);
      }

    /**

    • 公钥加密

    • @param data

    • 待加密数据

    • @return byte[] 加密数据

    • @throws Exception
      */
      public static byte[] encryptByPublicKey(byte[] data)
      throws Exception {

      // 取得公钥
      X509EncodedKeySpec x509KeySpec =
      new X509EncodedKeySpec(RSA_KEY_PAIR.publicKey.getEncoded());

      KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);

      PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);

      // 对数据加密
      Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
      cipher.init(Cipher.ENCRYPT_MODE, publicKey);

      // 完成加密
      return cipher.doFinal(data);
      }

    /**

    • 私钥加密

    • @param data

    • 待加密数据

    • @return byte[] 加密数据

    • @throws Exception
      */
      public static byte[] encryptByPrivateKey(byte[] data)
      throws Exception {

      // 取得私钥
      // 该类代表私有密钥的ASN.1编码,根据ASN.1类型PrivateKeyInfo进行编码。
      // PrivateKeyInfo语法在PKCS#8标准中
      PKCS8EncodedKeySpec pkcs8KeySpec =
      new PKCS8EncodedKeySpec(RSA_KEY_PAIR.privateKey.getEncoded());
      KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);

      // 生成私钥
      PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);

      // 初始化加密器
      Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
      cipher.init(Cipher.ENCRYPT_MODE, privateKey);
      // 进行加密
      return cipher.doFinal(data);
      }

    /**

    • 私钥解密

    • @param data

    • 待解密数据

    • @return byte[] 解密数据

    • @throws Exception
      */
      public static byte[] decryptByPrivateKey(byte[] data)
      throws Exception {

      // 取得私钥
      // 该类代表私有密钥的ASN.1编码,根据ASN.1类型PrivateKeyInfo进行编码。
      // PrivateKeyInfo语法在PKCS#8标准中
      PKCS8EncodedKeySpec pkcs8KeySpec =
      new PKCS8EncodedKeySpec(RSA_KEY_PAIR.privateKey.getEncoded());

      KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);

      // 生成私钥
      PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);

      // 对数据解密
      Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());

      cipher.init(Cipher.DECRYPT_MODE, privateKey);

      return cipher.doFinal(data);
      }
      }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值