java加密技术


          这是一篇总结类的文章,我把一些常用的Java加密技术和核心代码写在这边,供参考。

          首先大家要记住现代密码学最重要的原则柯克霍夫原则:

           数据的安全基于密钥而不是算法的保密。也就是说即使密码系统的任何细节已为人悉知,只要密匙未洩漏,它也应是安全的。

具体来说: 
1. 系统必须可用,非数学上不可译码。 
2. 系统不一定要保密,可以轻易落入敌人手中。 
3. 密匙必须可以不经书写的资料交换和记忆,且双方可以改变密匙。 
4. 系统可以用于电讯。 
5. 系统可以转移位置,它的功能必须不用经过几个人之手才可达到。 
6. 系统容易使用,不要求使用者的脑力过份操劳或有很多的规则。

一、主要的加密方式代码提供方 
JDK:代码在java安装目录下的jre\lib\jce.jar包里; 
CC:Apache公司提供的org.apache.commons.codec 
主页:http://commons.apache.org/proper/commons-codec/ 

所需依赖包:

<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
			<version>1.9</version>
		</dependency>

二、Base64算法 

1、从现在加密算法的复杂性来看Base64这种都不好意思说自己是加密,不过对于完全不懂计算机的人来说也够用了。采用Base64编码具有不可读性,即所编码的数据不会被人用肉眼所直接看到。 
Base64编码一般用于url的处理,或者说任何你不想让普通人一眼就知道是啥的东西都可以用Base64编码处理后再发布在网络上。


package com.rz.secure;

import java.util.Base64;
/**
 * Base64算法基于64个基本字符,加密后的string中只包含这64个字符
 */
public class Base64Strategy {

	public String encode(String src) {
		byte[] encodeBytes = Base64.getEncoder().encode(src.getBytes());
		return new String(encodeBytes);
	}

	public String decode(String src) {
		byte[] decodeBytes = Base64.getDecoder().decode(src.getBytes());
		return new String(decodeBytes);
	}

	public static void main(String[] args) {
		 Base64Strategy bs=new Base64Strategy();
		 
         String encrypt=bs.encode("admin");
         //密文长度跟加密源字符串有关
         System.out.printf("加密后[%d]:%s%n", encrypt.length(),encrypt);
         System.out.println("解密后:"+bs.decode(encrypt));
	}

}
加密后[8]:YWRtaW4=
解密后:admin

2、Base64编码对应关系表 


三、消息摘要算法(Message Digest) 
消息摘要(Message Digest)又称为数字摘要(Digital Digest)。它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生。HASH函数的抗冲突性使得如果一段明文稍有变化,哪怕只更改该段落的一个字母,通过哈希算法作用后都将产生不同的值。而HASH算法的单向性使得要找到哈希值相同的两个不同的输入消息,在计算上是不可能的。所以数据的哈希值,即消息摘要,可以检验数据的完整性。 
用大白话来说,任何一段数据应该都和人一样是唯一的,唯一的标识是什么,人类的话目前就是指纹,而数据的指纹是什么呢?没错,就是消息摘要算法产生的这一段String。比如我们在注册网站的时候,客户端向服务器传输的,应该是我们输入的密码进行消息摘要处理后的内容,这样就算服务器被攻破,Hack也无法知道用户真实的密码是什么。不过有说现在MD5和SHA已经被攻破了,具体大家可以谷歌。 

1、MD5

package com.rz.secure;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;

/**
 * 消息摘要算法
 * 消息摘要(Message Digest)又称为数字摘要(Digital Digest)。它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生。HASH函数的抗冲突性使得如果一段明文稍有变化,哪怕只更改该段落的一个字母,通过哈希算法作用后都将产生不同的值。而HASH算法的单向性使得要找到哈希值相同的两个不同的输入消息,在计算上是不可能的。所以数据的哈希值,即消息摘要,可以检验数据的完整性。 
 */
public class MD5Strategy {

	public String encode_apache(String src) {

		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			byte[] encodeBytes = md.digest(src.getBytes());

			return Hex.encodeHexString(encodeBytes);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return null;
	}

	public String encode_jdk(String str) {
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			md.update(str.getBytes());
			byte b[] = md.digest();

			int i;

			StringBuffer buf = new StringBuffer("");
			for (int offset = 0; offset < b.length; offset++) {
				i = b[offset];
				if (i < 0)
					i += 256;
				if (i < 16)
					buf.append("0");
				buf.append(Integer.toHexString(i));
			}
			str = buf.toString();
		} catch (Exception e) {
			e.printStackTrace();

		}
		return str;
	}

	public String decode(String src) {
		throw new RuntimeException("MD5 no decode");
	}

	public static void main(String[] args) throws InterruptedException {
		
		MD5Strategy bs = new MD5Strategy();

		System.out.println("--------------apache---------------");
		String encrypt = bs.encode_apache("admin");
		System.out.printf("加密后[%d]:%s%n", encrypt.length(),encrypt);
		try {
			System.out.println("解密后:" + bs.decode(encrypt));
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}
		Thread.sleep(100);
		System.out.println("--------------jdk---------------");
		String encrypt_jdk = bs.encode_apache("admin");
		System.out.printf("加密后[%d]:%s%n", encrypt_jdk.length(),encrypt_jdk);
		try {
			System.out.println("解密后:" + bs.decode(encrypt_jdk));
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}
	}

}

输出结果:

--------------apache---------------
加密后[32]:21232f297a57a5a743894a0e4a801fc3
MD5 no decode
--------------jdk---------------
加密后[32]:21232f297a57a5a743894a0e4a801fc3
MD5 no decode

2、SHA

package com.rz.secure;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;

/**
 * 安全散列算法
 */
public class SHAStrategy 
{

    public String encode(String src)
    {
        try
        {
            MessageDigest md = MessageDigest.getInstance("SHA");
            md.update(src.getBytes());
            return Hex.encodeHexString(md.digest());
        }
        catch (NoSuchAlgorithmException e)
        {
            e.printStackTrace();
        }
        return null;
    }

    public String decode(String src)
    {
        throw new RuntimeException("SHA no decode");
    }

	public static void main(String[] args) {
		
		SHAStrategy bs=new SHAStrategy();
		 
        String encrypt=bs.encode("admin");
        System.out.printf("加密后[%d]:%s%n", encrypt.length(),encrypt);
        System.out.println("解密后:"+bs.decode(encrypt));
	}
}

输出结果:

加密后[40]:d033e22ae348aeb5660fc2140aec35850c4da997
Exception in thread "main" java.lang.RuntimeException: SHA no decode
	at com.rz.secure.SHAStrategy.decode(SHAStrategy.java:30)
	at com.rz.secure.SHAStrategy.main(SHAStrategy.java:39)
四、对称加密 
采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。而因为加密和解密都使用同一个密钥,如何把密钥安全地传递到解密者手上就成了必须要解决的问题。当然,安全性较低带来的优点就是优计算量小、加密速度快、加密效率高。 
然并卵,现代计算机对这种级别的计算量早就不care了,安全才是最重要的。 
1、DES 
DES,全称为“Data Encryption Standard”,中文名为“数据加密标准”,是一种使用密钥加密的块算法。DES 算法为密码体制中的对称密码体制,又被称为美国数据加密标准,是 1972 年美国 IBM 公司研制的对称密码体制加密算法。 明文按 64 位进行分组,密钥长 64 位,密钥事实上是 56 位参与 DES 运算(第8、16、24、32、40、48、56、64 位是校验位, 使得每个密钥都有奇数个 1)分组后的明文组和 56 位的密钥按位替代或交换的方法形成密文组的加密方法。


package com.rz.secure;


import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import org.apache.commons.codec.binary.Hex;


/**
 *对称加密算法-des
 */
public class DESStrategy 
{
    private Cipher cipher;
    private SecretKey generateKey;

    public String encode(String src)
    {
        try
        {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
            keyGenerator.init(56);//size
            SecretKey secretKey = keyGenerator.generateKey();
            byte[] keyBytes = secretKey.getEncoded();

            DESKeySpec desKeySpec = new DESKeySpec(keyBytes);
            SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
            generateKey = secretKeyFactory.generateSecret(desKeySpec);

            cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, generateKey);
            byte[] resultBytes = cipher.doFinal(src.getBytes());

            return Hex.encodeHexString(resultBytes);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }

    public String decode(String src)
    {
        try
        {
            cipher.init(Cipher.DECRYPT_MODE, generateKey);
            byte[] result = Hex.decodeHex(src.toCharArray());
            return new String(cipher.doFinal(result));
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }
    
	public static void main(String[] args) {
		DESStrategy bs=new DESStrategy();
		 
        String encrypt=bs.encode("admin");
        System.out.printf("加密后[%d]:%s%n", encrypt.length(),encrypt);
        System.out.println("解密后:"+bs.decode(encrypt));
	}

}
加密后[16]:23a0424ee4e8f390
解密后:admin
2、3DES 

3DES,也就是“Triple DES”,中文名“三重数据加密算法”,它相当于是对每个数据块应用三次 DES 加密算法。由于计算机运算能力的增强,原版 DES 密码的密钥长度变得容易被暴力破解;3DES 即是设计用来提供一种相对简单的方法,即通过增加 DES 的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法。

package com.rz.secure;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import org.apache.commons.codec.binary.Hex;

/**
 * 3DES,也就是“Triple DES”,中文名“三重数据加密算法”,它相当于是对每个数据块应用三次 DES 加密算法。由于计算机运算能力的增强,原版 DES 密码的密钥长度变得容易被暴力破解;3DES 即是设计用来提供一种相对简单的方法,即通过增加 DES 的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法。
 * @author 18217
 */
public class ThreeDESStrategy
{
    private Cipher cipher;
    private SecretKey generateKey;

    public String encode(String src)
    {
        try
        {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede");
            keyGenerator.init(168);//size
            SecretKey secretKey = keyGenerator.generateKey();
            byte[] keyBytes = secretKey.getEncoded();

            DESedeKeySpec desKeySpec = new DESedeKeySpec(keyBytes);
            SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DESede");
            generateKey = secretKeyFactory.generateSecret(desKeySpec);

            cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, generateKey);
            byte[] resultBytes = cipher.doFinal(src.getBytes());

            return Hex.encodeHexString(resultBytes);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }

    public String decode(String src)
    {
        try
        {
            cipher.init(Cipher.DECRYPT_MODE, generateKey);
            byte[] result = Hex.decodeHex(src.toCharArray());
            return new String(cipher.doFinal(result));
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }
    
	public static void main(String[] args) {
		
		ThreeDESStrategy bs=new ThreeDESStrategy();
		 
        String encrypt=bs.encode("admin");
        System.out.printf("加密后[%d]:%s%n", encrypt.length(),encrypt);
        System.out.println("解密后:"+bs.decode(encrypt));
	}
}
加密后[16]:469b6b27d37c4541
解密后:admin

3、AES 

AES,全称为“Advanced Encryption Standard”,中文名“高级加密标准”,在密码学中又称 Rijndael 加密法,是美国联邦政府采用的一种区块加密标准。AES 加密算法作为新一代的数据加密标准汇聚了强安全性、高性能、高效率、易用和灵活等优点。AES 设计有三个密钥长度:128,192,256 位。相对而言,AES 的 128 密钥比 DES 的 56 密钥强了 1021 倍。

package com.rz.secure;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;

/**
 * 对称加密算法-AES算法
 * AES,全称为“Advanced Encryption Standard”,中文名“高级加密标准”,在密码学中又称 Rijndael 加密法,是美国联邦政府采用的一种区块加密标准。AES 加密算法作为新一代的数据加密标准汇聚了强安全性、高性能、高效率、易用和灵活等优点。AES 设计有三个密钥长度:128,192,256 位。相对而言,AES 的 128 密钥比 DES 的 56 密钥强了 1021 倍。
 * @author 18217
 *
 */
public class AESStrategy 
{
    private Cipher cipher;
    private SecretKey generateKey;

    public String encode(String src)
    {
        try
        {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(128);//size
            SecretKey secretKey = keyGenerator.generateKey();
            byte[] keyBytes = secretKey.getEncoded();

            generateKey = new SecretKeySpec(keyBytes, "AES");

            cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, generateKey);
            byte[] resultBytes = cipher.doFinal(src.getBytes());

            return Hex.encodeHexString(resultBytes);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }

    public String decode(String src)
    {
        try
        {
            cipher.init(Cipher.DECRYPT_MODE, generateKey);
            byte[] result = Hex.decodeHex(src.toCharArray());
            return new String(cipher.doFinal(result));
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }
    
	public static void main(String[] args) {
		AESStrategy bs=new AESStrategy();
		 
        String encrypt=bs.encode("admin");
        System.out.printf("加密后[%d]:%s%n", encrypt.length(),encrypt);
        System.out.println("解密后:"+bs.decode(encrypt));
	}
}
加密后[32]:c73544d47ac30b0b210b3870e3915bfa
解密后:admin
4、PBE 
PBE,全称为“Password Base Encryption”,中文名“基于口令加密”,是一种基于密码的加密算法,其特点是使用口令代替了密钥,而口令由用户自己掌管,采用随机数杂凑多重加密等方法保证数据的安全性。 

PBE算法没有密钥的概念,把口令当做密钥了。因为密钥长短影响算法安全性,还不方便记忆,这里我们直接换成我们自己常用的口令就大大不同了,便于我们的记忆。但是单纯的口令很容易被字典法给穷举出来,所以我们这里给口令加了点“盐”,这个盐和口令组合,想破解就难了。同时我们将盐和口令合并后用消息摘要算法进行迭代很多次来构建密钥初始化向量的基本材料,使破译更加难了。

package com.rz.secure;
/**
 * 对称加密算法-PEB算法
 * PBE,全称为“Password Base Encryption”,中文名“基于口令加密”,是一种基于密码的加密算法,其特点是使用口令代替了密钥,而口令由用户自己掌管,采用随机数杂凑多重加密等方法保证数据的安全性。
 * PBE算法没有密钥的概念,把口令当做密钥了。因为密钥长短影响算法安全性,还不方便记忆,这里我们直接换成我们自己常用的口令就大大不同了,便于我们的记忆。但是单纯的口令很容易被字典法给穷举出来,所以我们这里给口令加了点“盐”,这个盐和口令组合,想破解就难了。同时我们将盐和口令合并后用消息摘要算法进行迭代很多次来构建密钥初始化向量的基本材料,使破译更加难了。
 * @author 18217
 */
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.apache.commons.codec.binary.Hex;


/**
 * 基于口令的加密(password),对称 + 消息摘要
 */
public class PBEStrategy
{
    private Cipher cipher;
    private SecretKey generateKey;
    private PBEParameterSpec pbeParameterSpec;

    public String encode(String src)
    {
        try
        {
            SecureRandom secureRandom = new SecureRandom();
            byte[] salt = secureRandom.generateSeed(8);

            String password = "amuro";
            PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
            SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBEWITHMD5andDES");
            generateKey = secretKeyFactory.generateSecret(pbeKeySpec);

            pbeParameterSpec = new PBEParameterSpec(salt, 100);
            cipher = Cipher.getInstance("PBEWITHMD5andDES");
            cipher.init(Cipher.ENCRYPT_MODE, generateKey, pbeParameterSpec);
            byte[] resultBytes = cipher.doFinal(src.getBytes());
            return Hex.encodeHexString(resultBytes);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }

    public String decode(String src)
    {
        try
        {
            cipher.init(Cipher.DECRYPT_MODE, generateKey, pbeParameterSpec);
            byte[] result = Hex.decodeHex(src.toCharArray());
            return new String(cipher.doFinal(result));
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }
    
	public static void main(String[] args) {
		PBEStrategy bs=new PBEStrategy();
		 
        String encrypt=bs.encode("admin");
        System.out.printf("加密后[%d]:%s%n", encrypt.length(),encrypt);
        System.out.println("解密后:"+bs.decode(encrypt));
	}

}
加密后[16]:7ca2e0688b25fb2a
解密后:admin

五、非对称加密 
非对称加密算法需要两个密钥来进行加密和解密,分别是公钥和私钥。需要注意的一点,这个公钥和私钥必须是一对的,如果用公钥对数据进行加密,那么只有使用对应的私钥才能解密,反之亦然。由于加密和解密使用的是两个不同的密钥,因此,这种算法叫做非对称加密算法。 
1、RSA 

其实,在早在 1978 年的时候,RSA就已经出现了,它是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。其原理就如上面的工作过程所述。RSA 算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。

package com.rz.secure;
/**
 * 非对称加密算法-rsa
 * @author 18217
 */
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Hex;


public class RSAStrategy 
{
    private RSAPublicKey rsaPublicKey;
    private RSAPrivateKey rsaPrivateKey;

    public String encode(String src)
    {
        try
        {
            //初始化密钥
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(512);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            rsaPublicKey = (RSAPublicKey)keyPair.getPublic();
            rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate();

            //私钥加密 公钥解密
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec 
                = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            byte[] resultBytes = cipher.doFinal(src.getBytes());

            //私钥解密 公钥加密
//          X509EncodedKeySpec x509EncodedKeySpec =
//                  new X509EncodedKeySpec(rsaPublicKey.getEncoded());
//          KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//          PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
//          Cipher cipher = Cipher.getInstance("RSA");
//          cipher.init(Cipher.ENCRYPT_MODE, publicKey);
//          byte[] resultBytes = cipher.doFinal(src.getBytes());

            return Hex.encodeHexString(resultBytes);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }

    public String decode(String src)
    {
        try
        {
            //私钥加密 公钥解密
            X509EncodedKeySpec x509EncodedKeySpec =
                    new X509EncodedKeySpec(rsaPublicKey.getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            byte[] resultBytes = cipher.doFinal(Hex.decodeHex(src.toCharArray()));

            //私钥解密 公钥加密
//          PKCS8EncodedKeySpec pkcs8EncodedKeySpec 
//              = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
//          KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//          PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//          Cipher cipher = Cipher.getInstance("RSA");
//          cipher.init(Cipher.DECRYPT_MODE, privateKey);
//          byte[] resultBytes = cipher.doFinal(Hex.decodeHex(src.toCharArray()));

            return new String(resultBytes);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }
    
	public static void main(String[] args) {
		 RSAStrategy bs=new RSAStrategy();
		 
        String encrypt=bs.encode("admin");
        System.out.printf("加密后[%d]:%s%n", encrypt.length(),encrypt);
        System.out.println("解密后:"+bs.decode(encrypt));
	}

}
加密后[128]:8cfe1c05bcbeee22c73b4d8306b3ddcf2dd820d34c21d4ec0c5937a80e4f0604c1210e215471d1d945dce8e3d1b1e9abf37349a59c2ee09079232b1baeee5e86
解密后:admin

2、DH算法 
DH,全称为“Diffie-Hellman”,他是一种确保共享KEY安全穿越不安全网络的方法,也就是常说的密钥一致协议。由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。简单的说就是允许两名用户在公开媒体上交换信息以生成“一致”的、可以共享的密钥。也就是由甲方产出一对密钥(公钥、私钥),乙方依照甲方公钥产生乙方密钥对(公钥、私钥)。 

以此为基线,作为数据传输保密基础,同时双方使用同一种对称加密算法构建本地密钥(SecretKey)对数据加密。这样,在互通了本地密钥(SecretKey)算法后,甲乙双方公开自己的公钥,使用对方的公钥和刚才产生的私钥加密数据,同时可以使用对方的公钥和自己的私钥对数据解密。不单单是甲乙双方两方,可以扩展为多方共享数据通讯,这样就完成了网络交互数据的安全通讯!

package com.rz.secure;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Objects;

import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import org.apache.commons.codec.binary.Hex;

/**
 * 非对称加密算法-DH
 * @author 18217
 * DH,全称为“Diffie-Hellman”,他是一种确保共享KEY安全穿越不安全网络的方法,也就是常说的密钥一致协议。由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。简单的说就是允许两名用户在公开媒体上交换信息以生成“一致”的、可以共享的密钥。也就是由甲方产出一对密钥(公钥、私钥),乙方依照甲方公钥产生乙方密钥对(公钥、私钥)。 
以此为基线,作为数据传输保密基础,同时双方使用同一种对称加密算法构建本地密钥(SecretKey)对数据加密。这样,在互通了本地密钥(SecretKey)算法后,甲乙双方公开自己的公钥,使用对方的公钥和刚才产生的私钥加密数据,同时可以使用对方的公钥和自己的私钥对数据解密。不单单是甲乙双方两方,可以扩展为多方共享数据通讯,这样就完成了网络交互数据的安全通讯!
 */
public class DHStrategy {
	private Cipher cipher;
	private SecretKey receiverSecretKey;

	public String encode(String src) {
		try {
			// 初始化发送方密钥
			KeyPairGenerator senderKeyPairGenerator = KeyPairGenerator.getInstance("DH");
			senderKeyPairGenerator.initialize(512);
			KeyPair senderkeyPair = senderKeyPairGenerator.generateKeyPair();
			PrivateKey senderPrivateKey = senderkeyPair.getPrivate();
			byte[] senderPublicKeyBytes = senderkeyPair.getPublic().getEncoded();// 发送方的公钥

			// 初始化接收方密钥,用发送方的公钥
			KeyFactory receiverKeyFactory = KeyFactory.getInstance("DH");
			X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(senderPublicKeyBytes);
			PublicKey receiverPublicKey = receiverKeyFactory.generatePublic(x509EncodedKeySpec);
			DHParameterSpec dhParameterSpec = ((DHPublicKey) receiverPublicKey).getParams();
			KeyPairGenerator receiverKeyPairGenerator = KeyPairGenerator.getInstance("DH");
			receiverKeyPairGenerator.initialize(dhParameterSpec);
			KeyPair receiverKeyPair = receiverKeyPairGenerator.generateKeyPair();
			PrivateKey receiverPrivateKey = receiverKeyPair.getPrivate();
			byte[] receiverPublicKeyBytes = receiverKeyPair.getPublic().getEncoded();

			KeyAgreement receiverKeyAgreement = KeyAgreement.getInstance("DH");
			receiverKeyAgreement.init(receiverPrivateKey);
			receiverKeyAgreement.doPhase(receiverPublicKey, true);
			receiverSecretKey = receiverKeyAgreement.generateSecret("DES");

			// 发送方拿到接收方的public key就可以做加密了
			KeyFactory senderKeyFactory = KeyFactory.getInstance("DH");
			x509EncodedKeySpec = new X509EncodedKeySpec(receiverPublicKeyBytes);
			PublicKey senderPublicKey = senderKeyFactory.generatePublic(x509EncodedKeySpec);
			KeyAgreement senderKeyAgreement = KeyAgreement.getInstance("DH");
			senderKeyAgreement.init(senderPrivateKey);
			senderKeyAgreement.doPhase(senderPublicKey, true);
			SecretKey senderSecretKey = senderKeyAgreement.generateSecret("DES");

			if (Objects.equals(receiverSecretKey, senderSecretKey)) {
				cipher = Cipher.getInstance("DES");
				cipher.init(Cipher.ENCRYPT_MODE, senderSecretKey);
				byte[] result = cipher.doFinal(src.getBytes());
				return Hex.encodeHexString(result);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	public String decode(String src) {
		try {
			cipher.init(Cipher.DECRYPT_MODE, receiverSecretKey);
			byte[] result = Hex.decodeHex(src.toCharArray());
			return new String(cipher.doFinal(result));
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	public static void main(String[] args) {
	   DHStrategy bs=new DHStrategy();
		 
       String encrypt=bs.encode("admin");
       System.out.printf("加密后[%d]:%s%n", encrypt.length(),encrypt);
       System.out.println("解密后:"+bs.decode(encrypt));
	}

}
java.security.NoSuchAlgorithmException: Unsupported secret key algorithm: DES
	at com.sun.crypto.provider.DHKeyAgreement.engineGenerateSecret(DHKeyAgreement.java:387)
	at javax.crypto.KeyAgreement.generateSecret(KeyAgreement.java:648)
	at com.rz.secure.DHStrategy.encode(DHStrategy.java:51)
	at com.rz.secure.DHStrategy.main(DHStrategy.java:91)
Exception in thread "main" java.lang.NullPointerException
	at com.rz.secure.DHStrategy.main(DHStrategy.java:92)

该加密算法笔者还在调试中。。。,待调试成功后补全该加密算法。

六、数字签名证书 
非对称加密已经灰常安全了,但是还有一个破绽: 
服务器A公布了自己的公钥,我的电脑是用服务器A的公钥加密数据后再发给服务器A的;这时候服务器B侵入了我的电脑,把我用来加密的公钥换成了它的公钥,于是我发出去的数据就会被服务器B的私钥破解了。肿么防止公钥被篡改呢? 
对,我们想到了前面的消息摘要,服务器A把公钥丢给我的时候,同时去CA申请一份数字证书,其实主要就是公钥的消息摘要,有了这份证书,当我再用公钥加密的时候,我就可以先验证一下当前的公钥是否确定是服务器A发送给我的。 
这里就贴一种RSA的:

package com.rz.secure;

import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * 数字签名证书
 * @author 18217
 */
public class RSASign {
	

	 public static boolean verifySign(String src)
	    {
	        try
	        {
	            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
	            keyPairGenerator.initialize(512);
	            KeyPair keyPair = keyPairGenerator.generateKeyPair();
	            PublicKey rsaPublicKey = (RSAPublicKey)keyPair.getPublic();
	            PrivateKey rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate();

	            PKCS8EncodedKeySpec pkcs8EncodedKeySpec 
	                = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
	            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
	            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
	            Signature signature = Signature.getInstance("MD5withRSA");
	            signature.initSign(privateKey);
	            signature.update(src.getBytes());
	            //生成签名bytes
	            byte[] signBytes = signature.sign();

	            X509EncodedKeySpec x509EncodedKeySpec =
	                    new X509EncodedKeySpec(rsaPublicKey.getEncoded());
	            keyFactory = KeyFactory.getInstance("RSA");
	            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
	            signature = Signature.getInstance("MD5withRSA");
	            signature.initVerify(publicKey);
	            signature.update(src.getBytes());
	            boolean isVerified = signature.verify(signBytes);

	            return isVerified;
	        }
	        catch(Exception e)
	        {
	            e.printStackTrace();
	        }

	        return false;
	    }


	public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException {
		RSASign bs = new RSASign();
		System.out.println(bs.verifySign("admin"));

	}

}
true
关于数字签名和非对称加密算法的使用,还看到一个非常棒的例子,分享给大家: 
唉,这个月买了太多的书,到月底揭不开锅了。正巧在QQ上遇到了Clark: 
1-2-3:“Clark,我需要200两纹银,能否借给我?” 
Clark:“没问题。我这就给你转账。请给我一张借条。” 
1-2-3:“太谢谢了,我这就用Word写一个借条给你。” 
然后,我新建一个Word文档,写好借条,存盘。然后,然后怎么办呢?我不能直接把借条发送给Clark,原因有: 
1. 我无法保证Clark不会在收到借条后将“纹银200两”改为“纹银2000两”。 
2. 如果我赖账,Clark无法证明这个借条就是我写的。 
3. 普通的Word文档不能作为打官司的证据。 
好在我早就申请了数字证书。我先用我的私钥对借条进行加密,然后将加密后的密文用QQ发送给Clark。Clark收到了借条的密文后,在数字证书认证中心的网站上下载我的公钥,然后使用我的公钥将密文解密,发现确实写的是“借纹银200两”,Clark就可以把银子放心的借给我了,我也不会担心Clark会篡改我的借条,原因是: 
1. 由于我发给Clark的是密文,Clark无法进行修改。Clark倒是可以修改解密后的借条,但是Clark没有我的私钥,没法模仿我对借条进行加密。这就叫防篡改。 
2. 由于用我的私钥进行加密的借条,有且只有我的公钥可以解密。反过来讲,能用我的公钥解密的借条,一定是使用我的私钥加密的,而只有我才拥有我的私钥,这样Clark就可以证明这个借条就是我写的。这就叫防抵赖。 
3. 如果我一直赖着不还钱,Clark把我告上了法庭,这个用我的私钥加密过的Word文档就可以当作程堂证供。因为我国已经出台了《中华人民共和国电子签名法》,使数字签名具有了法律效力。 

您一定已经注意到了,这个使用我的私钥进行了加密的借条,具有了防篡改、防抵赖的特性,并且可以作为程堂证供,就跟我对这个借条进行了“签名”的效果是一样的。对了,“使用我的私钥对借条进行加密”的过程就叫做数字签名。

ok,加密算法告一段落。





  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值