JAVA 加密算法初探DES&AES

开发项目中需要将重要数据缓存在本地以便在离线是读取,如果不对数据进行处理,很容易造成损失。所以,我们一般对此类数据进行加密处理。这里,主要介绍两种简单的加密算法:DES&AES。

先简单介绍一下一般的加密方案(如下图所示):

技术分享

1)明文:原始信息。
2)加密算法:以密钥为参数,对明文进行多种置换和转换的规则和步骤,变换结果为密文。
3)密钥:加密与解密算法的参数,直接影响对明文进行变换的结果。
4)密文:对明文进行变换的结果。
5)解密算法:加密算法的逆变换,以密文为输入、密钥为参数,变换结果为明文。

密码学中常用的数学运算有一下几种:

◆移位和循环移位
  移位就是将一段数码按照规定的位数整体性地左移或右移。循环右移就是当右移时,把数码的最后的位移到数码的最前头,循环左移正相反。例如,对十进制数码12345678循环右移1位(十进制位)的结果为81234567,而循环左移1位的结果则为23456781。
◆置换
  就是将数码中的某一位的值根据置换表的规定,用另一位代替。它不像移位操作那样整齐有序,看上去杂乱无章。这正是加密所需,被经常应用。
◆扩展
  就是将一段数码扩展成比原来位数更长的数码。扩展方法有多种,例如,可以用置换的方法,以扩展置换表来规定扩展后的数码每一位的替代值。
◆压缩
  就是将一段数码压缩成比原来位数更短的数码。压缩方法有多种,例如,也可以用置换的方法,以表来规定压缩后的数码每一位的替代值。
◆异或
  这是一种二进制布尔代数运算。异或的数学符号为⊕ ,它的运算法则如下:
1⊕1 = 0 
0⊕0 = 0 
1⊕0 = 1 
0⊕1 = 1 
  也可以简单地理解为,参与异或运算的两数位如相等,则结果为0,不等则为1。
◆迭代
  迭代就是多次重复相同的运算,这在密码算法中经常使用,以使得形成的密文更加难以破解。

一、下面就介绍下DES算法:

1、先看下代码实验结果(明文不变,密钥前八位不变):

技术分享

技术分享

通过对比发现在对同一明文加密时,只要密钥前八位相同,所产生的密文就相同。由此可以看出,DES采用的密钥长度为64位,进一步了解可以发现,这64位的密钥中只有56位可以用到,其余8位作为奇偶校验(这8位分别是每个字符转化成二进制后的最后一位)。所以,这也是DES如今不常用的原因,现在的电脑通过穷举,24小时之内就可以得到正确的密钥。但作为研究的材料还是可以的。

下面,我们再看一下密钥和明文怎么产生密文的。

在得到原始密钥(即我们输入的密钥)之后,还需要进行一系列的变换,才能产生真正和明文作用的密钥。其中的变换算法这里不作深究。简单的讲下用到的数学运算有:位移、16次迭代。最终形成16套加密密钥:key[0],key[1],key[2],…。key[14],key[15]。这16套密钥是真正参与到密文的生产。

当然,在得到明文之后也需要进行操作,然后和密钥一起生成密文。其中,明文也会进行16次迭代,在每次迭代过程中,与16套中的密钥以此进行异或运算。之后再进一步对迭代之后的数据进行处理,最终得到密文。

加密完成了,自然会涉及到解密。我们可能会认为解密是加密的逆运算。其实不全是。DES采用的解密算法,和加密算法是一样的,不同的是密钥的使用顺序。在加密中是按照第i次迭代就采用第i次迭代生成的密钥进行异或,而解密时第i次迭代就采用第17-i次迭代生成的密钥和数据进行异或。

完整代码如下:

 

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
public class des {
                      
    private final static String DES = "DES";                     
                   
    public static void main(String[] args){
    	String str_pass = "12345678fedcba";
    	String content = "不剃头的一休哥";
      //  String encryptString = encrypt("不剃头的一休哥",str_pass);
        byte[] key = str_pass.getBytes();
        byte[] src = content.getBytes();
        SecureRandom sr = new SecureRandom();
        byte[] result = null;
        // 从原始密匙数据创建DESKeySpec对象
        DESKeySpec dks;
		try {
			dks = new DESKeySpec(key);
			SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
		    SecretKey securekey = keyFactory.generateSecret(dks);
		    // Cipher对象实际完成加密操作
		    Cipher cipher = Cipher.getInstance(DES);
		    // 用密匙初始化Cipher对象
		    cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
		    // 正式执行加密操作
		    result = cipher.doFinal(src);
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        // 创建一个密匙工厂,然后用它把DESKeySpec转换成一个SecretKey对象
		catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeySpecException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
       
        String hs = "";
        String stmp = "";
        for (int n = 0; n < result.length; n++) {
            stmp = (java.lang.Integer.toHexString(result[n] & 0XFF));
            if (stmp.length() == 1)
                hs = hs + "0" + stmp;
            else
                hs = hs + stmp;
        }
        String encryptString = hs.toUpperCase();
        System.out.println("密钥:"+str_pass);
        System.out.println("密文:"+encryptString);
    }
                      
   
                      
}

二、AES算法

AES 算法是基于置换和代替的。置换是数据的重新排列,而代替是用一个单元数据替换另一个。具体实现这里不介绍了,感兴趣的朋友可以自行查找。明确地说,AES 是一个迭代的、对称密钥分组的密码,它可以使用128、192 和 256 位密钥,并且用 128 位(16字节)分组加密和解密数据。

先上代码吧

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

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 javax.crypto.spec.SecretKeySpec;


public class Aes {

	public static void main(String[] args)
	{
		String content = "不剃头的一休哥";
		String password = "12345678fedcba";
		try {
			KeyGenerator kg = KeyGenerator.getInstance("AES");
			SecureRandom sr = new SecureRandom(password.getBytes());
			kg.init(128,sr);
			
			SecretKey secretKey = kg.generateKey();
			System.out.println("SecureRandom:"+sr.toString());
			System.out.println("SecretKey:"+secretKey);
			byte[] enCodeFormat = secretKey.getEncoded(); 
			SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); 
			Cipher cipher = Cipher.getInstance("AES");// 创建密码器 
			byte[] byteContent = content.getBytes("utf-8");  
            cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化  
            byte[] result = cipher.doFinal(byteContent);  
            String str_res = byte2String(result); // 
            System.out.println("密文:"+str_res);
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public static String byte2String(byte[] b) {
        String hs = "";
        String stmp = "";
        for (int n = 0; n < b.length; n++) {
            stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
            if (stmp.length() == 1)
                hs = hs + "0" + stmp;
            else
                hs = hs + stmp;
        }
        return hs.toUpperCase();
    }
}

 

 代码中我有些一点解释:

kg.init(128,new SecureRandom(password.getBytes()));
SecretKey secretKey = kg.generateKey();

 第一行代码中的随机数可能不同,也就是说kg.init产生的影响可能不同。但是,只要保证password相同,所产生的secretKey就相同。如图所示(DES算法中也用到了随机数,所以也存在此类问题)

技术分享

技术分享

 

查看参考手册可以发现,init函数的作用是  使用用户提供的随机源初始化此密钥生成器,使其具有确定的密钥大小。SecureRandom此类提供强加密随机数生成器。所以,虽然值不同,但是他们同属于一类。我们使用的时候就是用到他们的共同特征。(好牵强,我也没搞懂他们的共同特征是什么o(╯□╰)o)

大概就写这么多吧,有时间查下KeyGenerator.generateKey()的源代码,就能知道他们的共同特征了。然后再作补充。

嗯,加油。

PS:

1、部分内容来源于网上。

2、下周就要真正参与到开发了,周末好好熟悉一下业务流程。O(∩_∩)O~~

 


 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。