• java加密算法入门(一)-算法概念及单向加密


    说起加密,我的第一印象就是电视剧各种密码本破解解密的场景,这两天在看加密相关的东西,做下笔记以便以后查看,也提供给大家个参考。

    本文是java加密的第一篇,主要讲述下消息编码Base64以及简单的消息摘要算法MD5,SHA,MAC等,如果有不对的地方还望大家指正。

    1、算法概念简述

    1.1、加密算法分类

    消息编码:Base64

    消息摘要:MD类,SHA类,MAC

    对称加密:DES,3DES,AES

    非对称加密:RSA,DH密钥交换

    数字签名:RSA signature,DSA signature

    1.2、算法的主要流程

    明文-->加密算法--> 密文 --> 解密算法 --> 明文

    1.3、常用的jar包

    1、jdk自身提供的加密类

    2、其他提供加密的第三方jar包
        Apache Commons Codec(简称CC),
        Bouncy Castle(BC)

            <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
            <dependency>
                <groupId>org.bouncycastle</groupId>
                <artifactId>bcprov-jdk15on</artifactId>
                <version>1.57</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
            <dependency>
                <groupId>commons-codec</groupId>
                <artifactId>commons-codec</artifactId>
                <version>1.10</version>
            </dependency>

    2、消息摘要算法

    消息摘要算法,可谓是非可逆加密,就是不可解密的加密方法也称单向加密算法。当然现在网上也有大神将一些算法破解解密的例子,这只是特例。 

    2.1、使用BASE64编码格式

    为什么是使用base64编码呢,因为严格地说,Base64属于编码格式,而非加密算法。我在网上有看到一个base64产生的背景,看完这个相信大家就会理解我说的这句话了。

    Base64算法最早主要是解决电子邮件的传输问题,由于当时的网关有个问题就是可能会使非ASCII码字符的二进制位做调整,导致用户收取的邮件变成乱码,所以就出现了Base64算法。

    什么是base64算法?

    按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。
    常见于邮件、http加密,截取http信息,你就会发现登录操作的用户名、密码字段通过BASE64加密的。 

    算法流程:

    明文-->Base64加密--> 密文 --> Base64解密 --> 明文

    代码实现:

    base64目前主要是通过jdk自带的类实现,也有BC和CC等第三方的解决方案,根据我的了解,BC是对jdk的补充,CC主要是对jdk操作的简化,大家可以在后面的代码中看到的。

    package checkcode;
    
    import org.apache.commons.codec.binary.Base64;
    
    import sun.misc.BASE64Decoder;
    import sun.misc.BASE64Encoder;
    /**
     * {@link http://www.cnblogs.com/allanzhang/}
     * @author 小卖铺的老爷爷
     *
     */
    public class Base64Test 
    {
        public static final String src = "laoyeye base64";
        public static void main(String[] args) 
        {
            jdkBase64();
            commonsCodesBase64();
            bouncyCastleBase64();
        } 
        // 用jdk实现
        public static void jdkBase64()
        {
            try 
            {
                BASE64Encoder encoder = new BASE64Encoder();
                String encode = encoder.encode(src.getBytes());
                System.out.println("encode:" + encode);
          
                BASE64Decoder decoder = new BASE64Decoder();
                System.out.println("decode:" + new String(decoder.decodeBuffer(encode)));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        
        // 用Apache的common codes实现
        public static void commonsCodesBase64()
        {
            byte[] encodeBytes = Base64.encodeBase64(src.getBytes());
            System.out.println("common codes encode:" + new String(encodeBytes));
            
            byte[] dencodeBytes = Base64.decodeBase64(encodeBytes);
            System.out.println("common codes decode:" + new String(dencodeBytes));
        }
        
    
        // 用bouncy castle实现
        public static void bouncyCastleBase64()
        {
            byte[] encodeBytes = org.bouncycastle.util.encoders.Base64.encode(src.getBytes());
            System.out.println("bouncy castle encode:" + new String(encodeBytes));
            
            byte[] dencodeBytes = org.bouncycastle.util.encoders.Base64.decode(encodeBytes);
            System.out.println("bouncy castle decode:" + new String(dencodeBytes));
            
        }
    
    }

    效果图:

    注:BASE加密后产生的字节位数是8的倍数,如果不够位数以=符号填充。

    2.2 MD(Message Digest algorithm,信息摘要算法)

    MD算法目前使用最多的大概就是MD5了吧,MD5的全称是Message-Digest Algorithm 5(信息-摘要算法),在90年代初由MIT Laboratory for Computer Science和RSA Data Security Inc的Ronald L. Rivest开发出来,经MD2和MD4等发展而来。目前jdk提供了MD2和MD5的实现,对于MD4我们需要借助BC来实现。现在MD广泛用于加密和解密技术,常用于文件校验。不管文件多大,经过MD后都能生成唯一的MD值。好比现在的ISO校验,都是MD校验。怎么用?当然是把ISO经过MD后产生MD的值。一般下载linux-ISO的朋友都见过下载链接旁边放着MD的串。就是用来验证文件是否一致的。

    加密流程:

    明文-->MD加密--> 密文--> 接收者

    明文-->接收者--> MD加密--> 密文--> 比较验证

    代码实现:

    package checkcode;
    
    import java.security.MessageDigest;
    import java.security.Security;
    
    import org.apache.commons.codec.binary.Hex;
    import org.apache.commons.codec.digest.DigestUtils;
    import org.bouncycastle.crypto.digests.MD4Digest;
    import org.bouncycastle.crypto.digests.MD5Digest;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    /**
     * {@link http://www.cnblogs.com/allanzhang/}
     * @author 小卖铺的老爷爷
     *
     */
    public class MD5Test {
    
        public static final String src = "laoyeye md5";
        public static void main(String[] args) 
        {
            jdkMD5();
            jdkMD2();
            
            bcMD4();
            bcMD5();
            
            bc2jdkMD4();
            
            ccMD5();
            ccMD2();
    
        }
        
        // 用jdk实现:MD5
        public static void jdkMD5()
        {
            try 
            {
                MessageDigest md = MessageDigest.getInstance("MD5");
                byte[] md5Bytes = md.digest(src.getBytes());
                System.out.println("JDK MD5:" + Hex.encodeHexString(md5Bytes));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        // 用jdk实现:MD2
        public static void jdkMD2()
        {
            try 
            {
                MessageDigest md = MessageDigest.getInstance("MD2");
                byte[] md2Bytes = md.digest(src.getBytes());
                System.out.println("JDK MD2:" + Hex.encodeHexString(md2Bytes));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        
        // 用bouncy castle实现:MD5
        public static void bcMD5()
        {
            MD5Digest digest = new MD5Digest();
            digest.update(src.getBytes(),0,src.getBytes().length);
            byte[] md5Bytes = new byte[digest.getDigestSize()];
            digest.doFinal(md5Bytes, 0);
            System.out.println("bouncy castle MD5:" + Hex.encodeHexString(md5Bytes));
            
        }
        
        // 用bouncy castle实现:MD4
        public static void bcMD4()
        {
            MD4Digest digest = new MD4Digest();
            digest.update(src.getBytes(),0,src.getBytes().length);
            byte[] md4Bytes = new byte[digest.getDigestSize()];
            digest.doFinal(md4Bytes, 0);
            System.out.println("bouncy castle MD4:" + Hex.encodeHexString(md4Bytes));
        }
        
        // 用bouncy castle与jdk结合实现:MD4
        public static void bc2jdkMD4()
        {
            try 
            {
                Security.addProvider(new BouncyCastleProvider());
                MessageDigest md = MessageDigest.getInstance("MD4");
                byte[] md4Bytes = md.digest(src.getBytes());
                System.out.println("bc and JDK MD4:" + Hex.encodeHexString(md4Bytes));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }  
        // 用common codes实现实现:MD5
        public static void ccMD5()
        {
            System.out.println("common codes MD5:" + DigestUtils.md5Hex(src.getBytes()));
        }    
        // 用common codes实现实现:MD2
        public static void ccMD2()
        {
            System.out.println("common codes MD2:" + DigestUtils.md2Hex(src.getBytes()));
        }
    
    }

    2.3  SHA(Secure Hash Algorithm,安全散列算法)

    SHA现在主要有SHA1和SHA2两大类,SH2又分好多种,大家可以网上看下。SHA,被广泛地应用于电子商务等信息安全领域。虽然,SHA与MD5通过碰撞法都被破解了, 但是SHA仍然是公认的安全加密算法,较之MD5更为安全。 

    加密流程:

    明文-->SHA加密--> 密文--> 接收者

    明文-->接收者--> SHA加密--> 密文--> 比较验证

     代码实现:

    package checkcode;
    
    
    
    import java.math.BigInteger;
    import java.security.MessageDigest;
    import java.security.Security;
    
    import org.apache.commons.codec.binary.Hex;
    import org.apache.commons.codec.digest.DigestUtils;
    import org.bouncycastle.crypto.Digest;
    import org.bouncycastle.crypto.digests.SHA1Digest;
    import org.bouncycastle.crypto.digests.SHA224Digest;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    /**
     * {@link http://www.cnblogs.com/allanzhang/}
     * @author 小卖铺的老爷爷
     *
     */
    public class SHATest {
        public static final String src = "laoyeye sha";
        public static void main(String[] args) 
        {
            jdkSHA1();
            bcSHA1();
            bcSHA224();
            bcSHA224b();
            generateSha256();
            ccSHA1();
    
        }
        
        // 用jdk实现:SHA1
        public static void jdkSHA1()
        {
            try 
            {
                MessageDigest md = MessageDigest.getInstance("SHA");
                md.update(src.getBytes());
                System.out.println("jdk sha-1:" + Hex.encodeHexString(md.digest()));
                
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        // 用bouncy castle实现:SHA1 
        public static void bcSHA1()
        {
            
            Digest digest = new SHA1Digest();
            digest.update(src.getBytes(), 0, src.getBytes().length );
            byte[] sha1Bytes = new byte[digest.getDigestSize()];
            digest.doFinal(sha1Bytes, 0);
            System.out.println("bc sha-1:" + org.bouncycastle.util.encoders.Hex.toHexString(sha1Bytes));        
        }
        
    
        // 用bouncy castle实现:SHA224 
        public static void bcSHA224()
        {
            
            Digest digest = new SHA224Digest();
            digest.update(src.getBytes(), 0, src.getBytes().length );
            byte[] sha224Bytes = new byte[digest.getDigestSize()];
            digest.doFinal(sha224Bytes, 0);
            System.out.println("bc sha-224:" + org.bouncycastle.util.encoders.Hex.toHexString(sha224Bytes));        
        }
        
        // 用bouncy castle与jdk结合实现:SHA224 
        public static void bcSHA224b()
        {
            
            try 
            {
                Security.addProvider(new BouncyCastleProvider());
                MessageDigest md = MessageDigest.getInstance("SHA224");
                md.update(src.getBytes());
                System.out.println("bc and JDK sha-224:" + Hex.encodeHexString(md.digest()));
                
            } catch (Exception e) {
                e.printStackTrace();
            }        
        }
        public static void generateSha256() {
            MessageDigest md = null;
            try {
                md = MessageDigest.getInstance("SHA-256");
                md.update(src.getBytes("UTF-8")); // Change this to "UTF-16" if needed
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            byte[] digest = md.digest();
            BigInteger bigInt = new BigInteger(1, digest);
            System.out.println("Sha256 hash: " + bigInt.toString(16));
        }
        
        // 用common codes实现实现:SHA1
        public static void ccSHA1()
        {
            System.out.println("common codes SHA1 - 1 :" + DigestUtils.sha1Hex(src.getBytes()));
            System.out.println("common codes SHA1 - 2 :" + DigestUtils.sha1Hex(src));
        }
        
        
    
    }

    效果图:

    SHA与MD的比较

    因为二者均由MD导出,SHA和MD彼此很相似。相应的,他们的强度和其他特性也是相似,但还有以下几点不同:

    对强行攻击的安全性:最显著和最重要的区别是SHA-摘要比MD摘要长。使用强行技术,产生任何一个报文使其摘要等于给定报摘要的难度对MD是2^128数量级的操作,而对SHA则是2^160数量级的操作。这样,SHA对强行攻击有更大的强度。

    对密码分析的安全性:由于MD的设计,易受密码分析的攻击,SHA显得不易受这样的攻击。

    速度:在相同的硬件上,SHA的运行速度比MD慢。

    2.4  HMAC(Hash Message Authentication Code,散列消息鉴别码)

    基于密钥的Hash算法的认证协议。消息鉴别码实现鉴别的原理是,用公开函数和密钥产生一个固定长度的值作为认证标识,用这个 标识鉴别消息的完整性。使用一个密钥生成一个固定大小的小数据块,即MAC,并将其加入到消息中,然后传输。接收方利用与发送方共享的密钥进行鉴别认证等。

    加密流程:

    构建秘钥-->发送秘钥--> 接收者

    明文-->HMAC算法+秘钥加密--> 密文--> 接收者

    明文-->接收者--> HMAC算法+秘钥加密--> 密文--> 比较验证

    代码实现:

    package checkcode;
    
    import javax.crypto.KeyGenerator;
    import javax.crypto.Mac;
    import javax.crypto.SecretKey;
    import javax.crypto.spec.SecretKeySpec;
    
    import org.apache.commons.codec.binary.Hex;
    import org.bouncycastle.crypto.digests.MD5Digest;
    import org.bouncycastle.crypto.macs.HMac;
    import org.bouncycastle.crypto.params.KeyParameter;
    /**
     * {@link http://www.cnblogs.com/allanzhang/}
     * @author 小卖铺的老爷爷
     *
     */
    public class HMACTest {
        public static final String src = "laoyeye hmac";
    
        public static void main(String[] args) 
        {
            jdkHmacMD5();
            bcHmacMD5();
    
        }
        
        // 用jdk实现:
        public static void jdkHmacMD5()
        {
            try 
            {
                // 初始化KeyGenerator
                KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5");
                // 产生密钥
                SecretKey secretKey = keyGenerator.generateKey();
                //1、 获取密钥
    //            byte[] key = secretKey.getEncoded();
                //2、使用固定秘钥
                byte[] key = Hex.decodeHex(new char[]{'1','2','3','4','5','6','7','8','9','a','b','c','d','e' });
                
                // 还原密钥
                SecretKey restoreSecretKey = new SecretKeySpec(key, "HmacMD5");
                // 实例化MAC
                Mac mac = Mac.getInstance(restoreSecretKey.getAlgorithm());
                // 初始化MAC
                mac.init(restoreSecretKey);
                // 执行摘要
                byte[] hmacMD5Bytes = mac.doFinal(src.getBytes());
                System.out.println("jdk hmacMD5:" + Hex.encodeHexString(hmacMD5Bytes));
                
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        // 用bouncy castle实现:
        public static void bcHmacMD5()
        {
            HMac hmac = new HMac(new MD5Digest());
            // 必须是16进制的字符,长度必须是2的倍数
            hmac.init(new KeyParameter(org.bouncycastle.util.encoders.Hex.decode("123456789abcde")));
            hmac.update(src.getBytes(), 0, src.getBytes().length);
            
            // 执行摘要
            byte[] hmacMD5Bytes = new byte[hmac.getMacSize()];
            hmac.doFinal(hmacMD5Bytes, 0);
            System.out.println("bc hmacMD5:" + org.bouncycastle.util.encoders.Hex.toHexString(hmacMD5Bytes));
            
        }
    
    }

    注:还原秘钥是将jdk初始化的密钥转换为符合特定算法规则的密钥,只要有密钥的算法,在加密步骤之前都需要进行还原密钥操作,来作为加密与解密操作时的参数.

    效果图:

    总结:

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

    源码地址:https://git.oschina.net/allanzhang/checkcode.git
  • 相关阅读:
    cnpm镜像安装
    wxParse解析html
    C++回调函数
    QT源码分析:QTcpServer
    QT实现支持加密的Sqlite数据库引擎
    VS2013+OpenCV3.4.2编译
    Android Tcp操作
    使用Delphi开发linux应用
    QT5.10+MinGW+OpenCV3.4.2编译
    C++ ActiveX开发的问题讨论
  • 原文地址:https://www.cnblogs.com/laoyeye/p/6984747.html
Copyright © 2020-2023  润新知