精华内容
下载资源
问答
  • 哈希算法的原理

    千次阅读 2012-07-02 19:50:58
    现在我们用哈希算法的思想来管理小猪,我们按照小猪的体重来分猪圈(体重当然不能精确到毫克级别,那样就要分N个猪圈,费用太高。我们考虑到公斤级别级别,这样就可以把小猪按照体重有效的分开来)。 现在如果你想要...

    用一个比喻来说明什么是哈希算法:

    假设有N只小猪,它们的体重各不相同,一开始我们把它们放在一个猪圈里面。如果想寻找其中某只小猪,只能一个一个的找,很耗时间。

    现在我们用哈希算法的思想来管理小猪,我们按照小猪的体重来分猪圈(体重当然不能精确到毫克级别,那样就要分N个猪圈,费用太高。我们考虑到公斤级别级别,这样就可以把小猪按照体重有效的分开来)。

    现在如果你想要找其中某一只小猪,先看看他的体重,然后到对应体重的猪圈里面寻找,这样时间就节省了。


    上面的比喻中的小猪的体重就相当于hashcode,每个变量都有一个hashcode。如果用哈希算法来查找某一个变量,首先要匹配hashcode,这样就能快速的查找了。


    展开全文
  • 一致性哈希算法的原理与实现 转载至https://kefeng.wang/2018/08/10/consistent-hashing/ 分布式系统中对象与节点的映射关系,传统方案是使用对象的哈希值,对节点个数取模,再映射到相应编号的节点,这种方案在...

    一致性哈希算法的原理与实现

    分布式系统中对象与节点的映射关系,传统方案是使用对象的哈希值,对节点个数取模,再映射到相应编号的节点,这种方案在节点个数变动时,绝大多数对象的映射关系会失效而需要迁移;而一致性哈希算法中,当节点个数变动时,映射关系失效的对象非常少,迁移成本也非常小。本文总结了一致性哈希的算法原理和Java实现,并列举了其应用。

    1 概述

    1.1 传统哈希(硬哈希)

    分布式系统中,假设有 n 个节点,传统方案使用 mod(key, n) 映射数据和节点。
    当扩容或缩容时(哪怕只是增减1个节点),映射关系变为 mod(key, n+1) / mod(key, n-1),绝大多数数据的映射关系都会失效。

    1.2 一致性哈希(Consistent Hashing)

    1997年,麻省理工学院(MIT)的 David Karger 等6个人发布学术论文《Consistent hashing and random trees: distributed caching protocols for relieving hot spots on the World Wide Web(一致性哈希和随机树:用于缓解万维网上热点的分布式缓存协议)》,对于 K 个关键字和 n 个槽位(分布式系统中的节点)的哈希表,增减槽位后,平均只需对 K/n 个关键字重新映射。

    1.3 哈希指标

    评估一个哈希算法的优劣,有如下指标,而一致性哈希全部满足:

    • 均衡性(Balance):将关键字的哈希地址均匀地分布在地址空间中,使地址空间得到充分利用,这是设计哈希的一个基本特性。
    • 单调性(Monotonicity): 单调性是指当地址空间增大时,通过哈希函数所得到的关键字的哈希地址也能映射的新的地址空间,而不是仅限于原先的地址空间。或等地址空间减少时,也是只能映射到有效的地址空间中。简单的哈希函数往往不能满足此性质。
    • 分散性(Spread): 哈希经常用在分布式环境中,终端用户通过哈希函数将自己的内容存到不同的缓冲区。此时,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。
    • 负载(Load): 负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。

    1.4 资料链接

    原始论文《Consistent Hashing and Random Trees》链接如下:

    相关论文《Web Caching with Consistent Hashing》链接如下:

    更多资料:
    WikiPedia - Consistent hashing
    codeproject - Consistent hashing

    2 算法原理

    2.1 映射方案

    2.1.1 公用哈希函数和哈希环

    设计哈希函数 Hash(key),要求取值范围为 [0, 2^32)
    各哈希值在上图 Hash 环上的分布:时钟12点位置为0,按顺时针方向递增,临近12点的左侧位置为2^32-1。

    2.1.2 节点(Node)映射至哈希环

    如图哈希环上的绿球所示,四个节点 Node A/B/C/D,
    其 IP 地址或机器名,经过同一个 Hash() 计算的结果,映射到哈希环上。

    2.1.3 对象(Object)映射于哈希环

    如图哈希环上的黄球所示,四个对象 Object A/B/C/D,
    其键值,经过同一个 Hash() 计算的结果,映射到哈希环上。

    2.1.4 对象(Object)映射至节点(Node)

    在对象和节点都映射至同一个哈希环之后,要确定某个对象映射至哪个节点,
    只需从该对象开始,沿着哈希环顺时针方向查找,找到的第一个节点,即是。
    可见,Object A/B/C/D 分别映射至 Node A/B/C/D。

    2.2 删除节点

    现实场景:服务器缩容时删除节点,或者有节点宕机。如下图,要删除节点 Node C:
    只会影响欲删除节点(Node C)与上一个(顺时针为前进方向)节点(Node B)与之间的对象,也就是 Object C,
    这些对象的映射关系,按照 2.1.4 的规则,调整映射至欲删除节点的下一个节点 Node D。
    其他对象的映射关系,都无需调整。

    2.3 增加节点

    现实场景:服务器扩容时增加节点。比如要在 Node B/C 之间增加节点 Node X:
    只会影响欲新增节点(Node X)与上一个(顺时针为前进方向)节点(Node B)与之间的对象,也就是 Object C,
    这些对象的映射关系,按照 2.1.4 的规则,调整映射至新增的节点 Node X。
    其他对象的映射关系,都无需调整。

    2.4 虚拟节点

    对于前面的方案,节点数越少,越容易出现节点在哈希环上的分布不均匀,导致各节点映射的对象数量严重不均衡(数据倾斜);相反,节点数越多越密集,数据在哈希环上的分布就越均匀。
    但实际部署的物理节点有限,我们可以用有限的物理节点,虚拟出足够多的虚拟节点(Virtual Node),最终达到数据在哈希环上均匀分布的效果:
    如下图,实际只部署了2个节点 Node A/B,
    每个节点都复制成3倍,结果看上去是部署了6个节点。
    可以想象,当复制倍数为 2^32 时,就达到绝对的均匀,通常可取复制倍数为32或更高。
    虚拟节点哈希值的计算方法调整为:对“节点的IP(或机器名)+虚拟节点的序号(1~N)”作哈希。

    3 算法实现

    一致性哈希算法有多种具体的实现,包括 Chord 算法,KAD 算法等,都比较复杂。
    这里给出一个简易实现及其演示,可以看到一致性哈希的均衡性和单调性的优势。
    单调性在本例中没有统计数据,但根据前面原理可知,增删节点后只有很少量的数据需要调整映射关系。

    3.1 源码

    /**
     * @author: https://kefeng.wang
     * @date: 2018-08-10 11:08
     **/
    public class ConsistentHashing {
        // 物理节点
        private Set<String> physicalNodes = new TreeSet<String>() {
            {
                add("192.168.1.101");
                add("192.168.1.102");
                add("192.168.1.103");
                add("192.168.1.104");
            }
        };
    
        //虚拟节点
        private final int VIRTUAL_COPIES = 1048576; // 物理节点至虚拟节点的复制倍数
        private TreeMap<Long, String> virtualNodes = new TreeMap<>(); // 哈希值 => 物理节点
    
        // 32位的 Fowler-Noll-Vo 哈希算法
        // https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function
        private static Long FNVHash(String key) {
            final int p = 16777619;
            Long hash = 2166136261L;
            for (int idx = 0, num = key.length(); idx < num; ++idx) {
                hash = (hash ^ key.charAt(idx)) * p;
            }
            hash += hash << 13;
            hash ^= hash >> 7;
            hash += hash << 3;
            hash ^= hash >> 17;
            hash += hash << 5;
    
            if (hash < 0) {
                hash = Math.abs(hash);
            }
            return hash;
        }
    
        // 根据物理节点,构建虚拟节点映射表
        public ConsistentHashing() {
            for (String nodeIp : physicalNodes) {
                addPhysicalNode(nodeIp);
            }
        }
    
        // 添加物理节点
        public void addPhysicalNode(String nodeIp) {
            for (int idx = 0; idx < VIRTUAL_COPIES; ++idx) {
                long hash = FNVHash(nodeIp + "#" + idx);
                virtualNodes.put(hash, nodeIp);
            }
        }
    
        // 删除物理节点
        public void removePhysicalNode(String nodeIp) {
            for (int idx = 0; idx < VIRTUAL_COPIES; ++idx) {
                long hash = FNVHash(nodeIp + "#" + idx);
                virtualNodes.remove(hash);
            }
        }
    
        // 查找对象映射的节点
        public String getObjectNode(String object) {
            long hash = FNVHash(object);
            SortedMap<Long, String> tailMap = virtualNodes.tailMap(hash); // 所有大于 hash 的节点
            Long key = tailMap.isEmpty() ? virtualNodes.firstKey() : tailMap.firstKey();
            return virtualNodes.get(key);
        }
    
        // 统计对象与节点的映射关系
        public void dumpObjectNodeMap(String label, int objectMin, int objectMax) {
            // 统计
            Map<String, Integer> objectNodeMap = new TreeMap<>(); // IP => COUNT
            for (int object = objectMin; object <= objectMax; ++object) {
                String nodeIp = getObjectNode(Integer.toString(object));
                Integer count = objectNodeMap.get(nodeIp);
                objectNodeMap.put(nodeIp, (count == null ? 0 : count + 1));
            }
    
            // 打印
            double totalCount = objectMax - objectMin + 1;
            System.out.println("======== " + label + " ========");
            for (Map.Entry<String, Integer> entry : objectNodeMap.entrySet()) {
                long percent = (int) (100 * entry.getValue() / totalCount);
                System.out.println("IP=" + entry.getKey() + ": RATE=" + percent + "%");
            }
        }
    
        public static void main(String[] args) {
            ConsistentHashing ch = new ConsistentHashing();
    
            // 初始情况
            ch.dumpObjectNodeMap("初始情况", 0, 65536);
    
            // 删除物理节点
            ch.removePhysicalNode("192.168.1.103");
            ch.dumpObjectNodeMap("删除物理节点", 0, 65536);
    
            // 添加物理节点
            ch.addPhysicalNode("192.168.1.108");
            ch.dumpObjectNodeMap("添加物理节点", 0, 65536);
        }
    }

    3.2 复制倍数为 1 时的均衡性

    修改代码中 VIRTUAL_COPIES = 1(相当于没有虚拟节点),运行结果如下(可见各节点负荷很不均衡):

    ======== 初始情况 ========
    IP=192.168.1.101: RATE=45%
    IP=192.168.1.102: RATE=3%
    IP=192.168.1.103: RATE=28%
    IP=192.168.1.104: RATE=22%
    ======== 删除物理节点 ========
    IP=192.168.1.101: RATE=45%
    IP=192.168.1.102: RATE=3%
    IP=192.168.1.104: RATE=51%
    ======== 添加物理节点 ========
    IP=192.168.1.101: RATE=45%
    IP=192.168.1.102: RATE=3%
    IP=192.168.1.104: RATE=32%
    IP=192.168.1.108: RATE=18%

    3.2 复制倍数为 32 时的均衡性

    修改代码中 VIRTUAL_COPIES = 32,运行结果如下(可见各节点负荷比较均衡):

    ======== 初始情况 ========
    IP=192.168.1.101: RATE=29%
    IP=192.168.1.102: RATE=21%
    IP=192.168.1.103: RATE=25%
    IP=192.168.1.104: RATE=23%
    ======== 删除物理节点 ========
    IP=192.168.1.101: RATE=39%
    IP=192.168.1.102: RATE=37%
    IP=192.168.1.104: RATE=23%
    ======== 添加物理节点 ========
    IP=192.168.1.101: RATE=35%
    IP=192.168.1.102: RATE=20%
    IP=192.168.1.104: RATE=23%
    IP=192.168.1.108: RATE=20%

    3.2 复制倍数为 1M 时的均衡性

    修改代码中 VIRTUAL_COPIES = 1048576,运行结果如下(可见各节点负荷非常均衡):

    ======== 初始情况 ========
    IP=192.168.1.101: RATE=24%
    IP=192.168.1.102: RATE=24%
    IP=192.168.1.103: RATE=25%
    IP=192.168.1.104: RATE=25%
    ======== 删除物理节点 ========
    IP=192.168.1.101: RATE=33%
    IP=192.168.1.102: RATE=33%
    IP=192.168.1.104: RATE=33%
    ======== 添加物理节点 ========
    IP=192.168.1.101: RATE=25%
    IP=192.168.1.102: RATE=24%
    IP=192.168.1.104: RATE=24%
    IP=192.168.1.108: RATE=24%

    4 应用

    一致性哈希是分布式系统组件负载均衡的首选算法,它既可以在客户端实现,也可以在中间件上实现。其应用有:

    • 分布式散列表(DHT)的设计;
    • 分布式关系数据库(MySQL):分库分表时,计算数据与节点的映射关系;
    • 分布式缓存:Memcached 的客户端实现了一致性哈希,还可以使用中间件 twemproxy 管理 redis/memcache 集群;
    • RPC 框架 Dubbo:用来选择服务提供者;
    • 亚马逊的云存储系统 Dynamo;
    • 分布式 Web 缓存;
    • Bittorrent DHT;
    • LVS。
    • 转载至https://kefeng.wang/2018/08/10/consistent-hashing/

    展开全文
  • 模糊哈希算法的原理与应用

    千次阅读 2013-10-07 19:19:30
    模糊哈希算法的原理与应用 分类: 数据处理2012-05-22 17:24 717人阅读 评论(0) 收藏 举报 算法securitydistance网络协议工作存储 目录(?)[+] 模糊哈希算法的原理与应用 Posted...
     

    模糊哈希算法的原理与应用

    分类: 数据处理 717人阅读 评论(0) 收藏 举报

    目录(?)[+]

    模糊哈希算法的原理与应用

    关于模糊哈希(Fuzzy Hashing)算法,目前网上有几篇中文资料介绍,但均不准确。写这篇文章以纠正,并对其原理和应用作详细的介绍。

    一、概述

    模糊哈希算法又叫基于内容分割的分片分片哈希算法(context triggered piecewise hashing, CTPH),主要用于文件的相似性比较。

    2006年,Jesse Kornblum [1] 提出CTPH,并给出一个名为spamsum的算法实例。随后,Jason Sherman开发了ssdeep [2] 工具以实现这一算法。该算法最初用于取证,后来被用于恶意代码检测,最近又有用于开源软件漏洞挖掘等。

    模糊哈希的主要原理是,使用一个弱哈希计算文件局部内容,在特定条件下对文件进行分片,然后使用一个强哈希对文件每片计算哈希值,取这些值的一部分并连接起来,与分片条件一起构成一个模糊哈希结果。使用一个字符串相似性对比算法判断两个模糊哈希值的相似度有多少,从而判断两个文件的相似程度。

    对文件的部分变化(包括在多处修改、增加、删除部分内容),使用模糊哈希均能发现与源文件的相似关系,是目前判断相似性较好的一种方法。

    二、背景

    一个文件也许有意或无意地产生变化。例如,有意的情况有作者改动文本内容、恶意代码自动变化;无意的情况有传输出错、磁盘存储出错等。如何有效判断两个文件是否相似,从而是同源的?这个问题在很多领域都有遇到。

    最朴素的方法是逐个字节对比,并统计相同字节占文件大小的比例。这存在两个问题:1、对n个文件寻找相似关系,每个文件需要读取和对比n-1次;2、如果在一个文件中插入或删除一个字节,这种方法得到的相似结果也许很糟糕。

    先说第一个问题。自然的想法是对每个文件生成较短的“指纹”,然后不一一比较文件本身,而是一一比较指纹。这样来降低比较量,提高效率。显然,传统的哈希算法(尤其是密码学意义上的哈希)正好可以充当这种“指纹”。

    传统哈希也会面临前面提到的第二个问题。此外,即便是只修改文件中的一个字节,得到的结果也会大不相同(这是哈希算法的基本要求)。

    为了解决后者,Nick Harbour提出所谓的分片哈希(piecewise hashing)算法,并在dcfldd [3] 中进行了实现。其策略非常简单,即每隔一个固定间隔就将文件分片,对每片计算一个哈希值,将这些哈希值一起作相似比较。显然,局部的修改只会影响少量分片的哈希结果,因此从整个文件看还是会有较高的相似性(虽然不是100%)。

    这种方法依然面临前面的一个问题,即在一个文件中插入或删除一个字节,就会失效。

    如何降低这种局部变化带来的全局影响?Kornblum提出了模糊哈希算法,其思想是:不再固定长度分片,而使用文件局部数据的特点来决定是否分片,使得局部的变化(包括修改、增加、删除等)只影响局部的分片方法,而不会将影响扩散至其他分片点。

    三、算法的组成方法

    一个模糊哈希算法由以下部分组成:

    1. 一个弱哈希算法,以及一个分片值,用于分片
    2. 一个强哈希算法,用于计算每片的哈希
    3. 一个压缩映射算法,将每片的哈希值映射为一个更短的值
    4. 一个比较算法,用于对两个模糊哈希值计算相似程度

    3.1 分片

    在文件中读取一部分内容,给弱哈希算法计算,得到一个哈希值。

    通常逐字节读取固定长度的内容,就像网络协议中的滑动窗口一样在文件中以固定窗口滑动,每次都对窗口内的内容计算。因此,为了便捷,通常采用滚动哈希算法(rolling hashing)。这里的滚动哈希是指,比如原来已经计算了abcdef的哈希值h1,接下来要计算bcdefg的哈希值,不需要完全重新计算,只需要h1 – X(a) + Y(g)即可。其中X、Y是两个函数,即只需要相应增减差量对哈希值的影响即可。这种哈希大大可以加快分片判断的速度。

    例如,在ssdeep中,使用Alder-32 [4] 算法作为弱哈希。它实际是一种用于校验和的弱哈希,类似于CRC32,不能用于密码学算法,但计算快速,生成4字节哈希值,并且是滚动哈希。

    除了弱哈希算法,还需要一个分片值,例如n,由它来控制分片条件。在ssdeep中,n的值根据其他条件决定(后面再介绍)。

    当Alder-32哈希值除以n的余数恰好等于n-1时,就在当前位置分片;否则,不分片,窗口往后滚动一个字节,然后再次计算Alder-32哈希值并判断,如此继续。

    3.2 每片求哈希

    将文件分片完以后,就可以对每片分别计算哈希了。可以使用传统的哈希算法,例如MD5。在ssdeep中,使用一个名为Fowler-Noll-Vo hash [5]的哈希算法。

    3.3 压缩映射

    对每一个文件分片,计算得到一个哈希值以后,可以选择将结果压缩短。例如,在ssdeep中,只取FNV哈希结果的最低6位,并用一个ASCII字符表示出来,作为这个分片的最终哈希结果。

    显然,这种压缩映射会损失一部分准确性,引入误报问题。可以选择不做压缩映射,而存储较长的哈希值。不过从实践情况来看,做压缩映射的问题也不大。

    3.4 连接哈希值

    将每片压缩后的哈希值连接到一起,就得到这个文件的模糊哈希值了。如果分片条件参数n对不同文件可能不同,还应该将n纳入模糊哈希值中。

    3.5 比较哈希值

    最后的问题是,对两个文件的模糊哈希值,怎么样比较它们是不是相似?可以采用传统的相似性比较算法。

    在ssdeep中,采用的如下思路。由于ssdeep对每一片得到的哈希值是一个ASCII字符,最终得到的文件模糊哈希值就是一个字符串了。假设是s1、s2,将s1到s2的“加权编辑距离”(weighted edit distance)作为评价其相似性的依据。

    这里的加权编辑距离是指,先判断从s1变为s2,最少需要多少步操作(包括插入、删除、修改、交换),然后对不同操作给出一个权值,将结果加起来,即得是加权编辑距离。

    接下来,ssdeep将这个距离除以s1和s2的长度和,以将绝对结果变为相对结果,再映射到0-100的一个整数值上,其中,100表示两个字符串完全一致,而0表示完全不相似。

    这样,最后就得到的相似程度的评分,可以用来判断两个文件是否有相似关系。在实践中,一般将ssdeep的结果为1或以上认为有相似性,而将结果为0认为是不相似。

    四、解决问题的原理

    我们来看一下模糊哈希为什么能有效比较相似性。

    如果在一个文件中修改一个字节,有几种情况:

    1、这个字节原来不影响分片,改动后也不影响分片,则这次修改只会影响一个分片哈希,对全局的影响微乎其微,最后相似结果极高;

    2、这个字节原来不影响分片,改动后影响分片,则这次修改会影响两个分片哈希,并且造成两个文件的模糊哈希值不一样长,但相似性比较算法允许这种差异(无非是改动一个加上插入一个字母),其他大部分结果依然一致,因此相似结果依然很高;

    3、这个字节原来影响分片,改动后不影响分片,与情况2类似;

    4、这个字节原来影响分片,改动后依然影响分片,与情况1类似。

    如果在一个文件中增加一个字节,同样有上述四种情况,但与上述逻辑类似,同样的对两个文件的模糊哈希值影响极小。如果在文件中删除一个字节,也与此类似。

    因此,我们可以看到,在模糊哈希中,通过恰当的分片策略,以及相似度比较算法,可以有效地将细节变化对全局结果的影响限制在局部,从而对最终相似性作出有效的判断。事实上,即便是改动连续几个字符,或者作多处改动,模糊哈希算法依然可能作出有效的判断。

    最神奇的是,对一篇文章,与它的一个片段(例如一个段落),通过模糊哈希也能判断出它们的相似性。这种独特的性质会非常有用!

    五、ssdeep的其他改进

    在实际使用中,还是存在一个问题——如果分片数量太小,例如整个文件只有一处触发了分片条件,或者干脆就没有触发分片条件,那最后得到的模糊哈希值干脆只有少数几个字节,这时候退化成了传统的全文哈希了,怎么办?

    ssdeep精明地解决了这个问题。它根据文件长度和文件实际内容来决定如何分片。这是通过调整分片条件n的值来实现的。

    我们来想一下,即便对弱哈希,都有这样的性质,即产生的结果在其可能空间上是接近于均匀分布的。在ssdeep中,n的值始终取2的整数次方,这样Alder-32哈希值除以n的余数也接近于均匀分布。仅当余数等于n-1时分片,就相当于只有差不多1/n的情况下会分片。也就是说,对一个文件,窗口每移动一次,就有1/n的可能要分片。如果某一次分的片数太小,那就减小n的值,使每次分片的可能性增加,增大片数。而如果觉得分的片太多,就增大n的值,使每次分片的可能性减少,降低片数。在ssdeep中,每次都是将n除以或者乘以2,来调整,使最终的片数尽可能在32到64之间。

    实际上,由于分片的可能性差不多是1/n,所以每次ssdeep运行时,第一次尝试的n值就是一个接近于文件长度/64的值。事实表明效果不错。这反过来说明Alder-32的结果分布比较均匀。

    因为n的值可能与文件长度、文件内容有关,因此ssdeep中,n的值会作为一个最终结果的一部分出现。

    上述策略下,另一个问题出现了。这是一种比较极端的情况。假设一个文件使用的分片值n。在该文件中改动一个字节(修改、插入、删除等),且这个改动影响了分片的数量,使得分片数增加或减少,超出了边界范围,因此程序决定调整一下n,例如把n乘以或者除以2。因此,即便对文件的一个字节改动,也可能导致分片条件n的变化,从而导致分片数相差近一倍,而得到的结果显然也毫无相似性了。怎么办?

    ssdeep解决了这种问题。对每一个文件,它同时使用n和n/2作为分片值,算得两个不同的模糊哈希值,而这两个值都使用。因此,最后得到的一个文件的模糊哈希值是:

    n:h(n):h(n/2)

    而在比较时,如果两个文件的分片值分别为n和m,则判断是否有n==m, n==2m, 2n==m三种情况,如果有之一,则将两者相应的模糊哈希值进行比较。例如,如果n==2m,则比较h(n/2)与h(m)是否相似。这样,在一定程序上解决了分片值变化的问题。

    六、应用

    模糊哈希最初应用于计算机取证,随即,反病毒领域发现了它的妙处,试图将其用于恶意代码的变种检测,这些工作包括[6] 、[7]和[8]。然而根据[8],直接使用的效果并不佳,应考虑与其他方法配合使用。[9]对模糊哈希用于恶意代码检测有科普性的介绍。

    然而,在源码相似性比较上,模糊哈希表现出较好的准确性,因此被用于开源代码漏洞挖掘。在现实中,大量的开源项目使用第三方开源库,而又这些库引入的漏洞并不同步更新,因此存在潜在的安全威胁。于此有关的一些工作[10]使用了模糊哈希来判断开源项目之间的相互引用关系,并取得了杰出的成绩。

    七、进一步工作

    对模糊哈希的主要问题存在于两个方面,首先就是其准确性。

    一是其并非一种精确判断,然而事实上对“相似性”的定义本来就不固定,而且就是一种不精确的概念。在目前,模糊哈希是一种相对较好的通用文件相似性比较方法。

    二是对其的专门攻击,在计算机取证中,这种攻击有可能产生一些问题。在[11]中对此有专门的研究。

    另一方面则是其性能表现上。ssdeep并不适合于进行大规模计算,例如,为了计算得到一个合适的分片值n,也许要多次读取全文并作大量计算。以ssdeep为代表的模糊哈希算法从设计和实现技巧两方面均存在速度问题。[12]对此做了改进和优化,得到了更好的性能和准确性。

    申明

    致勤奋的审查员:专利“基于模糊哈希算法的恶意代码检测系统及方法”的第一发明人与本文作者是同一人。本文首次发表于2012年2月6日,在该专利的申请日之后公开。


    原文出处: http://blog.claudxiao.net/2012/02/fuzzy_hashing/#comment-489


    参考文献

    [1] Jesse Kornblum. Identifying almost identical files using context triggered piecewise hashing. Digital Investigation, 2006, pp. 91 – 97

    [2] Jason Sherman. ssdeep. Available from: http://ssdeep.sourceforge.net/

    [3] Harbour Nicholas. dcfldd. Available from: http://dcfldd.sourceforge.net/

    [4] Wikipedia. Adler-32. Available from: http://en.wikipedia.org/wiki/Adler-32

    [5] Wikipedia. Fowler–Noll–Vo hash function. Available from: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function

    [6] Daniel Raygoza. Automated Malware Similarity Analysis. Black Hat 2009

    [7] Michael Smith. Identifying Malware with Byte Frequency Distribution and Context Triggered Piecewise Hashing. James Madison University Infosec Techreport, 2008.4

    [8] DigitalNinja. Fuzzy Clarity: Using Fuzzy Hashing Techniques to Identify Malicious Code. 2007.4

    [9] Michael Kassner. Fuzzy hashing helps researchers spot morphing malware. Available from: http://www.techrepublic.com/blog/security/fuzzy-hashing-helps-researchers-spot-morphing-malware/5274 , 2011.4

    [10] Silvio Cesare. Automated Detection of Software Bugs and Vulnerabilities in Linux. Ruxcon, 2011

    [11] Harald Baier, Frank Breitinger. Security Aspects of Piecewise Hashing in Computer Forensics. Sixth International Conference on IT Security Incident Management and IT Forensics, 2011.5

    [12] Long Chen, Guoyin Wang. An Efficient Piecewise Hashing Method for Computer Forensics. Workshop on Knowledge Discovery and Data Mining, 2008

    模糊哈希算法的原理与应用

    关于模糊哈希(Fuzzy Hashing)算法,目前网上有几篇中文资料介绍,但均不准确。写这篇文章以纠正,并对其原理和应用作详细的介绍。

    一、概述

    模糊哈希算法又叫基于内容分割的分片分片哈希算法(context triggered piecewise hashing, CTPH),主要用于文件的相似性比较。

    2006年,Jesse Kornblum [1] 提出CTPH,并给出一个名为spamsum的算法实例。随后,Jason Sherman开发了ssdeep [2] 工具以实现这一算法。该算法最初用于取证,后来被用于恶意代码检测,最近又有用于开源软件漏洞挖掘等。

    模糊哈希的主要原理是,使用一个弱哈希计算文件局部内容,在特定条件下对文件进行分片,然后使用一个强哈希对文件每片计算哈希值,取这些值的一部分并连接起来,与分片条件一起构成一个模糊哈希结果。使用一个字符串相似性对比算法判断两个模糊哈希值的相似度有多少,从而判断两个文件的相似程度。

    对文件的部分变化(包括在多处修改、增加、删除部分内容),使用模糊哈希均能发现与源文件的相似关系,是目前判断相似性较好的一种方法。

    二、背景

    一个文件也许有意或无意地产生变化。例如,有意的情况有作者改动文本内容、恶意代码自动变化;无意的情况有传输出错、磁盘存储出错等。如何有效判断两个文件是否相似,从而是同源的?这个问题在很多领域都有遇到。

    最朴素的方法是逐个字节对比,并统计相同字节占文件大小的比例。这存在两个问题:1、对n个文件寻找相似关系,每个文件需要读取和对比n-1次;2、如果在一个文件中插入或删除一个字节,这种方法得到的相似结果也许很糟糕。

    先说第一个问题。自然的想法是对每个文件生成较短的“指纹”,然后不一一比较文件本身,而是一一比较指纹。这样来降低比较量,提高效率。显然,传统的哈希算法(尤其是密码学意义上的哈希)正好可以充当这种“指纹”。

    传统哈希也会面临前面提到的第二个问题。此外,即便是只修改文件中的一个字节,得到的结果也会大不相同(这是哈希算法的基本要求)。

    为了解决后者,Nick Harbour提出所谓的分片哈希(piecewise hashing)算法,并在dcfldd [3] 中进行了实现。其策略非常简单,即每隔一个固定间隔就将文件分片,对每片计算一个哈希值,将这些哈希值一起作相似比较。显然,局部的修改只会影响少量分片的哈希结果,因此从整个文件看还是会有较高的相似性(虽然不是100%)。

    这种方法依然面临前面的一个问题,即在一个文件中插入或删除一个字节,就会失效。

    如何降低这种局部变化带来的全局影响?Kornblum提出了模糊哈希算法,其思想是:不再固定长度分片,而使用文件局部数据的特点来决定是否分片,使得局部的变化(包括修改、增加、删除等)只影响局部的分片方法,而不会将影响扩散至其他分片点。

    三、算法的组成方法

    一个模糊哈希算法由以下部分组成:

    1. 一个弱哈希算法,以及一个分片值,用于分片
    2. 一个强哈希算法,用于计算每片的哈希
    3. 一个压缩映射算法,将每片的哈希值映射为一个更短的值
    4. 一个比较算法,用于对两个模糊哈希值计算相似程度

    3.1 分片

    在文件中读取一部分内容,给弱哈希算法计算,得到一个哈希值。

    通常逐字节读取固定长度的内容,就像网络协议中的滑动窗口一样在文件中以固定窗口滑动,每次都对窗口内的内容计算。因此,为了便捷,通常采用滚动哈希算法(rolling hashing)。这里的滚动哈希是指,比如原来已经计算了abcdef的哈希值h1,接下来要计算bcdefg的哈希值,不需要完全重新计算,只需要h1 – X(a) + Y(g)即可。其中X、Y是两个函数,即只需要相应增减差量对哈希值的影响即可。这种哈希大大可以加快分片判断的速度。

    例如,在ssdeep中,使用Alder-32 [4] 算法作为弱哈希。它实际是一种用于校验和的弱哈希,类似于CRC32,不能用于密码学算法,但计算快速,生成4字节哈希值,并且是滚动哈希。

    除了弱哈希算法,还需要一个分片值,例如n,由它来控制分片条件。在ssdeep中,n的值根据其他条件决定(后面再介绍)。

    当Alder-32哈希值除以n的余数恰好等于n-1时,就在当前位置分片;否则,不分片,窗口往后滚动一个字节,然后再次计算Alder-32哈希值并判断,如此继续。

    3.2 每片求哈希

    将文件分片完以后,就可以对每片分别计算哈希了。可以使用传统的哈希算法,例如MD5。在ssdeep中,使用一个名为Fowler-Noll-Vo hash [5]的哈希算法。

    3.3 压缩映射

    对每一个文件分片,计算得到一个哈希值以后,可以选择将结果压缩短。例如,在ssdeep中,只取FNV哈希结果的最低6位,并用一个ASCII字符表示出来,作为这个分片的最终哈希结果。

    显然,这种压缩映射会损失一部分准确性,引入误报问题。可以选择不做压缩映射,而存储较长的哈希值。不过从实践情况来看,做压缩映射的问题也不大。

    3.4 连接哈希值

    将每片压缩后的哈希值连接到一起,就得到这个文件的模糊哈希值了。如果分片条件参数n对不同文件可能不同,还应该将n纳入模糊哈希值中。

    3.5 比较哈希值

    最后的问题是,对两个文件的模糊哈希值,怎么样比较它们是不是相似?可以采用传统的相似性比较算法。

    在ssdeep中,采用的如下思路。由于ssdeep对每一片得到的哈希值是一个ASCII字符,最终得到的文件模糊哈希值就是一个字符串了。假设是s1、s2,将s1到s2的“加权编辑距离”(weighted edit distance)作为评价其相似性的依据。

    这里的加权编辑距离是指,先判断从s1变为s2,最少需要多少步操作(包括插入、删除、修改、交换),然后对不同操作给出一个权值,将结果加起来,即得是加权编辑距离。

    接下来,ssdeep将这个距离除以s1和s2的长度和,以将绝对结果变为相对结果,再映射到0-100的一个整数值上,其中,100表示两个字符串完全一致,而0表示完全不相似。

    这样,最后就得到的相似程度的评分,可以用来判断两个文件是否有相似关系。在实践中,一般将ssdeep的结果为1或以上认为有相似性,而将结果为0认为是不相似。

    四、解决问题的原理

    我们来看一下模糊哈希为什么能有效比较相似性。

    如果在一个文件中修改一个字节,有几种情况:

    1、这个字节原来不影响分片,改动后也不影响分片,则这次修改只会影响一个分片哈希,对全局的影响微乎其微,最后相似结果极高;

    2、这个字节原来不影响分片,改动后影响分片,则这次修改会影响两个分片哈希,并且造成两个文件的模糊哈希值不一样长,但相似性比较算法允许这种差异(无非是改动一个加上插入一个字母),其他大部分结果依然一致,因此相似结果依然很高;

    3、这个字节原来影响分片,改动后不影响分片,与情况2类似;

    4、这个字节原来影响分片,改动后依然影响分片,与情况1类似。

    如果在一个文件中增加一个字节,同样有上述四种情况,但与上述逻辑类似,同样的对两个文件的模糊哈希值影响极小。如果在文件中删除一个字节,也与此类似。

    因此,我们可以看到,在模糊哈希中,通过恰当的分片策略,以及相似度比较算法,可以有效地将细节变化对全局结果的影响限制在局部,从而对最终相似性作出有效的判断。事实上,即便是改动连续几个字符,或者作多处改动,模糊哈希算法依然可能作出有效的判断。

    最神奇的是,对一篇文章,与它的一个片段(例如一个段落),通过模糊哈希也能判断出它们的相似性。这种独特的性质会非常有用!

    五、ssdeep的其他改进

    在实际使用中,还是存在一个问题——如果分片数量太小,例如整个文件只有一处触发了分片条件,或者干脆就没有触发分片条件,那最后得到的模糊哈希值干脆只有少数几个字节,这时候退化成了传统的全文哈希了,怎么办?

    ssdeep精明地解决了这个问题。它根据文件长度和文件实际内容来决定如何分片。这是通过调整分片条件n的值来实现的。

    我们来想一下,即便对弱哈希,都有这样的性质,即产生的结果在其可能空间上是接近于均匀分布的。在ssdeep中,n的值始终取2的整数次方,这样Alder-32哈希值除以n的余数也接近于均匀分布。仅当余数等于n-1时分片,就相当于只有差不多1/n的情况下会分片。也就是说,对一个文件,窗口每移动一次,就有1/n的可能要分片。如果某一次分的片数太小,那就减小n的值,使每次分片的可能性增加,增大片数。而如果觉得分的片太多,就增大n的值,使每次分片的可能性减少,降低片数。在ssdeep中,每次都是将n除以或者乘以2,来调整,使最终的片数尽可能在32到64之间。

    实际上,由于分片的可能性差不多是1/n,所以每次ssdeep运行时,第一次尝试的n值就是一个接近于文件长度/64的值。事实表明效果不错。这反过来说明Alder-32的结果分布比较均匀。

    因为n的值可能与文件长度、文件内容有关,因此ssdeep中,n的值会作为一个最终结果的一部分出现。

    上述策略下,另一个问题出现了。这是一种比较极端的情况。假设一个文件使用的分片值n。在该文件中改动一个字节(修改、插入、删除等),且这个改动影响了分片的数量,使得分片数增加或减少,超出了边界范围,因此程序决定调整一下n,例如把n乘以或者除以2。因此,即便对文件的一个字节改动,也可能导致分片条件n的变化,从而导致分片数相差近一倍,而得到的结果显然也毫无相似性了。怎么办?

    ssdeep解决了这种问题。对每一个文件,它同时使用n和n/2作为分片值,算得两个不同的模糊哈希值,而这两个值都使用。因此,最后得到的一个文件的模糊哈希值是:

    n:h(n):h(n/2)

    而在比较时,如果两个文件的分片值分别为n和m,则判断是否有n==m, n==2m, 2n==m三种情况,如果有之一,则将两者相应的模糊哈希值进行比较。例如,如果n==2m,则比较h(n/2)与h(m)是否相似。这样,在一定程序上解决了分片值变化的问题。

    六、应用

    模糊哈希最初应用于计算机取证,随即,反病毒领域发现了它的妙处,试图将其用于恶意代码的变种检测,这些工作包括[6] 、[7]和[8]。然而根据[8],直接使用的效果并不佳,应考虑与其他方法配合使用。[9]对模糊哈希用于恶意代码检测有科普性的介绍。

    然而,在源码相似性比较上,模糊哈希表现出较好的准确性,因此被用于开源代码漏洞挖掘。在现实中,大量的开源项目使用第三方开源库,而又这些库引入的漏洞并不同步更新,因此存在潜在的安全威胁。于此有关的一些工作[10]使用了模糊哈希来判断开源项目之间的相互引用关系,并取得了杰出的成绩。

    七、进一步工作

    对模糊哈希的主要问题存在于两个方面,首先就是其准确性。

    一是其并非一种精确判断,然而事实上对“相似性”的定义本来就不固定,而且就是一种不精确的概念。在目前,模糊哈希是一种相对较好的通用文件相似性比较方法。

    二是对其的专门攻击,在计算机取证中,这种攻击有可能产生一些问题。在[11]中对此有专门的研究。

    另一方面则是其性能表现上。ssdeep并不适合于进行大规模计算,例如,为了计算得到一个合适的分片值n,也许要多次读取全文并作大量计算。以ssdeep为代表的模糊哈希算法从设计和实现技巧两方面均存在速度问题。[12]对此做了改进和优化,得到了更好的性能和准确性。

    申明

    致勤奋的审查员:专利“基于模糊哈希算法的恶意代码检测系统及方法”的第一发明人与本文作者是同一人。本文首次发表于2012年2月6日,在该专利的申请日之后公开。

    参考文献

    [1] Jesse Kornblum. Identifying almost identical files using context triggered piecewise hashing. Digital Investigation, 2006, pp. 91 – 97

    [2] Jason Sherman. ssdeep. Available from: http://ssdeep.sourceforge.net/

    [3] Harbour Nicholas. dcfldd. Available from: http://dcfldd.sourceforge.net/

    [4] Wikipedia. Adler-32. Available from: http://en.wikipedia.org/wiki/Adler-32

    [5] Wikipedia. Fowler–Noll–Vo hash function. Available from: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function

    [6] Daniel Raygoza. Automated Malware Similarity Analysis. Black Hat 2009

    [7] Michael Smith. Identifying Malware with Byte Frequency Distribution and Context Triggered Piecewise Hashing. James Madison University Infosec Techreport, 2008.4

    [8] DigitalNinja. Fuzzy Clarity: Using Fuzzy Hashing Techniques to Identify Malicious Code. 2007.4

    [9] Michael Kassner. Fuzzy hashing helps researchers spot morphing malware. Available from: http://www.techrepublic.com/blog/security/fuzzy-hashing-helps-researchers-spot-morphing-malware/5274 , 2011.4

    [10] Silvio Cesare. Automated Detection of Software Bugs and Vulnerabilities in Linux. Ruxcon, 2011

    [11] Harald Baier, Frank Breitinger. Security Aspects of Piecewise Hashing in Computer Forensics. Sixth International Conference on IT Security Incident Management and IT Forensics, 2011.5

    [12] Long Chen, Guoyin Wang. An Efficient Piecewise Hashing Method for Computer Forensics. Workshop on Knowledge Discovery and Data Mining, 2008



    展开全文
  • 哈希算法的原理及其 Java 实现一、目的二、运行环境三、基本原理及步骤(I)各种加密算法的原理:① DES 数据加密标准(Data Encryption Standard):算法介绍算法流程优点缺点破解方式适用场景安全性② 3DES(DES ...

    11 种加密 & 哈希算法的原理及其 Java 实现

    一、目的

    (1)通过对同一段明文分别进行DES、3DES、AES、PBE、CBC、IDEA、RSA、Caesar 8 种加密、以及 MD5、SHA-1、SHA-256 3 种哈希算法的实现,从而比较不同的加密 / 哈希算法的消耗时间,进而对它们的运行效率进行对比;

    (2)学习 Java 中 javax.crypto.Cipher 类的原理,深入理解 JCE 框架的核心;

    (3)对各种对称、非对称以及哈希算法的原理进行巩固复习,掌握它们实现的基本原理、密钥长度、实现机制、算法流程、应用环境、优缺点、安全性等基础知识。

    二、运行环境

    (1)处理器:Inter ® Pentium ® CPU 3825U @1.90 GHz;

    (2)安装内存 (RAM):4.00 GB;

    (3)系统类型:64 位操作系统;

    (4)Windows 版本:Windows 7;

    (5)运行平台:Eclipse Java EE IDE for Web Developers;
                              Version: Oxygen Release (4.7.0)。

    三、基本原理及步骤

    (I)各种加密算法的原理:

    ① DES 数据加密标准(Data Encryption Standard):

    算法介绍

    1. 属于对称加密算法;
    2. 数据分组(64 位)用密钥(64 位;其中 56 位有效位,8 位校验位)加密;
    3. 算法公开,对密钥保护。

    算法流程

    1. 根据用户输入,取得一个 64 位的密钥,然后进行等分、移位、选取和迭代形成一套 16 个加密密钥,分别提供每轮运算使用;
    2. 对 64 位明文分组 MM 进行操作,MM 经过初期置换 IPIP,置换为 m0{m_{0}},将 m0{m_{0}} 分为左右各 32 位长,并进行 16 轮相同的运算(迭代),每轮运算都和相应的密钥结合;
    3. 在每一轮中,密码位移位,从密钥的 56 位中选出 48 位,通过一个扩展置换将数据右半边扩展成 48 位,并通过异或操作替代成新的 48 位;然后压缩至 32 位,并通过一个异或与左半边结合,其结果为右半边,原来的右半边成为左半边,该操作执行 16 次;
    4. 经过 16 轮迭代,左右部分合在一起进行一个末置换(数据整理),完成加密过程;
    5. 解密时同样使用此算法。

    优点

      算法公开、计算量小、加密速度快、效率高。

    缺点

    1. 如果双方都持有密钥,安全性无法保证;
    2. 密钥安全的保护成本高,管理困难。

    破解方式

      暴力破解、穷举。

    适用场景

      普通数据加密。

    安全性

      低。

    ② 3DES(DES ede)(或称为Triple DES)——是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)的通称 :

    算法介绍

    1. 三重 DES 加密算法;
    2. 每个数据块用三次 DES 加密;
    3. 是 DES 向 AES 过渡的加密算法。

    算法流程

    1. 加密过程:C=Ek3(Dk2(Ek1(P))){\rm{C = }}{{\rm{E}}_{{k_3}}}{\rm{(}}{{\rm{D}}_{{k_2}}}{\rm{(}}{{\rm{E}}_{{k_1}}}{\rm{(P)))}}
    2. 解密过程:P=Dk1(Ek2(Dk3(C))){\rm{P = }}{{\rm{D}}_{{k_1}}}{\rm{(}}{{\rm{E}}_{{k_2}}}{\rm{(}}{{\rm{D}}_{{k_3}}}{\rm{(C)))}}

    破解方式

      难度较大。

    安全性

      较高。

    ③ AES 高级加密标准(Advanced Encryption Standard,AES):

    算法介绍

    1. 属于对称加密算法;
    2. 基于排列置换算法;
    3. 易于软硬件实现;
    4. 属于分组密码体制;
    5. 用于取代原来的 DES。

    算法流程

    1. 对数据进行 128 位(16 字节)的分组加密,每次对一组数据加密需要多轮;
    2. 输入密钥长度为:128、192 或 256,如果不够则补齐;
    3. 加密基本流程:
      1)生成各轮的扩展密钥,存于 key 数组中,包含用户的输入密钥和扩展密钥;
      2)将待加密数组与第一组密钥异或;
      3)最后一轮前的变换操作:
        SubBytes(state)——对数据进行 S 字节变换;
        ShiftRows(state)——进行行变换;
        MixColumns( state )——进行列混合变换;
        AddRoundKey( state,Keys [当前轮密钥组] )——与当前轮密钥异或。
      4)最后一轮变换操作
        invShiftRows(state)——进行反行变换;
        invSubBytes(state)——对数据进行反 S 字节变换;
        AddRoundKey( state, Keys [第一组] )——与第一组密钥进行异或。
    4. 解密流程:与加密相反。
    5. 分组模式:
      1)ECB(电码本模式):
        优点:简单、并行计算、误差不会传递;
        缺点:不能隐藏明文的模式、可能造成对明文的主动攻击。
      2)CBC(密码分组链接):
        优点:能抵抗主动攻击、安全性好于 ECB、适合传输较长报文、是 SSL,IPSec 的标准;
        缺点:不利于并行计算、有误差传递、需要初始化向量;
      3)CFB(密码反馈模式):
        优点:隐藏明文的模式、将分组密码转化为流模式、可以及时加密传送分组的数据;
        缺点:不利于并行计算、有误差传递、IVIV值唯一。
      4)OFB(输出反馈模式):
        优点:隐藏明文、将分组密码转化流模式、可以及时加密传送分组的数据;
        缺点:不利于并行计算、可能对明文产生主动攻击、误差传递。
      5)CTR(计数器模式):
        优点:并行计算、仅要求实现加密算法而无需解密算法、无需填充、可以作为流进行高效加密。
    6. 常用填充方式:
      NoPadding——不填充;
      ZerosPadding——0 填充;
      PKCS5Padding——每个填充都记录了填充的总数。

    优点

      分组模式选择多,加密安全。

    缺点

    1. 同 DES 类似,存在密钥管理问题;
    2. 曾遭受线性密码攻击、差分密码攻击。

    安全性

      较高。

    ④ PBE(Password Based Encryption,基于口令加密):

    算法原理

      PBE(Password Based Encryption,基于口令加密)是一种基于口令的加密算法,其特点是使用口令代替了密钥,而口令由用户自己掌管,采用随机数、杂凑、多重加密等方法保证数据的安全性。

      PBE 算法在加密过程中并不是直接使用口令来加密,而是加密的密钥由口令生成,这个功能由 PBE 算法中的 KDF 函数完成。
    在这里插入图片描述

    算法流程

      KDF 函数的实现过程为:

    1. 将用户输入的口令首先通过“盐”(salt)的扰乱产生准密钥;
    2. 将准密钥经过散列函数,多次迭代后,生成最终的加密密钥;
    3. 密钥生成后,PBE 算法再使用对称加密算法对数据进行加密,可以选择 DES、3DES、RC5 等对称加密算法。

    ⑤ IDEA(国际数据加密算法):

    算法介绍

      国际数据加密算法(IDEA)是上海交通大学教授来学嘉与瑞士学者 James Massey 联合提出的,它在 1990 年正式公布并得到增强。这种算法是在 DES 算法的基础上发展出来的,类似于三重 DES。发展 IDEA 也是因为 DES 密钥太短等缺点,IDEA 的密钥为 128 位,在今后若干年内应该是安全的。

    算法特点

      类似于 DES,IDEA 算法也是一种分组加密算法,它设计了一系列加密轮次,每轮加密都使用从完整的加密密钥中生成的一个子密钥。与 DES 的不同之处在于,它在软件和硬件实现上同样快速。
      
      由于 IDEA 是在美国之外提出并发展起来的,避开了美国法律上对加密技术的诸多限制,因此,有关 IDEA 算法和实现技术的书籍都可以自由出版和交流,极大地促进了 IDEA 的发展和完善。

    应用领域

      目前 IDEA 在工程中已有大量应用实例:

    1. PGP ( Pretty Good Privacy)使用 IDEA 作为其分组加密算法;
    2. 安全套接字层 SSL(Secure Socket Layer)将 IDEA 包含在其加密算法库 SSLRef 中;
    3. 基于 IDEA 的 Exchange 安全插件;
    4. IDEA 加密芯片;
    5. IDEA 加密软件包等。

    ⑥ RSA:

    算法介绍

    1. 非对称加密;
    2. 密钥长度决定了其复杂度;
    3. 简单原理:公钥加密、私钥解密;
           私钥签名、公钥解密验证。

    算法流程

    1. 随意选择两个大的质数 ppqqpp 不等于 qq,计算 N=p×qN = p × q
    2. 根据欧拉函数,求得 r=(p1)(q1)r = (p - 1)(q - 1)
    3. 选择一个小于 rr 的整数 ee,求得 ee 关于模 rr 的模反元素,命名为 dd(模反元素存在,当且仅当 eerr 互质);
    4. ppqq 的记录销毁;
    5. (N,e)(N,e) 是公钥,(N,d)(N,d) 是私钥。

    优点

      原理简单。

    缺点

    1. 密钥生成较为麻烦,受到素数产生技术的限制,因此难以做到一次一密;
    2. 分组长度太大,不利于数据格式标准化;
    3. 加密难度大。

    应用场景

    1. 数字签名;
    2. 公钥加密;
    3. 防止数据篡改;
    4. 用于通讯领域较多。

    安全性

      高。

    ⑦ 凯撒密码:

    算法介绍

      作为一种最为古老的对称加密体制,凯撒密码在古罗马的时候就已经很流行了。它的基本思想是:通过把字母移动一定的位数来实现加密和解密。例如,如果字母的位数是 3,明文字母 B 就变成了密文的 E,依次类推,X 将变成 A,Y 变成 B,Z 变成 C……由此可见,位数就是凯撒密码加密和解密的密钥。

    算法流程

      一般化的凯撒加密算法为: C=E(k,P)=(P+k)mod26C = E(k, P) = (P + k) mod 26

      一般化的凯撒解密算法为: P=D(k,C)=(Ck)mod26P = D(k, C) = (C - k) mod 26

    1. 由于字母表中共有 26 个字符,因此移位前先将移动的位数 (keykey) 和 26 取模。将字符加上一个正整数即代表在字母表中右移多少位;
    2. 如果移动的位数是负值,则代表在字母表中左移多少位。尽管在移动之前已经将移动的位数和 26 取模,但通过这种方式实现右移或左移仍可能发生超界;
    3. 移位后进行判断,如果向左超界(c <‘a’)则增加 26;向右超界(c >‘z’)则减去 26。

    (II)各种 Hash 算法的原理:

    ① MD5:

    算法介绍

    1. 信息摘要算法;
    2. 压缩性:任意长度的数据,可以算出固定长度;
    3. 容易计算:从原数据计算 MD5 很容易;
    4. 抗修改性:对原数据修改 1 个字节,MD5 值的变化都很大;
    5. 强碰撞性:找一个具有相同 MD5 值的数据(伪造)比较困难;
    6. 具有不可逆性。

    算法流程

      按照 512 位分组处理,每一个分组分为 16 个 32 位子分组,处理后输出 4 个 32 位分组,将这 4 个分组级联后生成 128 位散列值。

    优点

      简单、难以伪造。

    缺点

      具有潜在的冲突;有破解的案例。

    应用场景

    1. 登录密码保护;
    2. 防止文件篡改;
    3. HTTP 传输内容加密防篡改;
    4. 用于数字签名。

    安全性

      较高。

    ② SHA1:

    算法介绍

    1. 属于消息摘要算法;
    2. 用于签名算法,保护数据的完整性;
    3. 算法不可逆;
    4. 消息算法:512 位。

    算法流程

      把原始信息变换成位(二进制)字符串,5 个步骤计算:
      1)补位:消息满足长度在对 512 取模后余数是 448,否则补位;
      2)补长度:原始数据长度补到补位操作的后面,如果大于 512,补成 512 的倍数;
      3)使用常量和相关的函数;
      4)计算消息摘要。

    优点

      保密性强。

    缺点

      效率较低;难度大。

    应用场景

    1. 数字签名;
    2. 数据完整性保护。

    安全性

      高。

    (III)javax.crypto.Cipher 类的原理

      基于 Java 中的 javax.crypto.Cipher 类,可以实现各种算法的加密和解密功能,该类是 JCE 框架的核心。

    1. 与所有的引擎类一样,可以通过调用 Cipher 类中的 getInstance 静态工厂方法得到 Cipher 对象:
         public static Cipher getInstance(String transformation,String provider);
    

      参数 transformation 是一个字符串,它描述了由指定输入产生输出所进行的操作或操作集合。它包含密码学算法名称,比如 DES,也可以在后面包含模式和填充方式。如果没有指定模式或填充方式,就使用特定提供者指定的默认模式或默认填充方式。

      当以流密码方式请求以块划分的 Cipher 时,可以在模式名后面跟上一次运算需要操作的 bit 数目。如果没有指定数目,则使用提供者指定的默认值。

      通过 getInstance 得到的 Cipher 对象使用下列四个模式之一进行初始化,这四个模式在 Cipher 类中被定义为 final integer 常数,可以使用符号名来引用这些模式:

        ENCRYPT_MODE, 加密数据;
        DECRYPT_MODE, 解密数据;
        WRAP_MODE, 将一个 Key 封装成字节,可以用来进行安全传输;
        UNWRAP_MODE, 将前述已封装的密钥解开成 java.security.Key 对象。
    

      每个 Cipher 初始化方法使用一个模式参数 opmod,并用此模式初始化 Cipher 对象。此外还有其他参数,包括密钥 key、包含密钥的证书 certificate、算法参数 params 和随机源 random。

      加密和解密必须使用相同的参数。当 Cipher 对象被初始化时,它将失去以前得到的所有状态,即初始化 Cipher 对象与新建一个 Cipher 实例后将它初始化是等价的。

    1. 调用 doFinal()方法完成单步的加密或解密数据:

      在多步加密或解密数据时,首先需要一次或多次调用 update 方法,用以提供加密或解密的所有数据。

      如果还有输入数据,多步操作可以使用 doFinal 方法之一结束。如果没有数据,多步操作可以使用 doFinal 方法结束。

      如果在 transformation 参数部分指定了 padding 或 unpadding 方式,则所有的 doFinal 方法都要注意所用的 padding 或 unpadding 方式。

      调用 doFinal 方法将会重置 Cipher 对象到使用 init 进行初始化时的状态,也就是说,Cipher 对象被重置,使得可以进行更多数据的加密或解密。这两种模式可以在调用 init 时进行指定。

    3、wrap 密钥必须先使用 WRAP_MODE 初始化 Cipher 对象,然后调用方法:

        public final byte[] wrap(Key key);
    

      如果将调用 wrap 方法后的密钥字节提供给 unwrap 的人使用,必须向接收者发送额外信息。

      (1)密钥算法名称:

      调用 Key 接口提供的 getAlgorithm 方法:

          public String getAlgorithm();
    

      (2)包裹密钥的类型:

         (Cipher.SECRET_KEY,Cipher.PRIVATE_KEY,Cipher.PUBLIC_KEY)
    
    1. SunJCE 提供者实现的 Cipher 算法参数:

      (1)采用 CBC、CFB、OFB、PCBC 模式的 DES、DES-EDE 和 Blowfish算法使用初始化向量 IVIV 作为参数。可以使用 javax.crypto.spec.IvParameterSpec 类并使用给定的 IVIV 参数来初始化 Cipher 对象。

      (2)PBEWithMD5AndDES 使用的参数是一个由盐值和迭代次数组成的参数集合。可以使用 javax.crypto.spec.PBEParameterSpec 类并利用给定盐值和迭代次数来初始化 Cipher 对象。

      (3)Cipher 中的某些 update 和 doFinal 方法允许调用者指定加密或解密数据的输出缓存。此时,保证指定的缓存足够大以容纳加密或解密运算的结果是非常重要的,可以使用 Cipher 的以下方法来决定输出缓存应该有多大:

        public int getOutputSize(int inputLen)。
    

    四、数据记录、仿真及设计

      对于待验证的所有 11 种加解密以及哈希算法,为保证时间效率计算的一致性,因此下面均使用相同的明文 “In doing we learn.” 对其进行加密,具体结果分析如下:

    (一)DES 算法的结果分析

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
      计算 10 次 DES 加密的时间消耗平均值,可以得出,在本系统平台上,DES 算法的平均时间消耗约为 1826 ms。

    (二)3DES 算法的结果分析

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
      计算 10 次 3DES 加密的时间消耗平均值,可以得出,在本系统平台上,3DES 算法的平均时间消耗约为 2021 ms。

    (三)AES 算法的结果分析

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
      计算 10 次 AES 加密的时间消耗平均值,可以得出,在本系统平台上,AES 算法的平均时间消耗约为 1941 ms。

    (四)PBE 算法的结果分析

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
      计算 10 次 PBE 加密的时间消耗平均值,可以得出,在本系统平台上,PBE 算法的平均时间消耗约为 1376 ms。

    (五)CBC 算法的结果分析

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
      计算 10 次 CBC 加密的时间消耗平均值,可以得出,在本系统平台上,CBC 算法的平均时间消耗约为 1318 ms。

    (六)IDEA 算法的结果分析

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
      计算 10 次 IDEA 加密的时间消耗平均值,可以得出,在本系统平台上,IDEA 算法的平均时间消耗约为 2328 ms。

    (七)RSA 算法的结果分析

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
      计算 10 次 RSA 加密的时间消耗平均值,可以得出,在本系统平台上,RSA 算法的平均时间消耗约为 2288 ms。

    (八)MD5 算法的结果分析

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
      计算 10 次 MD5 哈希算法的时间消耗平均值,可以得出,在本系统平台上,MD5 算法的平均时间消耗约为 97 ms。

    (九)SHA1 算法的结果分析

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
      计算 10 次 SHA-1 哈希算法的时间消耗平均值,可以得出,在本系统平台上,SHA-1 算法的平均时间消耗约为 117 ms。

    (十)SHA256 算法的结果分析

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
      计算 10 次 SHA-256 哈希算法的时间消耗平均值,可以得出,在本系统平台上,SHA-256 算法的平均时间消耗约为 145 ms。

    (十一)凯撒加密算法的结果分析

      n = 5:

    在这里插入图片描述
      n = 8 :

    在这里插入图片描述
      n = 16:

    在这里插入图片描述
      n = 21:

    在这里插入图片描述
      计算 10 次 Caesar 加密的时间消耗平均值,可以得出,在本系统平台上,Caesar 算法的平均时间消耗约为 2 ms。

      综合上述各种算法的运行时间,统计出它们分别的效率对比,如图所示:

    在这里插入图片描述

    五、测试结果及分析

    ①结果分析

      通过上面的统计结果可以大致看出:

      (1)针对各种加密算法,在本运行平台上的消耗时间为:

             IDEA > RSA > 3DES > AES > DES > PBE > CBC > Caesar
    

      (2)针对各种 Hash 算法,在本运行平台上的消耗时间为:

             SHA-256 > SHA-1 > MD5
    

    ②总结

    1. 对称算法:
        密钥管理:比较难,不适合互联网,一般用于内部系统;
        安全性:中;
        速度:快好几个数量级(软件加解密速度至少快 100 倍,每秒可以加解密几兆比特的数据),适合大数据量的加解密处理。

    2. 非对称算法:
        密钥管理:密钥容易管理;
        安全性:高;
        速度:慢,适合小数据量加解密或数据签名。

    3. Hash 算法:
        MD5 输出 128 bit、SHA1 输出 160 bit、SHA256 输出 256 bit。

      SHA-1 是 160 位的哈希值,而 SHA-2 是组合值,有不同的位数,其中最受欢迎的是 256 位。

      因为 SHA-2 有多种不同的位数,导致这个名词有一些混乱。但无论是“SHA-2”、“SHA-256” 或 “SHA-256 位”,其实都是同一种加密算法。SHA-224、SHA-384 或 SHA-512 表示 SHA-2 的二进制长度。

      SSL 行业选择 SHA 作为数字签名的散列算法,但随着互联网技术的提升,SHA-1 的缺点越来越突显。在 SHA-2 成为了新的标准之后,签发的 SSL 证书必须使用该算法签名。

      安全性方面,SHA-256 的安全性最高,但是耗时要比其他两种多很多。MD5 相对较容易破解,因此,SHA-1 是这三种中性能较好的一款哈希算法。

    ③遇到的问题及解决方法:

    i)字符串编码出现乱码

      一开始,我在编写加密函数时,由于对字节数组 byte[] 以及 String 对象之间的转化不当,导致了程序加密的输出结果呈现乱码,有许多问号 ?? 等特殊字符,这显然不符合加密结果的要求,如图所示:

    在这里插入图片描述
    在这里插入图片描述

      之后,我尝试在调用 toString 方法时,将输出结果用参数 “utf-8” 强制转换,但效果不佳。在查阅了相关的资料后,我发现可以编写一个函数,将结果用 Base64 编码来表示:

        public static String encryptBase64(byte[] key) throws Exception {
                return (new BASE64Encoder()).encodeBuffer(key);
        }
    

      这样一来,就可以将加密的编码方式转换为 Base64,此时的加密输出结果也更加易于表示、传输与解密。

    ii)com.sun.crypto.provider.SunJCE() 报错

      报错内容:

    Access restriction: The constructor SunJCE() is not accessible due to restriction on required library C:\Program Files\Java\jre7\lib\ext\sunjce_provider.jar
    

      查找相关文档的解释说明后,我发现该错误是由于对应的 jar 包版本较低所导致的,但可以选择忽视这个异常。

      解决方法:

    Window -> Preferences -> Java -> Compiler -> Errors/Warnings -> Deprecated and restricted API -> Forbidden reference (access rules) -> Warnings
    

      如图所示:

    在这里插入图片描述

    六、算法实现代码

    ① DES

    package des;
    
    import  java.security.InvalidKeyException;  
    import  java.security.NoSuchAlgorithmException;  
    import  java.security.Security;  
      
    import  javax.crypto.BadPaddingException;  
    import  javax.crypto.Cipher;  
    import  javax.crypto.IllegalBlockSizeException;  
    import  javax.crypto.KeyGenerator;  
    import  javax.crypto.NoSuchPaddingException;  
    import  javax.crypto.SecretKey;
    
    import sun.misc.BASE64Encoder;  
      
    public class des {     
        //KeyGenerator 提供对称密钥生成器的功能,支持各种算法   
        private KeyGenerator keygen;  
        //SecretKey 负责保存对称密钥   
        private SecretKey deskey;  
        //Cipher负责完成加密或解密工作   
        private Cipher c;  
        //该字节数组负责保存加密的结果   
        private byte [] cipherByte;  
          
        public des() throws NoSuchAlgorithmException, NoSuchPaddingException{  
            Security.addProvider(new com.sun.crypto.provider.SunJCE());  
            //实例化支持DES算法的密钥生成器(算法名称命名需按规定,否则抛出异常)   
            keygen = KeyGenerator.getInstance("DES");  
            //生成密钥   
            deskey = keygen.generateKey();  
            //生成Cipher对象,指定其支持的DES算法   
            c = Cipher.getInstance("DES");  
        }   
        /**  
         * 对字符串加密  
         *   
         * @param str  
         * @return  
         * @throws InvalidKeyException  
         * @throws IllegalBlockSizeException  
         * @throws BadPaddingException  
         */   
        public byte [] Encrytor(String str) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {  
            // 根据密钥,对Cipher对象进行初始化,ENCRYPT_MODE表示加密模式   
            c.init(Cipher.ENCRYPT_MODE, deskey);  
            byte [] src = str.getBytes();  
            // 加密,结果保存在cipherByte中   
            cipherByte = c.doFinal(src);  
            return cipherByte;  
        }  
      
        /**  
         * 对字符串解密  
         *   
         * @param buff  
         * @return  
         * @throws InvalidKeyException  
         * @throws IllegalBlockSizeException  
         * @throws BadPaddingException  
         */   
        public byte [] Decryptor(byte [] buff) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {  
            // 根据密钥,对Cipher对象进行初始化,DECRYPT_MODE表示加密模式   
            c.init(Cipher.DECRYPT_MODE, deskey);  
            cipherByte = c.doFinal(buff);  
            return  cipherByte;  
        }  
        
        public static String ByteToString(byte[] bytes) { 
        	StringBuilder strBuilder = new StringBuilder(); 
        	for (int i = 0; i <bytes.length ; i++) { 
        		if (bytes[i]!=0){ 
        			strBuilder.append((char)bytes[i]); 
        			}
        		else { 
        			break; 
        		} 
        	} 
        	return strBuilder.toString(); 
        }
    
        /**
         * BASE64 加密
         *
         * @param key 需要加密的字节数组
         * @return 字符串
         * @throws Exception
         */
        public static String encryptBase64(byte[] key) throws Exception {
            return (new BASE64Encoder()).encodeBuffer(key);
        }
        
        /**  
         * @param args  
         * @throws NoSuchPaddingException   
         * @throws NoSuchAlgorithmException   
         * @throws BadPaddingException   
         * @throws IllegalBlockSizeException   
         * @throws InvalidKeyException   
         */   
        public static void main(String[] args) throws Exception {  
        	//获取开始时间的时间戳
        	long startTime = System.currentTimeMillis();    
            des de1 = new des();  
            //源码文件是GBK格式,或者这个字符串是从GBK文件中读取出来的, 转换为string 变成unicode格式
            String msg1 = "In doing we learn."; 
            //利用getBytes将unicode字符串转成UTF-8格式的字节数组
            byte[] utf8Bytes = msg1.getBytes("UTF-8"); 
            //用utf-8 对这个字节数组解码成新的字符串
            String msg = new String(utf8Bytes, "UTF-8");
            byte [] encData = de1.Encrytor(msg);  
            String encontent = null;
            encontent = encryptBase64(encData);
            byte [] decontent = de1.Decryptor(encData);  
            System.out.println("----DES 加密结果----"); 
            System.out.println(); 
            System.out.println("待加密的明文: " + msg);  
            //System.out.println("加密后:" + new String(encontent, "utf-8"));  
            System.out.printf("加密的结果为: " + encontent);  
            System.out.println("解密的结果为: " + new String(decontent));  
            //获取结束时间的时间戳 
            long endTime = System.currentTimeMillis();    
            //输出程序运行时间
        	System.out.println("DES 加密的时间消耗为:" + (endTime - startTime) + " ms");    
        }  
    }  
    

    ② 3DES

    package three_des;
    
    import  java.security.InvalidKeyException;  
    import  java.security.NoSuchAlgorithmException;  
    import  java.security.Security;  
      
    import  javax.crypto.BadPaddingException;  
    import  javax.crypto.Cipher;  
    import  javax.crypto.IllegalBlockSizeException;  
    import  javax.crypto.KeyGenerator;  
    import  javax.crypto.NoSuchPaddingException;  
    import  javax.crypto.SecretKey;
    
    import des.des;
    import sun.misc.BASE64Encoder;  
      
    public class three_des {  
        // KeyGenerator 提供对称密钥生成器的功能,支持各种算法   
        private KeyGenerator keygen;  
        // SecretKey 负责保存对称密钥   
        private SecretKey deskey;  
        // Cipher负责完成加密或解密工作   
        private Cipher c;  
        // 该字节数组负责保存加密的结果   
        private byte [] cipherByte;  
      
        public three_des() throws NoSuchAlgorithmException, NoSuchPaddingException {  
            Security.addProvider(new com.sun.crypto.provider.SunJCE());  
            // 实例化支持DES算法的密钥生成器(算法名称命名需按规定,否则抛出异常)   
            keygen = KeyGenerator.getInstance("DESede" );  
            // 生成密钥   
            deskey = keygen.generateKey();  
            // 生成Cipher对象,指定其支持的DES算法   
            c = Cipher.getInstance("DESede" );  
        }  
      
        /**  
         * 对字符串加密  
         *   
         * @param str  
         * @return  
         * @throws InvalidKeyException  
         * @throws IllegalBlockSizeException  
         * @throws BadPaddingException  
         */   
        public byte [] Encrytor(String str) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {  
            // 根据密钥,对Cipher对象进行初始化,ENCRYPT_MODE表示加密模式   
            c.init(Cipher.ENCRYPT_MODE, deskey);  
            byte [] src = str.getBytes();  
            // 加密,结果保存进cipherByte   
            cipherByte = c.doFinal(src);  
            return cipherByte;  
        }  
      
        /**  
         * 对字符串解密  
         *   
         * @param buff  
         * @return  
         * @throws InvalidKeyException  
         * @throws IllegalBlockSizeException  
         * @throws BadPaddingException  
         */   
        public byte [] Decryptor(byte [] buff) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {  
            // 根据密钥,对Cipher对象进行初始化,DECRYPT_MODE表示加密模式   
            c.init(Cipher.DECRYPT_MODE, deskey);  
            cipherByte = c.doFinal(buff);  
            return cipherByte;  
        }  
      
        public static String ByteToString(byte[] bytes) { 
        	StringBuilder strBuilder = new StringBuilder(); 
        	for (int i = 0; i <bytes.length ; i++) { 
        		if (bytes[i]!=0){ 
        			strBuilder.append((char)bytes[i]); 
        			}
        		else { 
        			break; 
        		} 
        	} 
        	return strBuilder.toString(); 
        }
        
        public static String encryptBase64(byte[] key) throws Exception {
            return (new BASE64Encoder()).encodeBuffer(key);
        }
        
        /**  
         * @param args  
         * @throws NoSuchPaddingException   
         * @throws NoSuchAlgorithmException   
         * @throws BadPaddingException   
         * @throws IllegalBlockSizeException   
         * @throws InvalidKeyException   
         */   
        public static void main(String[] args) throws Exception {  
        	//获取开始时间的时间戳
        	long startTime = System.currentTimeMillis();    
            three_des de1 = new three_des();  
            //源码文件是GBK格式,或者这个字符串是从GBK文件中读取出来的, 转换为string 变成unicode格式
            String msg1 = "In doing we learn."; 
            //利用getBytes将unicode字符串转成UTF-8格式的字节数组
            byte[] utf8Bytes = msg1.getBytes("UTF-8"); 
            //用utf-8 对这个字节数组解码成新的字符串
            String msg = new String(utf8Bytes, "UTF-8");
            byte [] encData = de1.Encrytor(msg);  
            String encontent = null;
            encontent = encryptBase64(encData);
            byte [] decontent = de1.Decryptor(encData);  
            System.out.println("----3DES 加密结果----"); 
            System.out.println(); 
            System.out.println("待加密的明文: " + msg);  
            //System.out.println("加密后:" + new String(encontent, "utf-8"));  
            System.out.printf("加密的结果为: " + encontent);  
            System.out.println("解密的结果为: " + new String(decontent));  
            //获取结束时间的时间戳 
            long endTime = System.currentTimeMillis();    
            //输出程序运行时间
        	System.out.println("3DES 加密的时间消耗为:" + (endTime - startTime) + " ms");   
        }  
    }
    

    ③ AES

    package aes;
    
    import  java.security.InvalidKeyException;  
    import  java.security.NoSuchAlgorithmException;  
    import  java.security.Security;  
      
    import  javax.crypto.BadPaddingException;  
    import  javax.crypto.Cipher;  
    import  javax.crypto.IllegalBlockSizeException;  
    import  javax.crypto.KeyGenerator;  
    import  javax.crypto.NoSuchPaddingException;  
    import  javax.crypto.SecretKey;
    
    import sun.misc.BASE64Encoder;  
      
    public class aes {     
        //KeyGenerator 提供对称密钥生成器的功能,支持各种算法   
        private KeyGenerator keygen;  
        //SecretKey 负责保存对称密钥   
        private SecretKey deskey;  
        //Cipher负责完成加密或解密工作   
        private Cipher c;  
        //该字节数组负责保存加密的结果   
        private byte [] cipherByte;  
          
        public aes() throws NoSuchAlgorithmException, NoSuchPaddingException{  
            Security.addProvider(new com.sun.crypto.provider.SunJCE());  
            //实例化支持DES算法的密钥生成器(算法名称命名需按规定,否则抛出异常)   
            keygen = KeyGenerator.getInstance("AES");  
            //生成密钥   
            deskey = keygen.generateKey();  
            //生成Cipher对象,指定其支持的DES算法   
            c = Cipher.getInstance("AES");  
        }  
          
        /**  
         * 对字符串加密  
         *   
         * @param str  
         * @return  
         * @throws InvalidKeyException  
         * @throws IllegalBlockSizeException  
         * @throws BadPaddingException  
         */   
        public byte [] Encrytor(String str) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {  
            // 根据密钥,对Cipher对象进行初始化,ENCRYPT_MODE表示加密模式   
            c.init(Cipher.ENCRYPT_MODE, deskey);  
            byte [] src = str.getBytes();  
            // 加密,结果保存进cipherByte   
            cipherByte = c.doFinal(src);  
            return cipherByte;  
        }  
      
        /**  
         * 对字符串解密  
         *   
         * @param buff  
         * @return  
         * @throws InvalidKeyException  
         * @throws IllegalBlockSizeException  
         * @throws BadPaddingException  
         */   
        public byte [] Decryptor(byte [] buff) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {  
            // 根据密钥,对Cipher对象进行初始化,DECRYPT_MODE表示加密模式   
            c.init(Cipher.DECRYPT_MODE, deskey);  
            cipherByte = c.doFinal(buff);  
            return cipherByte;  
        }  
         
        public static String ByteToString(byte[] bytes) { 
        	StringBuilder strBuilder = new StringBuilder(); 
        	for (int i = 0; i <bytes.length ; i++) { 
        		if (bytes[i]!=0){ 
        			strBuilder.append((char)bytes[i]); 
        			}
        		else { 
        			break; 
        		} 
        	} 
        	return strBuilder.toString(); 
        }
        
        public static String encryptBase64(byte[] key) throws Exception {
            return (new BASE64Encoder()).encodeBuffer(key);
        }
        
        /**  
         * @param args  
         * @throws NoSuchPaddingException   
         * @throws NoSuchAlgorithmException   
         * @throws BadPaddingException   
         * @throws IllegalBlockSizeException   
         * @throws InvalidKeyException   
         */   
        public static void main(String[] args) throws Exception {  
            //获取开始时间的时间戳
        	long startTime = System.currentTimeMillis();    
            aes de1 = new aes();  
            //源码文件是 GBK 格式,或者这个字符串是从 GBK 文件中读取出来的, 转换为 string 变成 unicode 格式
            String msg1 = "In doing we learn."; 
            //利用 getBytes 将 unicode 字符串转成 UTF-8 格式的字节数组
            byte[] utf8Bytes = msg1.getBytes("UTF-8"); 
            //用 utf-8 对这个字节数组解码成新的字符串
            String msg = new String(utf8Bytes, "UTF-8");
            byte [] encData = de1.Encrytor(msg);  
            byte [] decontent = de1.Decryptor(encData);  
            String encontent = null;
            encontent = encryptBase64(encData);
            System.out.println("----AES 加密结果----"); 
            System.out.println(); 
            System.out.println("待加密的明文: " + msg);  
            //System.out.println("加密后:" + new String(encontent, "utf-8"));  
            System.out.printf("加密的结果为: " + encontent);  
            System.out.println("解密的结果为: " + new String(decontent));  
            //获取结束时间的时间戳 
            long endTime = System.currentTimeMillis();    
            //输出程序运行时间
        	System.out.println("AES 加密的时间消耗为:" + (endTime - startTime) + " ms");    
        }  
    }  
    

    ④ PBE

    package pbe;
     
    import sun.misc.BASE64Encoder;
     
    import javax.crypto.*;
    import javax.crypto.spec.PBEKeySpec;
    import javax.crypto.spec.PBEParameterSpec;
    import java.io.UnsupportedEncodingException;
    import java.security.InvalidAlgorithmParameterException;
    import java.security.InvalidKeyException;
    import java.security.Key;
    import java.security.NoSuchAlgorithmException;
    import java.security.spec.InvalidKeySpecException;
    import java.util.Random;
     
    public class pbe {
        /**
         * 定义加密方式
         * 支持以下任意一种算法
         * PBEWithMD5AndDES
         * PBEWithMD5AndTripleDES
         * PBEWithSHA1AndDESede
         * PBEWithSHA1AndRC2_40
         */
        private final static String KEY_PBE = "PBEWITHMD5andDES";
     
        private final static int SALT_COUNT = 100;
        /**
         * 初始化盐(salt)
         *
         * @return
         */
        public static byte[] init() {
            byte[] salt = new byte[8];
            Random random = new Random();
            random.nextBytes(salt);
            return salt;
        }
     
        /**
         * 转换密钥
         *
         * @param key 字符串
         * @return
         */
        public static Key stringToKey(String key) {
            SecretKey secretKey = null;
            try {
                PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray());
                SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_PBE);
                secretKey = factory.generateSecret(keySpec);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (InvalidKeySpecException e) {
                e.printStackTrace();
            }
            return secretKey;
        }
     
        /**
         * PBE 加密
         *
         * @param data 需要加密的字节数组
         * @param key  密钥
         * @param salt 盐
         * @return
         */
        public static byte[] encryptPBE(byte[] data, String key, byte[] salt) {
            byte[] bytes = null;
            try {
                // 获取密钥
                Key k = stringToKey(key);
                PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, SALT_COUNT);
                Cipher cipher = Cipher.getInstance(KEY_PBE);
                cipher.init(Cipher.ENCRYPT_MODE, k, parameterSpec);
                bytes = cipher.doFinal(data);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidAlgorithmParameterException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            }
            return bytes;
        }
     
        /**
         * PBE 解密
         *
         * @param data 需要解密的字节数组
         * @param key  密钥
         * @param salt 盐
         * @return
         */
        public static byte[] decryptPBE(byte[] data, String key, byte[] salt) {
            byte[] bytes = null;
            try {
                // 获取密钥
                Key k = stringToKey(key);
                PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, SALT_COUNT);
                Cipher cipher = Cipher.getInstance(KEY_PBE);
                cipher.init(Cipher.DECRYPT_MODE, k, parameterSpec);
                bytes = cipher.doFinal(data);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidAlgorithmParameterException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            }
            return bytes;
        }
     
        /**
         * BASE64 加密
         *
         * @param key 需要加密的字节数组
         * @return 字符串
         * @throws Exception
         */
        public static String encryptBase64(byte[] key) throws Exception {
            return (new BASE64Encoder()).encodeBuffer(key);
        }
     
        public static void main(String[] args) {
        	//获取开始时间的时间戳
        	long startTime = System.currentTimeMillis();   
        	// 加密前的原文
        	String msg = "In doing we learn." ;  
            // 口令
            String key = "qwert";
            // 初始化盐
            byte[] salt = init();
            // 采用PBE算法加密
            byte[] encData = encryptPBE(msg.getBytes(), key, salt);
            // 采用PBE算法解密
            byte[] decData = decryptPBE(encData, key, salt);
            String encontent = null;
            String decontent = null;
            try {
            	encontent = encryptBase64(encData);
            	decontent = new String(decData, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("----PBE 加密结果----"); 
            System.out.println(); 
            System.out.println("待加密的明文: " + msg);  
            System.out.printf("加密的结果为: " + encontent);  
            System.out.println("解密的结果为: " + decontent);  
            //获取结束时间的时间戳 
            long endTime = System.currentTimeMillis();    
            //输出程序运行时间
        	    System.out.println("PBE 加密的时间消耗为:" + (endTime - startTime) + " ms");    
        }
    }
    

    ⑤ CBC

    package cbc_aes;
    
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    
    import sun.misc.BASE64Decoder;
    import sun.misc.BASE64Encoder;
    
    /**
    * AES 是一种可逆加密算法,对用户的敏感信息加密处理
    * 对原始数据进行AES加密后,进行Base64编码转化;
    */
    public class cbc_aes {
    /*
    * 加密用的Key 用26个字母和数字组成
    * 使用AES-128-CBC加密模式,key需要为16位。
    */
        private static String sKey="1234567800123656";
        private static String ivParameter="1236567890123456";
        private static cbc_aes instance=null;
        //private static 
        private cbc_aes(){
    
        }
        public static cbc_aes getInstance(){
            if (instance==null)
                instance= new cbc_aes();
            return instance;
        }
        // 加密
        public String encrypt(String sSrc, String encodingFormat, String sKey, String ivParameter) throws Exception {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            byte[] raw = sKey.getBytes();
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());//使用CBC模式,需要一个向量iv,可增加加密算法的强度
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            byte[] encrypted = cipher.doFinal(sSrc.getBytes(encodingFormat));
            return new BASE64Encoder().encode(encrypted);//此处使用BASE64做转码。
    }
    
        // 解密
        public String decrypt(String sSrc, String encodingFormat, String sKey, String ivParameter) throws Exception {
            try {
                byte[] raw = sKey.getBytes("ASCII");
                SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
                cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
                byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);//先用base64解密
                byte[] original = cipher.doFinal(encrypted1);
                String originalString = new String(original,encodingFormat);
                return originalString;
            } catch (Exception ex) {
                return null;
            }
    }
    
        public static void main(String[] args) throws Exception {
        	//获取开始时间的时间戳
        	long startTime = System.currentTimeMillis();   
        	// 加密前的原文
            String msg = "In doing we learn.";
            // 加密
            String enString = cbc_aes.getInstance().encrypt(msg,"utf-8",sKey,ivParameter);
            // 解密
            String DeString = cbc_aes.getInstance().decrypt(enString,"utf-8",sKey,ivParameter);
            System.out.println("----CBC 加密结果----"); 
            System.out.println(); 
            System.out.println("待加密的明文: " + msg);  
            System.out.println("加密的结果为: " + enString);  
            System.out.println("解密的结果为: " + DeString);  
            //获取结束时间的时间戳 
            long endTime = System.currentTimeMillis();    
            //输出程序运行时间
        	System.out.println("CBC 加密的时间消耗为:" + (endTime - startTime) + " ms");    
        }
    }
    

    ⑥ IDEA

    package idea;
    
    import java.nio.charset.Charset;
    import java.security.NoSuchAlgorithmException;
    import java.security.Security;
    
    import javax.crypto.Cipher;
    import javax.crypto.KeyGenerator;
    import javax.crypto.NoSuchPaddingException;
    import javax.crypto.SecretKey;
    import javax.crypto.spec.SecretKeySpec;
    
    import sun.misc.BASE64Encoder;
    
    public class idea {
        public static final String ALGORITHM = "IDEA";
        public static final String CIPHER_ALGORITHM = "IDEA/ECB/ISO10126Padding";
    
        // 获取 IEDA Key
        public static byte[] getDesKey() throws NoSuchAlgorithmException, NoSuchPaddingException{
            try {  
                // 1、创建密钥生成器
                KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
                keyGenerator.init(128);
                // 2、产生密钥
                SecretKey secretKey = keyGenerator.generateKey();
                // 3、获取密钥
                byte[] key = secretKey.getEncoded();
                return key;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        // IDEA 解密
        public static byte[] encryptIdea(byte[] data, byte[] key) {
            try {
                SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
                // 加工作模式和填充方式
                Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
                cipher.init(Cipher.ENCRYPT_MODE, keySpec);
                byte[] rsData = cipher.doFinal(data);
                return rsData;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        public static byte[] decryptIdea(byte[] data, byte[] key) {
            try {
     SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
                // 加工作模式和填充方式
                Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
                cipher.init(Cipher.DECRYPT_MODE, keySpec);
                byte[] rsData = cipher.doFinal(data);
                return rsData;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        
        public static String encryptBase64(byte[] key) throws Exception {
            return (new BASE64Encoder()).encodeBuffer(key);
        }
    
        public static void main(String[] args) throws Exception {
        	//获取开始时间的时间戳
        	long startTime = System.currentTimeMillis();
        	String msg = "In doing we learn.";
            byte[] data = msg.getBytes(Charset.forName("UTF-8"));
            byte[] key = getDesKey();
            String encontent = null;
            byte[] encData = encryptIdea(data, key);
            encontent = encryptBase64(encData);
            byte[] decData = decryptIdea(encData, key);
            System.out.println("----IDEA 加密结果----"); 
            System.out.println(); 
            System.out.println("待加密的明文: " + msg);    
            System.out.printf("加密的结果为: " + encontent);  
            System.out.println("解密的结果为: " + new String(decData));  
            //获取结束时间的时间戳 
            long endTime = System.currentTimeMillis();    
            //输出程序运行时间
        	System.out.println("IDEA 加密的时间消耗为:" + (endTime - startTime) + " ms");    
        }
    }
    

    ⑦ RSA

    package rsa;
    
    import java.io.UnsupportedEncodingException;
    import  java.security.InvalidKeyException;  
    import  java.security.KeyPair;  
    import  java.security.KeyPairGenerator;  
    import  java.security.NoSuchAlgorithmException;  
    import  java.security.interfaces.RSAPrivateKey;  
    import  java.security.interfaces.RSAPublicKey;  
      
    import  javax.crypto.BadPaddingException;  
    import  javax.crypto.Cipher;  
    import  javax.crypto.IllegalBlockSizeException;  
    import  javax.crypto.NoSuchPaddingException;
    
    import sun.misc.BASE64Encoder;  
      
    public class rsa {    
        /**  
         * 加密  
         * @param publicKey  
         * @param srcBytes  
         * @return  
         * @throws NoSuchAlgorithmException  
         * @throws NoSuchPaddingException  
         * @throws InvalidKeyException  
         * @throws IllegalBlockSizeException  
         * @throws BadPaddingException  
         */   
        protected byte [] encrypt(RSAPublicKey publicKey, byte [] srcBytes) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{  
            if (publicKey != null ){  
                //Cipher负责完成加密或解密工作,基于RSA   
                Cipher cipher = Cipher.getInstance("RSA");  
                //根据公钥,对Cipher对象进行初始化   
                cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
                byte [] resultBytes = cipher.doFinal(srcBytes);  
                return resultBytes;  
            }  
            return null ;  
        }  
          
        /**  
         * 解密   
         * @param privateKey  
         * @param srcBytes  
         * @return  
         * @throws NoSuchAlgorithmException  
         * @throws NoSuchPaddingException  
         * @throws InvalidKeyException  
         * @throws IllegalBlockSizeException  
         * @throws BadPaddingException  
         */   
        protected byte [] decrypt(RSAPrivateKey privateKey, byte [] srcBytes) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{  
            if (privateKey != null ){  
                //Cipher负责完成加密或解密工作,基于RSA   
                Cipher cipher = Cipher.getInstance("RSA");  
                //根据公钥,对Cipher对象进行初始化   
                cipher.init(Cipher.DECRYPT_MODE, privateKey);  
                byte [] resultBytes = cipher.doFinal(srcBytes);  
                return resultBytes;  
            }  
            return null ;  
        }  
        public static String encryptBase64(byte[] key) throws Exception {
            return (new BASE64Encoder()).encodeBuffer(key);
        }
        
        /**  
         * @param args  
         * @throws Exception 
         */   
        public static void main(String[] args) throws Exception {  
        	//获取开始时间的时间戳
        	long startTime = System.currentTimeMillis();   
        	rsa rsa = new rsa();  
            String msg = "In doing we learn.";  
            //KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象   
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");  
            //初始化密钥对生成器,密钥大小为1024位   
            keyPairGen.initialize(1024);  
            //生成一个密钥对,保存在keyPair中   
            KeyPair keyPair = keyPairGen.generateKeyPair();  
            //得到私钥   
            RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();               
            //得到公钥   
            RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();   
            //用公钥加密   
            byte [] srcBytes = msg.getBytes();  
            byte [] resultBytes = rsa.encrypt(publicKey, srcBytes);  
            String encontent = null;
            encontent = encryptBase64(resultBytes);
            //用私钥解密   
            byte [] decBytes = rsa.decrypt(privateKey, resultBytes);  
            System.out.println("----RSA 加密结果----"); 
            System.out.println(); 
            System.out.println("待加密的明文: " + msg);  
            System.out.printf("加密的结果为: " + encontent);  
            System.out.println("解密的结果为: " + new String(decBytes, "utf-8"));  
            //获取结束时间的时间戳 
            long endTime = System.currentTimeMillis();    
            //输出程序运行时间
        	System.out.println("RSA 加密的时间消耗为:" + (endTime - startTime) + " ms");    
        }  
    }  
    

    ⑧ MD5

    package md5;
    
    import  java.io.UnsupportedEncodingException;
    import  java.security.MessageDigest;  
    import  java.security.NoSuchAlgorithmException;
    
    import sun.misc.BASE64Encoder;  
      
    public class md5 {  
    
    public byte [] eccrypt(String info) throws NoSuchAlgorithmException{  
            //根据MD5算法生成MessageDigest对象   
            MessageDigest md5 = MessageDigest.getInstance("MD5");  
            byte [] srcBytes = info.getBytes();  
            //使用srcBytes更新摘要   
            sha.update(srcBytes);  
            //完成哈希计算,得到result   
            byte [] resultBytes = md5.digest();  
            return resultBytes;  
        }  
    
    public static String encryptBase64(byte[] key) throws Exception {
            return (new BASE64Encoder()).encodeBuffer(key);
        }
       
        public static void main(String args[]) throws Exception{  
        	//获取开始时间的时间戳
        	long startTime = System.currentTimeMillis();   
        	String msg = "In doing we learn." ;  
            md5 md5 = new md5();  
            byte [] resultBytes = md5.eccrypt(msg);  
            String encontent = null;
            encontent = encryptBase64(resultBytes);
            System.out.println("----MD5 哈希结果----"); 
            System.out.println(); 
            System.out.println("待 Hash 的明文: " + msg);
            System.out.printf("Hash 的结果为:  " + encontent);  
            //获取结束时间的时间戳 
            long endTime = System.currentTimeMillis();    
            //输出程序运行时间
        	System.out.println("MD5 哈希的时间消耗为:" + (endTime - startTime) + " ms");       
    

    ⑨ SHA-1

    package sha1;
    
    import  java.io.UnsupportedEncodingException;
    import  java.security.MessageDigest;  
    import  java.security.NoSuchAlgorithmException;
    
    import sun.misc.BASE64Encoder;  
      
    public class sha1 {   
        public byte [] eccrypt(String info) throws NoSuchAlgorithmException{  
            //根据SHA1算法生成MessageDigest对象   
            MessageDigest sha = MessageDigest.getInstance("SHA-1");  
            byte [] srcBytes = info.getBytes();  
            //使用srcBytes更新摘要   
            sha.update(srcBytes);  
            //完成哈希计算,得到result   
            byte [] resultBytes = sha.digest();  
            return resultBytes;  
        }  
          
        public static String encryptBase64(byte[] key) throws Exception {
            return (new BASE64Encoder()).encodeBuffer(key);
        }
          
        public static void main(String args[]) throws Exception{  
        	//获取开始时间的时间戳
        	long startTime = System.currentTimeMillis();   
        	String msg = "In doing we learn." ;  
            sha1 sha = new sha1();  
            byte [] resultBytes = sha.eccrypt(msg);  
            String encontent = null;
            encontent = encryptBase64(resultBytes);
            System.out.println("----SHA1 哈希结果----"); 
            System.out.println(); 
            System.out.println("待 Hash 的明文: " + msg);
            System.out.printf("Hash 的结果为:  " + encontent);  
            //获取结束时间的时间戳 
            long endTime = System.currentTimeMillis();    
            //输出程序运行时间
        	System.out.println("SHA1 哈希的时间消耗为:" + (endTime - startTime) + " ms");         
        }
    }  
    

    ⑩ SHA-256

    package sha256;
    
    import  java.io.UnsupportedEncodingException;
    import  java.security.MessageDigest;  
    import  java.security.NoSuchAlgorithmException;
    import sun.misc.BASE64Encoder;  
      
    public class sha256 {     
        public byte [] eccrypt(String info) throws NoSuchAlgorithmException{  
            //根据SHA256算法生成MessageDigest对象   
            MessageDigest sha = MessageDigest.getInstance("SHA-256");  
            byte [] srcBytes = info.getBytes();  
            //使用srcBytes更新摘要   
            sha.update(srcBytes);  
            //完成哈希计算,得到result   
            byte [] resultBytes = sha.digest();  
            return resultBytes;  
        }  
          
        public static String encryptBase64(byte[] key) throws Exception {
            return (new BASE64Encoder()).encodeBuffer(key);
        }
         
        public static void main(String args[]) throws Exception{  
        	//获取开始时间的时间戳
        	long startTime = System.currentTimeMillis();   
        	String msg = "In doing we learn." ;  
            sha256 sha = new sha256();  
            byte [] resultBytes = sha.eccrypt(msg);  
            String encontent = null;
            encontent = encryptBase64(resultBytes);
            System.out.println("----SHA256 哈希结果----"); 
            System.out.println(); 
            System.out.println("待 Hash 的明文: " + msg);
            System.out.printf("Hash 的结果为:  " + encontent);  
            //获取结束时间的时间戳 
            long endTime = System.currentTimeMillis();    
            //输出程序运行时间
        	System.out.println("SHA256 哈希的时间消耗为:" + (endTime - startTime) + " ms");    
        } 
    }  
    

    ⑪ Caesar

    package caesar;
    
    /**
     * 一般化的凯撒加密算法为: C = E(k, p) = (p + k) mod 26
     * 一般化的凯撒解密算法为: p = D(k, C) = (C - k) mod 26   
     */ 
     
    public class caesar {  
        /**
         * 对单个字母进行加密
         * @param ch 字母
         * @param n 密钥
         * @return 加密后的字母
         */
        public static char encrypt(char ch,int n){
            int unicode;
            int c=ch-'a';
            if(c+n>'z') unicode=c+n-26;
            else unicode=c+n;
            return (char)(unicode%26+'a');
        }
        /**
         * 对明文进行加密
         * @param str 明文字符串
         * @param n 密钥
         * @return  对明文加密后的密文
         */
        public static String encrypt(String str,int n){
            char[] ch=str.toCharArray();
            StringBuilder sb=new StringBuilder();
            for(char c: ch){
                sb.append(encrypt(c,n));
            }
            return sb.toString();
        }  
        /**
         * 将加密后的字母解密
         * @param ch 加密后的字母
         * @param n 密钥
         * @return 解密后的字母
         */
        public static char decrypt(char ch,int n){
            int unicode;
             int c=ch-'a';
            if(c-n<'a') unicode=c-n+26;
            else unicode=c-n;
            return (char)(unicode%26+'a');
        }
        /**
         * 将密文解密
         * @param str 密文
         * @param n 密钥
         * @return 解密后的明文
         */
        public static String decrypt(String str,int n){
            char[] ch=str.toCharArray();
            StringBuilder sb=new StringBuilder();
            for(char c: ch){
                sb.append(decrypt(c,n));
            }
            return sb.toString();
        }
    
        public static void main(String args[]){
        	//获取开始时间的时间戳
        	long startTime = System.currentTimeMillis();
            String str="in doing we learn";
            String[] words=str.toLowerCase().split(" ");
            String result = "";
            for(String word: words){
            	result = result + encrypt(word, 21) + " ";
            }
            System.out.println("----Caesar 加密结果----"); 
            System.out.println(); 
            System.out.println("待加密的明文: " + str);    
            System.out.print("加密的结果为: "); 
            System.out.print(result);
            System.out.println();
            String[] ws=result.split(" ");
            System.out.print("解密的结果为: ");
            for(String w: ws){
                System.out.print(decrypt(w, 21)+" ");
            }
            //获取结束时间的时间戳 
            long endTime = System.currentTimeMillis();    
            //输出程序运行时间
            System.out.println();
            System.out.println("Caesar 加密的时间消耗为:" + (endTime - startTime) + " ms");    
            }
        }    
    
    展开全文
  • 一致性Hash算法背景 一致性哈希算法在1997年由麻省理工学院Karger等人在解决分布式Cache中提出,设计目标是为了解决因特网中热点(Hot spot)问题,初衷和CARP十分类似。一致性哈希修正了CARP使用简单哈希...
  • 什么是哈希算法?哈希是一种加密算法,也称为散列函数或杂凑函数。哈希函数是一个公开函数,可以将任意长度消息M映射成为一个长度较短且长度固定值H(M),称H(M)为哈希值、散列值(Hash Value)、杂凑值或者...
  • 一致性哈希算法核心思想,就是通过构造一个长度为2^32整数环,这个环也被称为一致性Hash环(只是逻辑上环),将缓存服务器节点名称哈希值均匀分布在[0,2^32-1]Hash环上,然后根据需要缓存Key值计算得到其...
  • 点击上方蓝色字体,选择“关注”优质文章,第...而一致性哈希算法中,当节点个数变动时,映射关系失效对象非常少,迁移成本也非常小。1 概述 1.1 传统哈希(硬哈希)分布式系统中,假设有 n 个节点,传统方案使用 mo...
  • 1.1 传统哈希(硬哈希) 分布式系统中,假设有 n 个节点,传统方案使用 mod(key, n) 映射数据和节点。 当扩容或缩容时(哪怕只是增减1个节点),映射关系变为 mod(key, n+1) / mod(key, n-1),绝大多数数据映射关系...
  • 一致性哈希算法的原理分析 环形Hash空间 按照常用的hash算法来将对应的key哈希到一个具有2^32次方个桶的空间中,即0~(2^32)-1的数字空间中。现在我们可以将这些数字头尾相连,想象成一个闭合的环形。如下图 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,999
精华内容 1,199
关键字:

哈希算法的原理