加密之对称Base64,DES,PBE

1 Base64算法

BASE64 严格地说,属于编码格式,而非加密算法

MD5、SHA、HMAC这三种加密算法,可谓是非可逆加密,就是不可解密的加密方法。我们通常只把他们作为加密的基础。单纯的以上三种的加密并不可靠。

1.1 简介

BASE64按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.)
常见于邮件、http加密,截取http信息,你就会发现登录操作的用户名、密码字段通过BASE64加密的。

标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的/+字符变为形如%XX的形式,而这些%号在存入数据库时还需要再进行转换,因为ANSI SQL中已将%号用作通配符。
为解决此问题,可采用一种用于URL的改进Base64编码,它在末尾填充=号,并将标准Base64中的+/分别改成了-_,这样就免去了在URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。
另有一种用于正则表达式的改进Base64变种,它将+/改成了“!”和“-”,因为+,*以及前面在IRCu中用到的“[”和“]”在正则表达式中都可能具有特殊含义。
此外还有一些变种,它们将+/改为“-”或“.”(用作编程语言中的标识符名称)或“.-”(用于XML中的Nmtoken)甚至“_:”(用于XML中的Name)。
Base64要求把每三个8Bit的字节转换为四个6Bit的字节(38 = 46 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。)

BASE64的加密解密是双向的,可以求反解。
MD5、SHA以及HMAC是单向加密,任何数据加密后只会产生唯一的一个加密串,通常用来校验数据在传输过程中是否被修改。其中HMAC 算法有一个密钥,增强了数据传输过程中的安全性,强化了算法外的不可控因素。

1.2 原理

1.2.1 转换步骤

使用base64转换utf-8编码时,utf-8的编码是变长编码,字节数为1、2、3
转换步骤:

  • 3字节utf-8编码转换,由3*8转换为4*6,然后每6位增加两个高位0,变为四个字节编码输出;
  • 2字节的转换时,将其拆分为6 6 4长度格式,前两个(长度为6的新字符)同上述操作,加两个高位的0,最后一个(长度为4的新字符)高位增加两个0,低位用0补充至一个字节长度,即00XXXX00,最后输出为三个字节的新字符外加一个=,即XXX=
  • 一字节长度的utf-8,方法相同,拆分为两个新字符6 2,操作如上,形成新字符,00XXXXXX00XX0000,输出为XX==

也就是说utf-8的字符编码通过base64算法转换,使其输出四个字节的字符,等号的数量对应这utf-8原始字节数,方便解码进行格式识别和编码还原

1.2.2 转换对应表

数字 码值 数字 码值 数字 码值 数字 码值
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

1.2.3 转换实例分析

1.2.3.1 三字节转换

Man如何转成Base64编码
第一步,ManASCII值分别是77、97、110,对应的二进制值是01001101、01100001、01101110,将它们连成一个24位的二进制字符串010011010110000101101110
第二步,将这个24位的二进制字符串分成4组,每组6个二进制位:010011、010110、000101、101110
第三步,在每组前面加两个00,扩展成32个二进制位,即四个字节:00010011、00010110、00000101、00101110。它们的十进制值分别是19、22、5、46
第四步,根据上表,得到每个值对应Base64编码,即T、W、F、u
因此,Man的Base64编码就是TWFu

1.2.3.2 不足三字节转换

如果字节数不足三,则这样处理:

  • 二个字节的情况:将这二个字节的一共16个二进制位,按照上面的规则,转成三组,最后一组除了前面加两个0以外,后面也要加两个0。这样得到一个三位的Base64编码,再在末尾补上一个=号。
    比如,Ma这个字符串是两个字节对应二进制是01001101、01100001,按照 6 6 4可以转化成三组000100110001011000000100以后,对应Base64值分别为T、W、E,再补上一个=号,因此MaBase64编码就是TWE=
  • 一个字节的情况:将这一个字节的8个二进制位,按照上面的规则转成6 2 二组,最后一组除了前面加二个0以外,后面再加4个0。这样得到一个二位的Base64编码,再在末尾补上两个=号。
    比如,M这个字母是一个字节对应二进制是01001101,可以转化为二组00010011、00010000,对应的Base64值分别为T、Q,再补上二个=号,因此MBase64编码就是TQ==

1.3 实际操作

通过java代码实现如下: ·

public static byte[] decryptBASE64(String key) throws Exception {  
    return (new BASE64Decoder()).decodeBuffer(key);  
}  
  
 
public static String encryptBASE64(byte[] key) throws Exception {  
    return (new BASE64Encoder()).encode(key);  
}  

主要就是BASE64Encoder、BASE64Decoder两个类,我们只需要知道使用对应的方法即可。另,BASE加密后产生的字节位数是8的倍数,如果不够位数以=符号填充。

2 DES算法

2.1 定义

DES-Data Encryption Standard,即数据加密算法。是IBM公司于1975年研究成功并公开发表的。DES算法的入口参数有三个:Key、Data、Mode。其中Key为8个字节共64位,是DES算法的工作密钥;Data也为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密解密
DES算法把64位的明文输入块变为64位的密文输出块,它所使用的密钥也是64位

2.2 具体操作

public static final String ALGORITHM = "DES";
    static BASE64Decoder de = new BASE64Decoder();
    static BASE64Encoder en = new BASE64Encoder();
	//根据密钥 生成key
    private static Key toKey(byte[] key) throws Exception {
        DESKeySpec dks = new DESKeySpec(key);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
        SecretKey secretKey = keyFactory.generateSecret(dks);
         // 当使用其他对称加密算法时,如AES、Blowfish等算法时,用下述代码替换上述三行代码   
        //SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);
        return secretKey;
    }

	//解密 操作
    public static String decrypt(byte[] data, String key) throws Exception {
        Key k = toKey(de.decodeBuffer(key));
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, k);

        return new String(cipher.doFinal(data));
    }

	//加密 操作
    public static String encrypt(byte[] data, String key) throws Exception {
        Key k = toKey(de.decodeBuffer(key));
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, k);
        return en.encodeBuffer(cipher.doFinal(data));
    }


	//初始化密钥
    public static String initKey(String seed) throws Exception {
        SecureRandom secureRandom = null;

        if (seed != null) {
            secureRandom = new SecureRandom(de.decodeBuffer(seed));
        } else {
            secureRandom = new SecureRandom();
        }

        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);
        kg.init(secureRandom);
        SecretKey secretKey = kg.generateKey();

        return en.encodeBuffer(secretKey.getEncoded());
    }


    public static void main(String[] args)throws Exception {
        String key = initKey("123");  //hceKtjfTbcg=
        System.out.print("钥匙:"+key);
        String enStr = encrypt("test".getBytes(),key);
        System.out.println("加密后:"+enStr);
        String deStr = decrypt(de.decodeBuffer(enStr),key);
        System.out.println("解密后:"+deStr);
    }

其实DES有很多同胞兄弟,如DESede(TripleDES)AESBlowfish、RC2、RC4(ARCFOUR)。这里就不过多阐述了,大同小异,只要换掉ALGORITHM换成对应的值,同时做一个代码替换SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);就可以了,此外就是密钥长度不同了

3 PBE算法

3.1 定义

PBE——Password-based encryption(基于密码加密)。其特点在于口令由用户自己掌管,不借助任何物理媒体;采用随机数(这里我们叫做盐)杂凑多重加密等方法保证数据的安全性。是一种简便的加密方式

3.2 实际操作

在使用PBE时可以使用,这些算法:PBEWithMD5AndDESPBEWithMD5AndTripleDESPBEWithSHA1AndDESedePBEWithSHA1AndRC2_40

	/**
     * 支持以下任意一种算法
     *
     * <pre>
     *  PBEWithMD5AndDES
     *  PBEWithMD5AndTripleDES
     *  PBEWithSHA1AndDESede
     *  PBEWithSHA1AndRC2_40
     *  </pre>
     */
    public static final String ALGORITHM = "PBEWITHMD5andDES";
    static BASE64Decoder de = new BASE64Decoder();
    static BASE64Encoder en = new BASE64Encoder();
    /**
     * 盐初始化
     * @return
     * @throws Exception
     */
    public static byte[] initSalt() throws Exception {
        byte[] salt = new byte[8];
        Random random = new Random();
        random.nextBytes(salt);
        return salt;
    }

    /**
     * 转换密钥<br>
     *
     * @param password
     * @return
     * @throws Exception
     */
    private static Key toKey(String password) throws Exception {
        PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
        SecretKey secretKey = keyFactory.generateSecret(keySpec);
        return secretKey;
    }

    /**
     * 加密
     *
     * @param data     数据
     * @param password 密码
     * @param salt     盐
     * @return
     * @throws Exception
     */
    public static String encrypt(byte[] data, String password, byte[] salt) throws Exception {
        Key key = toKey(password);
        PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
        return en.encodeBuffer(cipher.doFinal(data));

    }

    /**
     * 解密
     * @param data     数据
     * @param password 密码
     * @param salt     盐
     * @return
     * @throws Exception
     */
    public static String decrypt(byte[] data, String password, byte[] salt) throws Exception {
        Key key = toKey(password);
        PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);

        return new String(cipher.doFinal(data));

    }



    public static void main(String[] args) throws Exception {
        String inputStr = "abc";
        System.err.println("原文:  " + inputStr);
        byte[] input = inputStr.getBytes();

        String pwd = "efg";
        System.err.println("密码:  " + pwd);

        byte[] salt = initSalt();
        System.out.println("盐值:" +JSON.toJSON(salt));
        String data = encrypt(input, pwd, salt);
        System.err.println("加密后:  " + data);
        String output = decrypt(de.decodeBuffer(data), pwd, salt);
        System.err.println("解密后:  " + output);
    }
posted @ 2022-01-11 16:50  上善若泪  阅读(436)  评论(0编辑  收藏  举报