浅谈基本加密算法以及使用

1.加密算法

(1)对称加密算法
对称加密算法中,加密用的密钥和解密用的密钥是一样的,也就是说,加密和解密使用同一个密钥,密钥的保存和安全交换是一个问题。对称加密算法有DES(data encryption standard)数据加密标准,3DES(DESede),AES(Advanced encryption standard)高级加密标准。
(2)单向加密算法
MD4,MD5,SHA192,SHA256,SHA512单向的不可逆的摘要算法,用于验证数据的完整性。摘要算法的输出是固定的,192,256指的都是输出摘要的长度,例如MD5摘要算法就是把一个明文摘要成一个长16字节的结果,只要原文有一点变化,那么摘要结果就会完全不同(雪崩效应),不能从摘要结果去推到原文。比如Linux系统中的账号密码文件就是摘要以后的数据存放进去的,以确保安全。为了更好的提高安全性,也可以使用加盐(在明文中添加某些信息,混乱明文以后再进行摘要)的方法。目前MD5,SHA1都被淘汰了,他们的安全性不足,推荐使用SHA256以及以上的算法。
(3)非对称加密
在非对称加密算法中,加密使用的密钥和解密使用的密钥是不同的。非对称加密生成一个公钥和一个私钥,加密与解密只能在一对私钥和公钥之间进行,公钥加密的私钥解密。RSA,DSA是两个非对称加密算法,用于数字签名和加密,其中DSA只能用于签名

2.RSA算法介绍

RSA密码体制是一种公钥密码体制,公钥公开,私钥保密,它的加密解密算法是公开的。由公钥加密的内容可以,并且只能由私钥解密,并且由私钥加密的内容可以并且只能由公钥进行解密。由此可以说,RSA的这一对公钥,私钥都可以用来进行加密和解密,并且一方加密的内容只能由对方进行解密,客户端用公钥加密的数据,之能是认证的服务端使用相应私钥解密。这也是这个算法能用于身份识别(数字签名)的原因。
为了实现身份认证,RSA体系中的私钥加密可以实现身份验证,验证过程是用私钥加密,传给对方,对方用之前获得的公开的公钥解密,只要能顺利的解开,就能验证对方的身份是合法的。
要谈RSA与完整性的关系。首先要说明什么是完整性。完整性就是保证传递的信息是通信双方原本想传递的内容,没有被破坏和篡改。签名保证了完整性。
这里介绍一下签名,签名就是在信息的后面再加上一段内容,可以证明信息没有被修改过。要想实现该目的,一般是对信息做一个散列计算得到一个hash值,这个过程是不可逆的(使用摘要算法),也就是说,无法通过hash值来推导出原来的明文。把这个hash值加密后作为一个签名与信息一同发出,接收方再收到以后,会重新计算信息的hash值,并和信息所附带的hash值(解密后)进行对比,如果一致,则说明信息的内容没有被修改过,因为hash计算可以保证不同的内容一定会得到不同的hash值,所以只要内容一旦被篡改,根据信息内容计算出来的hash值就会改变。
如果不加密的话,攻击者可以修改信息内容的同时也修改hash值,从而让它们可以相匹配,为防止这种情况,hash值一般都会加密后(也就是签名)再和信息一起发送,以保证这个hash值不被修改。
使用RSA来处理这个签名,用公钥加密“明文”+“哈希值”,私钥解密得到明文。当然,简单的对称加密也可以实现完整性的验证,但是不能实现身份认证。
非对称加密效率是很低的,比对称加密低出3个数量级左右。

3.解决对称加密和非对称加密缺陷的一种数据传输方案

混用对称加密与非对称加密,数据加密时使用对称加密算法,再把对称加密算法的密钥和完整性校验值(hash值),身份认证标识,一起使用RSA密钥加密,对方接受到之后先使用RSA密钥解密,得到对称密钥,完整性校验值和身份认证标识,看到对方认证没问题,再使用对称密钥解密密文,并验证完整性。大致流程图如下:
浅谈基本加密算法以及使用_第1张图片

4.使用Java中的摘要算法库对字符串和文件进行摘要测试

public class DigestUtils {

    //这种做法是线程不安全的
    private static Map<String,MessageDigest> map = new HashMap<String ,MessageDigest>();

    public static MessageDigest getMDInstance (String algorithm) {

        try {
            if(map.get(algorithm) == null){
                MessageDigest md = MessageDigest.getInstance(algorithm);
                map.put(algorithm, md);
            }

            return map.get(algorithm);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }


    }
    /**
     * 通过摘要算法将所传入字符串加密,目前MD5和SHA1已经不安全了。而实际中SHA1加密强度是61位
     * @param str 要加密的字符串
     * @param list 可选摘要算法的列表,包括MD5.SHA1,SHA256,SHA512等算法
     * @return
     */
    public static byte[] encryptByDigest(String str,DigestAlgortihmList list){
        if(str == null || str.length() == 0){
            return null;
        }
        MessageDigest md;
        try{
            //这样做是线程安全的
            md = MessageDigest.getInstance(list.getName());
            md.update(str.getBytes("utf-8"));
            return md.digest();
        }
        catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将字节数组转换为十六进制字符串
     * @param array
     * @return
     */
    public static String parseToHex(byte[] array){
        if(array == null || array.length == 0){
            return null;
        }

        StringBuffer sb = new StringBuffer();
        for(Byte b : array){
            //System.out.println("b:" + b);
            //System.out.println(Integer.toHexString(b));
            /**
             * 在此对这段代码进行解释:b是一个8位的二进制串,以十进制显示。比如说b是1010 1011
             * 那么将b右移四位之后变为0000 1010 然后和0xf进行与操作,为0000 1010 这是s1
             */
            //高四位转变为16进制
            String s1 = Integer.toHexString((b >> 4) & 0xf);
            //System.out.println("s1:" + s1);
            //低四位转变为16进制
            String s2 = Integer.toHexString(b & 0xf);
            //System.out.println("s2:" + s2);
            sb.append(s1).append(s2);
        }
        return sb.toString();
    }

    /**
     * 对文件进行摘要,将文件读为字节流之后进行摘要
     * @param path 文件路径
     * @param list 选择摘要算法
     * @return
     */
    public static String digestOneFile(String path,DigestAlgortihmList list){
        File file = new File(path);
        FileInputStream fin;
        try {
            fin = new FileInputStream(file);
            FileChannel ch = fin.getChannel();  //文件通道
            MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY,0,file.length());//字节缓冲流
            MessageDigest md = MessageDigest.getInstance(list.getName());
            md.update(byteBuffer);
            byte[] result = md.digest();
            return parseToHex(result);
        }
        catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

public class TestDigest {
    public static void main(String[] args) throws NoSuchAlgorithmException, CloneNotSupportedException {
        String temp = "178458976218971Hello world你好啊";
        byte[] arr = DigestUtils.encryptByDigest(temp,DigestAlgortihmList.SHA1);
        String result = DigestUtils.parseToHex(arr);
        System.out.println("一段字符串使用SHA1摘要后的结果:" + result);


        //测试线程安全性
//        for(int i=0;i < 100;i ++){
//            Thread thread = new Thread(new Runnable() {
//                @Override
//                public void run() {
//                    String temp = "178458976218971Hello world你好啊";
//                    byte[] arr = DigestUtils.encryptByDigest(temp,DigestAlgortihmList.SHA1);
//                    String result = DigestUtils.parseToHex(arr);
//                    System.out.println(result);
//                }
//            });
//            thread.start();
//        }
        String path = "D:\\apache-tomcat-7.0.52.zip";
        String fileDigestResult = DigestUtils.digestOneFile(path,DigestAlgortihmList.SHA1);
        System.out.println("文件使用SHA1摘要后的结果:" + fileDigestResult);

        MessageDigest md = MessageDigest.getInstance(DigestAlgortihmList.SHA1.getName());
        md.update("178458976218971".getBytes());
        MessageDigest md1 = (MessageDigest)md.clone();  //浅克隆md
        byte[] arr1 = md1.digest();

        md.update("Hello world".getBytes());
        MessageDigest md2 = (MessageDigest)md.clone();
        byte[] arr2 = md2.digest();

        md.update("你好啊".getBytes());
        MessageDigest md3 = (MessageDigest)md.clone();
        byte[] arr3 = md.digest();  //最后调用md复位

        //会发现这样做完之后是对“178458976218971Hello world你好啊”的累加 与直接进行该字符串累加是一样的
        System.out.println("累加摘要的测试");
        System.out.println(DigestUtils.parseToHex(arr1));
        System.out.println(DigestUtils.parseToHex(arr2));
        System.out.println(DigestUtils.parseToHex(arr3));
    }
}

浅谈基本加密算法以及使用_第2张图片

5.使用Java中的对称加密算法库对字符串进行加密测试

AES算法测试:

public static byte[] encryptByAES128(String str,String random_str) {
        if (str==null || str.length()==0 || random_str==null || random_str.length()==0) {
            return null;
        }
        KeyGenerator kgen;
        try {
            //构建一个密钥生成器
            kgen = KeyGenerator.getInstance("aes");
            //初始化,将随机串放入,初始化为128位
            kgen.init(128, new SecureRandom(random_str.getBytes()));
            SecretKey secretKey = kgen.generateKey();

            System.out.println(secretKey.getEncoded().length);
            //获取原始密钥字节数组,new出新的安全密钥
            SecretKeySpec key = new SecretKeySpec(secretKey.getEncoded(), "AES");

            //创建加密器 指定加密算法  默认模式ECB,有填充 , AES/ECB/PKCS5Padding (128)
            //也可以选择 AES/ECB/NoPadding 算法是AES 模式是ECB 无填充
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            System.out.println(cipher.getBlockSize());

            //使用CBC模式需要构造一个初始向量
            String str_iv = "8144660571234567";
            IvParameterSpec iv = new IvParameterSpec(str_iv.getBytes("UTF-8"));

            //初始化加密器 选定模式,并将之前准备好的密钥放进去,如果选择了CBC模式需要添加进去初始向量
            cipher.init(Cipher.ENCRYPT_MODE, key,iv);
            // xxxx xxxx xxxx xxxx xxxW -> key -->  XX XXXXXXX XX X
            // xxxx^IV -key -> ^xxxx -key->^xxxx -key->  WW WW WWWWW WW
            // 0000    0001          0002    0003    0004
            //         c1(xor v)      c2       c3    c4
            //         enc1          enc2    enc3   enc4
            //  String str_iv = "14725836abcdefgx";
            //   IvParameterSpec iv = new IvParameterSpec(str_iv.getBytes("UTF-8"));
            //    cipher.init(Cipher.ENCRYPT_MODE, key,iv);
            byte[] byteContent = str.getBytes("utf-8");
            return cipher.doFinal(byteContent);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static byte[] dencryptByAES128(byte[] arr,String random_str) {
        if (arr==null || arr.length==0 || random_str==null || random_str.length()==0) {
            return null;
        }
        KeyGenerator kgen;
        try {
            //构建一个密钥生成器
            kgen = KeyGenerator.getInstance("AES");
            //初始化,将随机串放入
            kgen.init(128, new SecureRandom(random_str.getBytes()));
            SecretKey secretKey = kgen.generateKey();
            //获取原始密钥字节数组,new出新的安全密钥
            SecretKeySpec key = new SecretKeySpec(secretKey.getEncoded(), "AES");
            //创建加密器 指定加密算法
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            //初始化加密器 选定模式,并将之前准备好的密钥放进去
            //cipher.init(Cipher.DECRYPT_MODE, key);

            //使用CBC模式需要构造一个初始向量
            String str_iv = "8144660571234567";
            IvParameterSpec iv = new IvParameterSpec(str_iv.getBytes("UTF-8"));
            cipher.init(Cipher.DECRYPT_MODE, key,iv);
            return cipher.doFinal(arr);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
}	
public void testAES() throws UnsupportedEncodingException {
	String str = "123456789Hello world你好";
	String random_str = "echored112240xqezf";
	byte[] result = AESUtils.encryptByAES128(str,random_str);
	System.out.println(HexParseUtils.parseToHex(result));
	String s1 = HexParseUtils.parseToHex(result);
	result = HexParseUtils.parseTobyteArray(s1);
	byte[] result2 = AESUtils.dencryptByAES128(result,random_str);
	System.out.println(new String(result2,"utf-8"));
}

在这里插入图片描述
DES算法测试类似,就不再贴出。

6.对称加密的各种模式

ECB模式:Electronic Code Book Mode 电子密码本模式,需要对明文进行分组,分组长度可以为128,256或者512bit。优点:操作简单,容易实现,每个分组独立,误差不被传送,易于并行。缺点:暴漏明文信息的结构,相同的明文会得到相同的密文。
CBC模式:Cipher Blocking Chaining Mode 加密链模式。先将明文分成若干小段,然后每一小段与初始块(初始值,初始向量,IV)或者上一段密文进行异或运算之后,再用密钥进行加密。与前面的异或后再加密。优点是:能够掩盖明文的结构,也就是相同明文会得出不同的密文,安全性好于ECB,适合传输长的明文,是SSL和IPSec的标准。缺点是:无法进行并行运算,有填充,不适合流加密,存在传递误差,一步错,步步错。
计算器模式(Counter(CTR)):CTR模式中有一个自增的计数器,将从计数器中得到的数字(参数值)用密钥加密之后与明文做异或操作得出密文,相当于一次一密。优点是:简单快速,安全可靠,可并行加密;误差不会传递。缺点是:计数器不易得到。
输出反馈模式(OFB)
密码反馈模式(Cipher FeedBack(CFB))
GCM(GMAC Counter Mode):对称加密采用Counter模式,并附带有GMAC消息认证码。GMAC中的G就是指GMAC,C就是指CTR,GCM可以同时完成加密和完整性校验,另外还可以提供附加消息的完整性校验。
在实际应用场景中,有些信息是不需要保密的,但是信息的接收者需要确认它的真实性。例如源IP,源端口,目的IP,IV,等等。因此,我们可以将这一部分作为附加消息,加入到MAC值的计算当中。接收方会收到密文,计数器CTR的初始值,MAC值。其优点时效率高,可并行,误差不传播,可同时完成完整性校验,无填充(适合流加密)

那么说了一些有关MAC的东西,什么是MAC呢?MAC(Message Authentication Code 消息验证码),它是用来校验密文完整性的,收发双方共享一个密钥。密文发送者将密文的MAC值随密文一起发送,密文接收者通过密钥解密收到MAC值。这样就可以对收到的密文做完整性检验。
攻击者在篡改密文之后,因为没有密钥,也就相应无法计算出篡改后的密文的MAC值。如果加密模式是CTR,或者是其他有初始IV的加密模式,初始的计时器或吃屎向量的值作为附加消息也与密文一起计算MAC。GMAC(Galois message authentication code,伽罗瓦消息验证码),是利用伽罗瓦域乘法运算来计算消息的MAC值。假设密钥长度为128bits,当密文大于128bits时,需要将密文按128bits进行分组。

你可能感兴趣的:(TLS/SSL协议与加密算法,算法,加密解密,密码学)