zoukankan      html  css  js  c++  java
  • Java 加解密技术系列之 DES




    前几篇文章讲的都是单向加密算法。当中涉及到了 BASE64、MD5、SHA、HMAC 等几个比較常见的加解密算法。

    这篇文章,以及后面几篇。打算介绍几个对称加密算法。比方:DES、3DES(TripleDES)、AES 等。那么,这篇文章主要是对 DES 大概讲一下。



    背景


    在讨论 DES 之前。首先了解一下什么是对称加密算法吧。对于对称加密算法,他应用的时间比較早。技术相对来说比較成熟,在对称加密算法中。数据发信方将明文(原始数据)和加密密钥一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。收信方收到密文后,若想解读原文。则须要使用加密用过的密钥及同样算法的逆算法对密文进行解密,才干使其恢复成可读明文。




    在对称加密算法中,使用的密钥仅仅有一个。发收信两方都使用这个密钥对数据进行加密和解密。这就要求解密方事先必须知道加密密钥。

    对称加密算法的特点是算法公开、计算量小。不足之处是,交易两方都使用相同钥匙,安全性得不到保证。



    概念


    那么,什么是 DES?他是怎么来的?相信非常多人都非常感兴趣。由于曾经在开发的时候,对进度的要求比較严,非常多时候根本就没有时间来了解这些东西。

    因此,今天专门来研究研究这个东西。


    DES,全称为“Data Encryption Standard”,中文名为“数据加密标准”。是一种使用密钥加密的块算法。DES 算法为password体制中的对称password体制。又被称为美国数据加密标准。是 1972 年美国 IBM 公司研制的对称password体制加密算法。

    明文按 64 位进行分组,密钥长 64 位,密钥其实是 56 位參与 DES 运算(第8、16、24、32、40、48、56、64 位是校验位, 使得每一个密钥都有奇数个 1)分组后的明文组和 56 位的密钥按位替代或交换的方法形成密文组的加密方法。



    基本原理


    入口參数有三个:key、data、mode。key 为加密解密使用的密钥,data 为加密 解密的数据。mode 为其工作模式。当模式为加密模式时,明文依照 64 位进行分组。形成明文组。key 用于对数据加密,当模式为解密模式时。key 用于对数据解密。

    实际运用中,密钥仅仅用到了 64 位中的 56 位,这样才具有高的安全性。





    主要流程


    DES 算法把 64 位的明文输入块变为 64 位的密文输出块。它所使用的密钥也是 64 位。其算法主要分为两步:

    • 初始置换
    其功能是把输入的 64 位数据块按位又一次组合,并把输出分为 L0、R0 两部分。每部分各长 32 位,其置换规则为将输入的第 58 位换到第一位,第 50 位换到第 2 位 …… 依此类推,最后一位是原来的第 7 位。L0、R0 则是换位输出后的两部分。L0 是输出的左 32 位,R0 是右  32 位,例:设置换前的输入值为 D1 D2 D3 …… D64,则经过初始置换后的结果为:L0 = D58 D50 …… D8;R0 = D57 D49 …… D7。

    • 逆置换
    经过 16 次迭代运算后。得到 L16、R16。将此作为输入,进行逆置换,逆置换正好是初始置换的逆运算。由此即得到密文输出。

    整个算法 的主流程图例如以下:




    分组模式


    • ECB模式

    ECB。中文名“电子password本模式”。是最古老、最简单的模式,将加密的数据分成若干组,每组的大小跟加密密钥长度同样。

    然后每组都用同样的密钥加密,比方 DES 算法。假设最后一个分组长度不够 64 位,要补齐 64 位。

    如图所看到的:




    • CBC模式

    CBC。中文名“加密块链模式”,与 ECB 模式最大的不同是增加了初始向量。他的特点是,每次加密的密文长度为 64位 ( 8 个字节),当同样的明文使用同样的密钥和初始向量的时候 CBC 模式总是产生同样的密文。



    • CFB模式

    CFB,中文名“加密反馈模式”。加密反馈模式克服了须要等待 8 个字节才干加密的缺点。它採用了分组password作为流password的密钥流生成器。

    他的特点是。每次加密的 Pi 和 Ci 不大于 64 位;加密算法和解密算法同样。不能适用于公钥算法。使用同样的密钥和初始向量的时候。同样明文使用 CFB 模式加密输出同样的密文。能够使用不同的初始化变量使同样的明文产生不同的密文。防止字典攻击。加密强度依赖于密钥长度;加密块长度过小时,会添加循环的数量,导致开销添加;加密块长度应时 8 位的整数倍(即字节为单位);一旦某位数据出错,会影响眼下和其后 8 个块的数据。




    • OFB模式

    OFB,中文名“输出反馈模式”,与 CFB 模式不同之处在于, 加密位移寄存器与密文无关了,仅与加密 key 和加密算法有关,做法是不再把密文输入到加密移位寄存器,而是把输出的分组密文(Oi)输入到一位寄存器。由于密文没有參与链操作。所以使得 OFB 模式更easy受到攻击;不会进行错误传播,某位密文错误发生,仅仅会影响该位相应的明文,而不会影响别的位;不是自同步的。假设加密和解密两个操作失去同步,那么系统须要又一次初始化;每次又一次同步时,应使用不同的初始向量。能够避免产生同样的比特流。避免“已知明文”攻击。




    • CTR模式

    CTR。中文名“计数模式”,是对一系列输入数据块(称为计数)进行加密,产生一系列的输出块,输出块与明文异或得到密文。对于最后的数据块。可能是长 u 位的局部数据块,这 u 位就将用于异或操作,而剩下的 b-u 位将被丢弃(b表示块的长度)。




    代码实现

    <span style="font-family:Comic Sans MS;"><span style="font-size:12px;">package com.sica.des;
    
    import com.google.common.base.Strings;
    import sun.misc.BASE64Decoder;
    import sun.misc.BASE64Encoder;
    
    import javax.crypto.Cipher;
    import javax.crypto.KeyGenerator;
    import javax.crypto.SecretKey;
    import javax.crypto.SecretKeyFactory;
    import javax.crypto.spec.DESKeySpec;
    import java.security.InvalidKeyException;
    import java.security.Key;
    import java.security.NoSuchAlgorithmException;
    import java.security.SecureRandom;
    import java.security.spec.InvalidKeySpecException;
    
    /**
     * Created by xiang.li on 2015/2/28.
     * DES 加解密工具类
     *
     * <pre>
     * 支持 DES、DESede(TripleDES,就是3DES)、AES、Blowfish、RC2、RC4(ARCFOUR)
     * DES                  key size must be equal to 56
     * DESede(TripleDES)    key size must be equal to 112 or 168
     * AES                  key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available
     * Blowfish             key size must be multiple of 8, and can only range from 32 to 448 (inclusive)
     * RC2                  key size must be between 40 and 1024 bits
     * RC4(ARCFOUR)         key size must be between 40 and 1024 bits
     * 详细内容 须要关注 JDK Document http://.../docs/technotes/guides/security/SunProviders.html
     * </pre>
     */
    public class DES {
        /**
         * 定义加密方式
         */
        private final static String KEY_DES = "DES";
        private final static String KEY_AES = "AES";    // 測试
    
        /**
         * 全局数组
         */
        private final static String[] hexDigits = { "0", "1", "2", "3", "4", "5",
                "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
    
        /**
         * 初始化密钥
         * @return
         */
        public static String init() {
            return init(null);
        }
    
        /**
         * 初始化密钥
         * @param seed 初始化參数
         * @return
         */
        public static String init(String seed) {
            SecureRandom secure = null;
            String str = "";
            try {
                if (null != secure) {
                    // 带參数的初始化
                    secure = new SecureRandom(decryptBase64(seed));
                } else {
                    // 不带參数的初始化
                    secure = new SecureRandom();
                }
    
                KeyGenerator generator = KeyGenerator.getInstance(KEY_DES);
                generator.init(secure);
    
                SecretKey key = generator.generateKey();
                str = encryptBase64(key.getEncoded());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return str;
        }
    
        /**
         * 转换密钥
         * @param key 密钥的字节数组
         * @return
         */
        private static Key byteToKey(byte[] key) {
            SecretKey secretKey = null;
            try {
                DESKeySpec dks = new DESKeySpec(key);
                SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_DES);
                secretKey = factory.generateSecret(dks);
    
                // 当使用其它对称加密算法时,如AES、Blowfish等算法时,用下述代码替换上述三行代码
    //            secretKey = new SecretKeySpec(key, KEY_DES);
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (InvalidKeySpecException e) {
                e.printStackTrace();
            }
            return secretKey;
        }
    
        /**
         * DES 解密
         * @param data 须要解密的字符串
         * @param key 密钥
         * @return
         */
        public static String decryptDES(String data, String key) {
            // 验证传入的字符串
            if (Strings.isNullOrEmpty(data)) {
                return "";
            }
            // 调用解密方法完毕解密
            byte[] bytes = decryptDES(hexString2Bytes(data), key);
            // 将得到的字节数组变成字符串返回
            return new String(bytes);
        }
    
        /**
         * DES 解密
         * @param data 须要解密的字节数组
         * @param key 密钥
         * @return
         */
        public static byte[] decryptDES(byte[] data, String key) {
            byte[] bytes = null;
            try {
                Key k = byteToKey(decryptBase64(key));
                Cipher cipher = Cipher.getInstance(KEY_DES);
                cipher.init(Cipher.DECRYPT_MODE, k);
                bytes = cipher.doFinal(data);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return bytes;
        }
    
        /**
         * DES 加密
         * @param data 须要加密的字符串
         * @param key 密钥
         * @return
         */
        public static String encryptDES(String data, String key) {
            // 验证传入的字符串
            if (Strings.isNullOrEmpty(data)) {
                return "";
            }
            // 调用加密方法完毕加密
            byte[] bytes = encryptDES(data.getBytes(), key);
            // 将得到的字节数组变成字符串返回
            return byteArrayToHexString(bytes);
        }
    
        /**
         * DES 加密
         * @param data 须要加密的字节数组
         * @param key 密钥
         * @return
         */
        public static byte[] encryptDES(byte[] data, String key) {
            byte[] bytes = null;
            try {
                Key k = byteToKey(decryptBase64(key));
                Cipher cipher = Cipher.getInstance(KEY_DES);
                cipher.init(Cipher.ENCRYPT_MODE, k);
                bytes = cipher.doFinal(data);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return bytes;
        }
    
    
        /**
         * BASE64 解密
         * @param key 须要解密的字符串
         * @return 字节数组
         * @throws Exception
         */
        public static byte[] decryptBase64(String key) throws Exception {
            return (new BASE64Decoder()).decodeBuffer(key);
        }
    
        /**
         * BASE64 加密
         * @param key 须要加密的字节数组
         * @return 字符串
         * @throws Exception
         */
        public static String encryptBase64(byte[] key) throws Exception {
            return (new BASE64Encoder()).encodeBuffer(key);
        }
    
        /**
         * 将一个字节转化成十六进制形式的字符串
         * @param b 字节数组
         * @return 字符串
         */
        private static String byteToHexString(byte b) {
            int ret = b;
            //System.out.println("ret = " + ret);
            if (ret < 0) {
                ret += 256;
            }
            int m = ret / 16;
            int n = ret % 16;
            return hexDigits[m] + hexDigits[n];
        }
    
        /**
         * 转换字节数组为十六进制字符串
         * @param bytes 字节数组
         * @return 十六进制字符串
         */
        private static String byteArrayToHexString(byte[] bytes) {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < bytes.length; i++) {
                sb.append(byteToHexString(bytes[i]));
            }
            return sb.toString();
        }
    
    
        /**
         * 转换十六进制字符串为字节数组
         * @param hexstr 十六进制字符串
         * @return
         */
        public static byte[] hexString2Bytes(String hexstr) {
            byte[] b = new byte[hexstr.length() / 2];
            int j = 0;
            for (int i = 0; i < b.length; i++) {
                char c0 = hexstr.charAt(j++);
                char c1 = hexstr.charAt(j++);
                b[i] = (byte) ((parse(c0) << 4) | parse(c1));
            }
            return b;
        }
    
        /**
         * 转换字符类型数据为整型数据
         * @param c 字符
         * @return
         */
        private static int parse(char c) {
            if (c >= 'a')
                return (c - 'a' + 10) & 0x0f;
            if (c >= 'A')
                return (c - 'A' + 10) & 0x0f;
            return (c - '0') & 0x0f;
        }
    
        /**
         * 測试方法
         * @param args
         */
        public static void main(String[] args) {
            String key = DES.init();
            System.out.println("DES密钥:
    " + key);
    
            String word = "123";
            
    
            String encWord = encryptDES(word, key);
    
            System.out.println(word + "
    加密后:
    " + encWord);
            System.out.println(word + "
    解密后:
    " + decryptDES(encWord, key));
        }
    }</span><span style="font-size: 14px;">
    </span></span>


    结束语


    到这里。这篇文章也就差点儿相同要结束了,希望以上的内容对各位看官有稍许的帮助,哪怕一点也好。事实上,在日常的开发中,假设不是进度控制的特别严格。对于这些原理性的东西,我们还是需要知道的,对于那些细节的东西,能够不用死记硬背。有网的话,随用随查就能够了。但这个前提是,原理性的东西必需要懂,知道了原理。就会有解决思路,有了思路,解决这个问题是迟早的事,细节嘛,不用那么纠结,做的时候考虑到即可了,毕竟时间是有限的。


  • 相关阅读:
    最长公共子序列
    小测试 炒书
    洛谷P1968 美元汇率
    洛谷P3611 [USACO17JAN]Cow Dance Show奶牛舞蹈
    【刷题】【树】【模拟】树网的核
    【刷题】【dp】地精的贸易
    【刷题】【模拟】复制cs
    【刷题】【模拟】3n+1
    【刷题】【dp】中国象棋
    【刷题】【搜索】新数独
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/6789292.html
Copyright © 2011-2022 走看看