A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 低调的奢华 中级黑马   /  2013-11-16 18:45  /  2423 人查看  /  2 人回复  /   1 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 低调的奢华 于 2013-11-16 18:45 编辑

常见加密算法简介

本文介绍常见的算法(MD5/SHA,DSA,RSA,DES)的应用场景,以及在java上的使用方法.

1)      MD5/SHA

MessageDigest是一个数据的数字指纹.即对一个任意长度的数据进行计算,产生一个唯一指纹号.

MessageDigest的特性:

A)     两个不同的数据,难以生成相同的指纹号

B)      对于指定的指纹号,难以逆向计算出原始数据

代表:MD5/SHA

Java实现:

MD5:
  1. MessageDigest md = MessageDigest.getInstance("MD5");

  2. md.update(Constant.DATA.getBytes());

  3. byte[] result = md.digest();
复制代码
SHA:
  1. MessageDigest md = MessageDigest.getInstance("SHA");

  2. md.update(Constant.DATA.getBytes());

  3. byte[] result = md.digest();
复制代码
2)    DES

单密钥算法,是信息的发送方采用密钥A进行数据加密,信息的接收方采用同一个密钥A进行数据解密.

单密钥算法是一个对称算法.

缺点:由于采用同一个密钥进行加密解密,在多用户的情况下,密钥保管的安全性是一个问题.

代表:DES

Java实现:

首先,需要生成一个密钥,这边的做法,是把生成的密钥,保存到某个文件中.
  1. KeyGenerator gen = KeyGenerator.getInstance("DES");
  2. Key key = gen.generateKey();
  3. File keyFile = new File(Constant.CRYPT_KEY_FILE);
  4. ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(keyFile));
  5. out.writeObject(key);
  6. out.close();
复制代码
在生成key的时候,可以通过SecureRandom产生一个可信任的随机数源
  1. KeyGenerator gen = KeyGenerator.getInstance("DES");
  2. gen.init(new SecureRandom(seed));
  3. Key key = gen.generateKey();
复制代码
加密:
  1. Key key = gen.getKey(Constant.CRYPT_KEY_FILE);//从文件中得到密钥
  2. Cipher cipher = Cipher.getInstance("DES");
  3. cipher.init(Cipher.ENCRYPT_MODE, key); //指定是加密模式
  4. cipher.update(Constant.DATA.getBytes());
  5. byte[] result = cipher.doFinal();
复制代码
解密:
由于DES是一个对称算法,所以解密代码跟加密代码几乎一致
  1. key = gen.getKey(Constant.CRYPT_KEY_FILE);
  2. cipher.init(Cipher.DECRYPT_MODE, key); //指定是解密模式
  3. cipher.update(result);
  4. byte[] data = cipher.doFinal();
复制代码
由于采用了同一个密钥(key),所以两端代码中

Constant.DATA.getBytes()和 data 的值是一致的.

3)    DSA

所谓数字签名是指发送方从发送报文中抽取特征数据(称为数字指纹或摘要),然后用发送方的私钥对数字指纹使用加密算法进行算法操作,接受方使用发送方已经公开的公钥解密并验证报文.

数字签名用户验证发送方身份或者发送方信息的完整性

代表:DSA

Java实现:

同样,首先需要生成一个公钥和私钥,我们也把它保存到相应的文件中
  1. KeyPairGenerator gen = KeyPairGenerator.getInstance(“DSA”);

  2. //以指定的长度初始化KeyPairGenerator对象,如果没有初始化系统以1024长度默认设置

  3. //参数:keysize 算法位长.其范围必须在 512 到 1024 之间,且必须为 64 的倍数

  4. gen.initialize(1024);

  5. KeyPair pair = gen.generateKeyPair();

  6. File pubkeyFile = new File(Constant.PUB_KEY_FILE);

  7. File prikeyFile = new File(Constant.PRI_KEY_FILE);

  8. ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(pubkeyFile));

  9. out.writeObject(pair.getPublic());

  10. out.close();

  11. out = new ObjectOutputStream(new FileOutputStream(prikeyFile));

  12. out.writeObject(pair.getPrivate());

  13. out.close();
复制代码
签名:
  1. PrivateKey prikey = (PrivateKey) gen.getKey(Constant.PRI_KEY_FILE); //从文件得到私钥

  2. // 用私钥对数据签名

  3. Signature signature = Signature.getInstance("DSA");

  4. signature.initSign(prikey);

  5. signature.update(Constant.DATA.getBytes());

  6. byte[] bytes = signature.sign();
复制代码
把原始数据和签名发送给接收方

验证:
用公钥对原始数据和签名进行验证
  1. PublicKey pubkey = (PublicKey) gen.getKey(Constant.PUB_KEY_FILE);//从文件得到公钥

  2. Signature check = Signature.getInstance("DSA");

  3. check.initVerify(pubkey);

  4. check.update(Constant.DATA.getBytes());

  5. //验证数据的完整性

  6. if (check.verify(bytes)) {

  7. System.out.println("OK");

  8. } else {

  9. System.out.println("ERROR");

  10. }
复制代码
4)    RSA

公钥密码体制:为了解决单密钥保管安全性的问题,提供了公钥密码体制的概念.在公钥体制中,加密密钥不同于解密密钥,加密密钥公之于众,谁都可以使用;解密密钥只有解密人自己知道。它们分别称为公开密钥(Public key)和秘密密钥(Private key)。

代表:RSA

Java实现:

同样,需要生成公钥和私钥,并且保存到相应的文件中
  1. KeyPairGenerator gen = KeyPairGenerator.getInstance(“RSA”);

  2. //以指定的长度初始化KeyPairGenerator对象,如果没有初始化系统以1024长度默认设置

  3. //参数:keysize 算法位长.其范围必须在 512 到 1024 之间,且必须为 64 的倍数

  4. gen.initialize(1024);

  5. KeyPair pair = gen.generateKeyPair();

  6. File pubkeyFile = new File(Constant.PUB_KEY_FILE);

  7. File prikeyFile = new File(Constant.PRI_KEY_FILE);

  8. ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(pubkeyFile));

  9. out.writeObject(pair.getPublic());

  10. out.close();

  11. out = new ObjectOutputStream(new FileOutputStream(prikeyFile));

  12. out.writeObject(pair.getPrivate());

  13. out.close();
复制代码
加密:
采用公钥进行加密:
  1. PublicKey pubkey = (PublicKey) gen.getKey(Constant.PUB_KEY_FILE);//从文件中得到公钥

  2. Cipher cipher = Cipher.getInstance("RSA");

  3. cipher.init(Cipher.ENCRYPT_MODE, pubkey);//指定加密模式

  4. byte[] bytes = cipher.doFinal(Constant.DATA.getBytes());
复制代码
解密:
采用私钥进行解密:
  1. PrivateKey prikey = (PrivateKey) gen.getKey(Constant.PRI_KEY_FILE);//从文件中得到私钥

  2. Cipher c = Cipher.getInstance("RSA");

  3. c.init(Cipher.DECRYPT_MODE, prikey);//指定解密模式

  4. byte[] data = c.doFinal(bytes);
复制代码
两段代码中, Constant.DATA.getBytes()和data的值是一致的.

以上,对常见的算法,对了简单的介绍.一般情况下,可以满足我们日常的需求了.


另附3个著名加密算法(MD5、RSA、DES)的解析
地址:http://www.iplaysoft.com/encrypt-arithmetic.html

评分

参与人数 1技术分 +1 黑马币 +2 收起 理由
To + 1 + 2 以后多加点自己的理解,鼓励一分.

查看全部评分

2 个回复

倒序浏览
本帖最后由 低调的奢华 于 2013-11-16 18:35 编辑

MD5加密生成的密钥是固定的32位,而Base64加密和DES加密,生成的密钥不是固定。

现在假如要将用户登录的密码进行加密并存入数据库,MD5加密与DES加密相结合的方式是不错的选择。因为DES加密可逆,但是较之MD5难破解些,一般银行卡账户使用的就是这种算法;另外MD5生成固定的32位密钥,便于在数据库中存储,字段长度不变,不会浪费空间。

下面贴上测试代码:

1.MD5加密工具类
  1. import java.security.MessageDigest;
  2.     import java.security.NoSuchAlgorithmException;

  3.     /**
  4.      * MD5加密工具类
  5.      *
  6.      */
  7.     public class Md5Util {

  8.         /**
  9.          * 根据输入的字符串生成固定的32位MD5码
  10.          *
  11.          * @param str
  12.          *            输入的字符串
  13.          * @return MD5码
  14.          */
  15.         public final static String getMd5(String str) {
  16.             MessageDigest mdInst = null;
  17.             try {
  18.                 mdInst = MessageDigest.getInstance("MD5");
  19.             } catch (NoSuchAlgorithmException e) {
  20.                 e.printStackTrace();
  21.             }
  22.             mdInst.update(str.getBytes());// 使用指定的字节更新摘要
  23.             byte[] md = mdInst.digest();// 获得密文
  24.             return StrConvertUtil.byteArrToHexStr(md);
  25.         }
  26.     }
复制代码
2.DES加密工具类
  1.   import java.security.Key;
  2.     import java.security.Security;

  3.     import javax.crypto.Cipher;

  4.     /**
  5.      * DES加密工具类
  6.      */
  7.     public class DesUtil {

  8.         private static final String ENCRYPT_TYPE = "DES";
  9.         private static String defaultKey = "";// 字符串默认键值
  10.         private Cipher encryptCipher = null;// 加密工具
  11.         private Cipher decryptCipher = null;// 解密工具

  12.         public DesUtil() throws Exception {
  13.             this(defaultKey);
  14.         }

  15.         /**
  16.          * 指定密钥构造方法
  17.          *
  18.          * @param strKey
  19.          *            指定的密钥
  20.          * @throws Exception
  21.          */
  22.         public DesUtil(String strKey) throws Exception {
  23.             Security.addProvider(new com.sun.crypto.provider.SunJCE());
  24.             Key key = getKey(strKey.getBytes());
  25.             encryptCipher = Cipher.getInstance(ENCRYPT_TYPE);
  26.             encryptCipher.init(Cipher.ENCRYPT_MODE, key);
  27.             decryptCipher = Cipher.getInstance(ENCRYPT_TYPE);
  28.             decryptCipher.init(Cipher.DECRYPT_MODE, key);
  29.         }

  30.         /**
  31.          * 加密字节数组
  32.          *
  33.          * @param arr
  34.          *            需加密的字节数组
  35.          * @return 加密后的字节数组
  36.          * @throws Exception
  37.          */
  38.         private byte[] encryptStr(byte[] arr) throws Exception {
  39.             return encryptCipher.doFinal(arr);
  40.         }

  41.         /**
  42.          * 加密字符串
  43.          *
  44.          * @param strIn
  45.          *            需加密的字符串
  46.          * @return 加密后的字符串
  47.          * @throws Exception
  48.          */
  49.         public String encrypt(String strIn) throws Exception {
  50.             return StrConvertUtil.byteArrToHexStr(encryptStr(strIn.getBytes()));
  51.         }

  52.         /**
  53.          * 解密字节数组
  54.          *
  55.          * @param arr
  56.          *            需解密的字节数组
  57.          * @return 解密后的字节数组
  58.          * @throws Exception
  59.          */
  60.         private byte[] decryptStr(byte[] arr) throws Exception {
  61.             return decryptCipher.doFinal(arr);
  62.         }

  63.         /**
  64.          * 解密字符串
  65.          *
  66.          * @param strIn
  67.          *            需解密的字符串
  68.          * @return 解密后的字符串
  69.          * @throws Exception
  70.          */
  71.         public String decrypt(String strIn) throws Exception {
  72.             return new String(decryptStr(StrConvertUtil.hexStrToByteArr(strIn)));
  73.         }

  74.         /**
  75.          * 从指定字符串生成密钥,密钥所需的字节数组长度为8位。不足8位时后面补0,超出8位只取前8位
  76.          *
  77.          * @param arrBTmp
  78.          *            构成该字符串的字节数组
  79.          * @return 生成的密钥
  80.          */
  81.         private Key getKey(byte[] arrBTmp) {
  82.             byte[] arrB = new byte[8];// 创建一个空的8位字节数组(默认值为0)
  83.             // 将原始字节数组转换为8位
  84.             for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
  85.                 arrB<i> = arrBTmp<i>;
  86.           </i></i>  }
  87.             Key key = new javax.crypto.spec.SecretKeySpec(arrB, ENCRYPT_TYPE);// 生成密钥
  88.             return key;
  89.         }
  90.     }<i><i>
  91. </i></i>
复制代码
3.字符串转换工具类
  1. /**
  2.      * 字符串转换工具类
  3.      *
  4.      */
  5.     public class StrConvertUtil {

  6.         /**
  7.          * 将byte数组转换为表示16进制值的字符串,如:byte[]{8,18}转换为:0813, 和public static byte[]
  8.          * hexStrToByteArr(String strIn) 互为可逆的转换过程
  9.          *
  10.          * @param arrB
  11.          *            需要转换的byte数组
  12.          * @return 转换后的字符串
  13.          */
  14.         public static String byteArrToHexStr(byte[] arrB) {
  15.             int iLen = arrB.length;
  16.             // 每个byte(8位)用两个(16进制)字符才能表示,所以字符串的长度是数组长度的两倍
  17.             StringBuffer sb = new StringBuffer(iLen * 2);
  18.             for (int i = 0; i < iLen; i++) {
  19.                 int intTmp = arrB;
  20.                 while (intTmp < 0) {// 把负数转换为正数
  21.                     intTmp = intTmp + 256;
  22.                 }
  23.                 if (intTmp < 16) {// 小于0F的数需要在前面补0
  24.                     sb.append("0");
  25.                 }
  26.                 sb.append(Integer.toString(intTmp, 16));
  27.             }
  28.             return sb.toString();
  29.         }

  30.         /**
  31.          * 将表示16进制值的字符串转换为byte数组,和public static String byteArrToHexStr(byte[] arrB)
  32.          * 互为可逆的转换过程
  33.          *
  34.          * @param strIn
  35.          *            需要转换的字符串
  36.          * @return 转换后的byte数组
  37.          */
  38.         public static byte[] hexStrToByteArr(String strIn) {
  39.             byte[] arrB = strIn.getBytes();
  40.             int iLen = arrB.length;
  41.             // 两个(16进制)字符表示一个字节(8位),所以字节数组长度是字符串长度除以2
  42.             byte[] arrOut = new byte[iLen / 2];
  43.             for (int i = 0; i < iLen; i = i + 2) {
  44.                 String strTmp = new String(arrB, i, 2);
  45.                 arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
  46.             }
  47.             return arrOut;
  48.         }
  49.     }
复制代码
回复 使用道具 举报
本帖最后由 低调的奢华 于 2013-11-16 18:36 编辑

4.测试类
  1. public class Test {
  2.             public static void main(String[] args) {
  3.                 try {
  4.                     DesUtil des = new DesUtil("Java");// 自定义密钥
  5.                     String src = "需要进行加密的字符串";
  6.                     String src1 = des.encrypt(src);
  7.                     String src2 = des.decrypt(src1);
  8.                     String src3 = Md5Util.getMd5(src1);
  9.                     System.out.println("DES加密前的字符:" + src + ",长度:" + src.length());
  10.                     System.out.println("DES加密后的字符:" + src1 + ",长度:" + src1.length());
  11.                     System.out.println("DES解密后的字符:" + src2 + ",长度:" + src2.length());
  12.                     System.out.println("MD5加密后的字符:" + src3 + ",长度:" + src3.length());
  13.                 } catch (Exception e) {
  14.                     e.printStackTrace();
  15.                 }
  16.             }
  17.         }
复制代码
5.测试结果输出


回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马