各种Java加密算法

各种Java加密算法

您的评价:
         
 
  如基本的单向加密算法: 
  • BASE64 严格地说,属于编码格式,而非加密算法

  • MD5(Message Digest algorithm 5,信息摘要算法)

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

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


    复杂的对称加密(DES、PBE)、非对称加密算法: 

  • DES(Data Encryption Standard,数据加密算法)

  • PBE(Password-based encryption,基于密码验证)

  • RSA(算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman)

  • DH(Diffie-Hellman算法,密钥一致协议)

  • DSA(Digital Signature Algorithm,数字签名)

  • ECC(Elliptic Curves Cryptography,椭圆曲线密码编码学)



    本篇内容简要介绍BASE64MD5SHAHMAC几种方法。 
    MD5SHAHMAC这三种加密算法,可谓是非可逆加密,就是不可解密的加密方法。我们通常只把他们作为加密的基础。单纯的以上三种的加密并不可靠。 

BASE64 
按 照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.) 
常见于邮件、http加密,截取http信息,你就会发现登录操作的用户名、密码字段通过BASE64加密的。 

各种Java加密算法 

通过java代码实现如下:

01 /**
02      * BASE64解密
03      
04      * @param key
05      * @return
06      * @throws Exception
07      */
08     public static byte[] decryptBASE64(String key) throws Exception {
09         return (new BASE64Decoder()).decodeBuffer(key);
10     }
11  
12     /**
13      * BASE64加密
14      
15      * @param key
16      * @return
17      * @throws Exception
18      */
19     public static String encryptBASE64(byte[] key) throws Exception {
20         return (new BASE64Encoder()).encodeBuffer(key);
21     }


主要就是BASE64Encoder、BASE64Decoder两个类,我们只需要知道使用对应的方法即可。另,BASE加密后产生的字节位数是8的倍数,如果不够位数以=符号填充。 

MD5 
MD5 -- message-digest algorithm 5 (信息-摘要算法)缩写,广泛用于加密和解密技术,常用于文件校验。校验?不管文件多大,经过MD5后都能生成唯一的MD5值。好比现在的ISO校验,都 是MD5校验。怎么用?当然是把ISO经过MD5后产生MD5的值。一般下载linux-ISO的朋友都见过下载链接旁边放着MD5的串。就是用来验证文 件是否一致的。 

各种Java加密算法 

通过java代码实现如下:

01 /**
02      * MD5加密
03      
04      * @param data
05      * @return
06      * @throws Exception
07      */
08     public static byte[] encryptMD5(byte[] data) throws Exception {
09  
10         MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
11         md5.update(data);
12  
13         return md5.digest();
14  
15     }



通常我们不直接使用上述MD5加密。通常将MD5产生的字节数组交给BASE64再加密一把,得到相应的字符串。 

SHA 
SHA(Secure Hash Algorithm,安全散列算法),数字签名等密码学应用中重要的工具,被广泛地应用于电子商务等信息安全领域。虽然,SHA与MD5通过碰撞法都被破解了, 但是SHA仍然是公认的安全加密算法,较之MD5更为安全。 

各种Java加密算法 

通过java代码实现如下:

01 /**
02      * SHA加密
03      
04      * @param data
05      * @return
06      * @throws Exception
07      */
08     public static byte[] encryptSHA(byte[] data) throws Exception {
09  
10         MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
11         sha.update(data);
12  
13         return sha.digest();
14  
15     }
16 }



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

各种Java加密算法 

通过java代码实现如下:

01 /**
02      * 初始化HMAC密钥
03      
04      * @return
05      * @throws Exception
06      */
07     public static String initMacKey() throws Exception {
08         KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);
09  
10         SecretKey secretKey = keyGenerator.generateKey();
11         return encryptBASE64(secretKey.getEncoded());
12     }
13  
14     /**
15      * HMAC加密
16      
17      * @param data
18      * @param key
19      * @return
20      * @throws Exception
21      */
22     public static byte[] encryptHMAC(byte[] data, String key) throws Exception {
23  
24         SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);
25         Mac mac = Mac.getInstance(secretKey.getAlgorithm());
26         mac.init(secretKey);
27  
28         return mac.doFinal(data);
29  
30     }



给出一个完整类,如下:

001 import java.security.MessageDigest;
002  
003 import javax.crypto.KeyGenerator;
004 import javax.crypto.Mac;
005 import javax.crypto.SecretKey;
006  
007 import sun.misc.BASE64Decoder;
008 import sun.misc.BASE64Encoder;
009  
010 /**
011  * 基础加密组件
012  
013  * @author 梁栋
014  * @version 1.0
015  * @since 1.0
016  */
017 public abstract class Coder {
018     public static final String KEY_SHA = "SHA";
019     public static final String KEY_MD5 = "MD5";
020  
021     /**
022      * MAC算法可选以下多种算法
023      
024      
025      * HmacMD5 
026      * HmacSHA1 
027      * HmacSHA256 
028      * HmacSHA384 
029      * HmacSHA512
030      
031      */
032     public static final String KEY_MAC = "HmacMD5";
033  
034     /**
035      * BASE64解密
036      
037      * @param key
038      * @return
039      * @throws Exception
040      */
041     public static byte[] decryptBASE64(String key) throws Exception {
042         return (new BASE64Decoder()).decodeBuffer(key);
043     }
044  
045     /**
046      * BASE64加密
047      
048      * @param key
049      * @return
050      * @throws Exception
051      */
052     public static String encryptBASE64(byte[] key) throws Exception {
053         return (new BASE64Encoder()).encodeBuffer(key);
054     }
055  
056     /**
057      * MD5加密
058      
059      * @param data
060      * @return
061      * @throws Exception
062      */
063     public static byte[] encryptMD5(byte[] data) throws Exception {
064  
065         MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
066         md5.update(data);
067  
068         return md5.digest();
069  
070     }
071  
072     /**
073      * SHA加密
074      
075      * @param data
076      * @return
077      * @throws Exception
078      */
079     public static byte[] encryptSHA(byte[] data) throws Exception {
080  
081         MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
082         sha.update(data);
083  
084         return sha.digest();
085  
086     }
087  
088     /**
089      * 初始化HMAC密钥
090      
091      * @return
092      * @throws Exception
093      */
094     public static String initMacKey() throws Exception {
095         KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);
096  
097         SecretKey secretKey = keyGenerator.generateKey();
098         return encryptBASE64(secretKey.getEncoded());
099     }
100  
101     /**
102      * HMAC加密
103      
104      * @param data
105      * @param key
106      * @return
107      * @throws Exception
108      */
109     public static byte[] encryptHMAC(byte[] data, String key) throws Exception {
110  
111         SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);
112         Mac mac = Mac.getInstance(secretKey.getAlgorithm());
113         mac.init(secretKey);
114  
115         return mac.doFinal(data);
116  
117     }
118 }



再给出一个测试类:

01 import static org.junit.Assert.*;
02  
03 import org.junit.Test;
04  
05 /**
06  
07  * @author 梁栋
08  * @version 1.0
09  * @since 1.0
10  */
11 public class CoderTest {
12  
13     @Test
14     public void test() throws Exception {
15         String inputStr = "简单加密";
16         System.err.println("原文:\n" + inputStr);
17  
18         byte[] inputData = inputStr.getBytes();
19         String code = Coder.encryptBASE64(inputData);
20  
21         System.err.println("BASE64加密后:\n" + code);
22  
23         byte[] output = Coder.decryptBASE64(code);
24  
25         String outputStr = new String(output);
26  
27         System.err.println("BASE64解密后:\n" + outputStr);
28  
29         // 验证BASE64加密解密一致性
30         assertEquals(inputStr, outputStr);
31  
32         // 验证MD5对于同一内容加密是否一致
33         assertArrayEquals(Coder.encryptMD5(inputData), Coder
34                 .encryptMD5(inputData));
35  
36         // 验证SHA对于同一内容加密是否一致
37         assertArrayEquals(Coder.encryptSHA(inputData), Coder
38                 .encryptSHA(inputData));
39  
40         String key = Coder.initMacKey();
41         System.err.println("Mac密钥:\n" + key);
42  
43         // 验证HMAC对于同一内容,同一密钥加密是否一致
44         assertArrayEquals(Coder.encryptHMAC(inputData, key), Coder.encryptHMAC(
45                 inputData, key));
46  
47         BigInteger md5 = new BigInteger(Coder.encryptMD5(inputData));
48         System.err.println("MD5:\n" + md5.toString(16));
49  
50         BigInteger sha = new BigInteger(Coder.encryptSHA(inputData));
51         System.err.println("SHA:\n" + sha.toString(32));
52  
53         BigInteger mac = new BigInteger(Coder.encryptHMAC(inputData, inputStr));
54         System.err.println("HMAC:\n" + mac.toString(16));
55     }
56 }



控制台输出:

原文:
简单加密
BASE64加密后:
566A5Y2V5Yqg5a+G

BASE64解密后:
简单加密
Mac密钥:
uGxdHC+6ylRDaik++leFtGwiMbuYUJ6mqHWyhSgF4trVkVBBSQvY/a22xU8XT1RUemdCWW155Bke
pBIpkd7QHg==

MD5:
-550b4d90349ad4629462113e7934de56
SHA:
91k9vo7p400cjkgfhjh0ia9qthsjagfn
HMAC:
2287d192387e95694bdbba2fa941009a



注意 
编译时,可能会看到如下提示: 

引用


警告:sun.misc.BASE64Decoder 是 Sun 的专用 API,可能会在未来版本中删除 

import sun.misc.BASE64Decoder; 
               ^ 
警告:sun.misc.BASE64Encoder 是 Sun 的专用 API,可能会在未来版本中删除 

import sun.misc.BASE64Encoder; 
               ^ 



BASE64Encoder 和BASE64Decoder是非官方JDK实现类。虽然可以在JDK里能找到并使用,但是在API里查不到。JRE 中 sun 和 com.sun 开头包的类都是未被文档化的,他们属于 java, javax 类库的基础,其中的实现大多数与底层平台有关,一般来说是不推荐使用的。 


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

    接下来我们介绍对称加密算法,最常用的莫过于DES数据加密算法。 
DES 
DES-Data Encryption Standard,即数据加密算法。是IBM公司于1975年研究成功并公开发表的。DES算法的入口参数有三个:Key、Data、Mode。其中 Key为8个字节共64位,是DES算法的工作密钥;Data也为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密 或解密。 
        DES算法把64位的明文输入块变为64位的密文输出块,它所使用的密钥也是64位。 


通过java代码实现如下:Coder类见

001 import java.security.Key;
002 import java.security.SecureRandom;
003  
004 import javax.crypto.Cipher;
005 import javax.crypto.KeyGenerator;
006 import javax.crypto.SecretKey;
007 import javax.crypto.SecretKeyFactory;
008 import javax.crypto.spec.DESKeySpec;
009  
010  
011 /**
012  * DES安全编码组件
013  
014  
015  * 支持 DES、DESede(TripleDES,就是3DES)、AES、Blowfish、RC2、RC4(ARCFOUR)
016  * DES                  key size must be equal to 56
017  * DESede(TripleDES)     key size must be equal to 112 or 168
018  * AES                  key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available
019  * Blowfish          key size must be multiple of 8, and can only range from 32 to 448 (inclusive)
020  * RC2                  key size must be between 40 and 1024 bits
021  * RC4(ARCFOUR)      key size must be between 40 and 1024 bits
022  * 具体内容 需要关注 JDK Document http://.../docs/technotes/guides/security/SunProviders.html
023  
024  
025  * @author 梁栋
026  * @version 1.0
027  * @since 1.0
028  */
029 public abstract class DESCoder extends Coder {
030     /**
031      * ALGORITHM 算法 
032      * 可替换为以下任意一种算法,同时key值的size相应改变。
033      
034      
035      * DES                  key size must be equal to 56
036      * DESede(TripleDES)     key size must be equal to 112 or 168
037      * AES                  key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available
038      * Blowfish          key size must be multiple of 8, and can only range from 32 to 448 (inclusive)
039      * RC2                  key size must be between 40 and 1024 bits
040      * RC4(ARCFOUR)      key size must be between 40 and 1024 bits
041      
042      
043      * 在Key toKey(byte[] key)方法中使用下述代码
044      SecretKey secretKey = new SecretKeySpec(key, ALGORITHM); 替换
045      
046      * DESKeySpec dks = new DESKeySpec(key);
047      * SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
048      * SecretKey secretKey = keyFactory.generateSecret(dks);
049      
050      */
051     public static final String ALGORITHM = "DES";
052  
053     /**
054      * 转换密钥
055      
056      * @param key
057      * @return
058      * @throws Exception
059      */
060     private static Key toKey(byte[] key) throws Exception {
061         DESKeySpec dks = new DESKeySpec(key);
062         SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
063         SecretKey secretKey = keyFactory.generateSecret(dks);
064  
065         // 当使用其他对称加密算法时,如AES、Blowfish等算法时,用下述代码替换上述三行代码
066         // SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);
067  
068         return secretKey;
069     }
070  
071     /**
072      * 解密
073      
074      * @param data
075      * @param key
076      * @return
077      * @throws Exception
078      */
079     public static byte[] decrypt(byte[] data, String key) throws Exception {
080         Key k = toKey(decryptBASE64(key));
081  
082         Cipher cipher = Cipher.getInstance(ALGORITHM);
083         cipher.init(Cipher.DECRYPT_MODE, k);
084  
085         return cipher.doFinal(data);
086     }
087  
088     /**
089      * 加密
090      
091      * @param data
092      * @param key
093      * @return
094      * @throws Exception
095      */
096     public static byte[] encrypt(byte[] data, String key) throws Exception {
097         Key k = toKey(decryptBASE64(key));
098         Cipher cipher = Cipher.getInstance(ALGORITHM);
099         cipher.init(Cipher.ENCRYPT_MODE, k);
100  
101         return cipher.doFinal(data);
102     }
103  
104     /**
105      * 生成密钥
106      
107      * @return
108      * @throws Exception
109      */
110     public static String initKey() throws Exception {
111         return initKey(null);
112     }
113  
114     /**
115      * 生成密钥
116      
117      * @param seed
118      * @return
119      * @throws Exception
120      */
121     public static String initKey(String seed) throws Exception {
122         SecureRandom secureRandom = null;
123  
124         if (seed != null) {
125             secureRandom = new SecureRandom(decryptBASE64(seed));
126         else {
127             secureRandom = new SecureRandom();
128         }
129  
130         KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);
131         kg.init(secureRandom);
132  
133         SecretKey secretKey = kg.generateKey();
134  
135         return encryptBASE64(secretKey.getEncoded());
136     }
137 }


延续上一个类的实现,我们通过MD5以及SHA对字符串加密生成密钥,这是比较常见的密钥生成方式。 
再给出一个测试类:

01 import static org.junit.Assert.*;
02  
03  
04 import org.junit.Test;
05  
06 /**
07  
08  * @author 梁栋
09  * @version 1.0
10  * @since 1.0
11  */
12 public class DESCoderTest {
13  
14     @Test
15     public void test() throws Exception {
16         String inputStr = "DES";
17         String key = DESCoder.initKey();
18         System.err.println("原文:\t" + inputStr);
19  
20         System.err.println("密钥:\t" + key);
21  
22         byte[] inputData = inputStr.getBytes();
23         inputData = DESCoder.encrypt(inputData, key);
24  
25         System.err.println("加密后:\t" + DESCoder.encryptBASE64(inputData));
26  
27         byte[] outputData = DESCoder.decrypt(inputData, key);
28         String outputStr = new String(outputData);
29  
30         System.err.println("解密后:\t" + outputStr);
31  
32         assertEquals(inputStr, outputStr);
33     }
34 }


得到的输出内容如下:

原文:	DES
密钥:	f3wEtRrV6q0=

加密后:	C6qe9oNIzRY=

解密后:	DES


    由控制台得到的输出,我们能够比对加密、解密后结果一致。这是一种简单的加密解密方式,只有一个密钥。 
    其实DES有很多同胞兄弟,如DESede(TripleDES)、AES、Blowfish、RC2、RC4(ARCFOUR)。这里就不过多阐述了,大同小异,只要换掉ALGORITHM换成对应的值,同时做一个代码替换SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);就可以了,此外就是密钥长度不同了。 

1 /**
2  * DES          key size must be equal to 56
3  * DESede(TripleDES) key size must be equal to 112 or 168
4  * AES          key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available
5  * Blowfish     key size must be multiple of 8, and can only range from 32 to 448 (inclusive)
6  * RC2          key size must be between 40 and 1024 bits
7  * RC4(ARCFOUR) key size must be between 40 and 1024 bits
8  **/

    除了DES,我们还知道有DESede(TripleDES,就是3DES)、AES、Blowfish、RC2、RC4(ARCFOUR)等多种对称加密方式,其实现方式大同小异,这里介绍对称加密的另一个算法——PBE 
PBE 
    PBE——Password-based encryption(基于密码加密)。其特点在于口令由用户自己掌管,不借助任何物理媒体;采用随机数(这里我们叫做盐)杂凑多重加密等方法保证数据的安全性。是一种简便的加密方式。 
通过java代码实现如下:Coder类见 

001 import java.security.Key;
002 import java.util.Random;
003   
004 import javax.crypto.Cipher;
005 import javax.crypto.SecretKey;
006 import javax.crypto.SecretKeyFactory;
007 import javax.crypto.spec.PBEKeySpec;
008 import javax.crypto.spec.PBEParameterSpec;
009   
010 /**
011  * PBE安全编码组件
012  
013  * @author 梁栋
014  * @version 1.0
015  * @since 1.0
016  */
017 public abstract class PBECoder extends Coder {
018     /**
019      * 支持以下任意一种算法
020      
021      
022      * PBEWithMD5AndDES 
023      * PBEWithMD5AndTripleDES 
024      * PBEWithSHA1AndDESede
025      * PBEWithSHA1AndRC2_40
026      
027      */
028     public static final String ALGORITHM = "PBEWITHMD5andDES";
029   
030     /**
031      * 盐初始化
032      
033      * @return
034      * @throws Exception
035      */
036     public static byte[] initSalt() throws Exception {
037         byte[] salt = new byte[8];
038         Random random = new Random();
039         random.nextBytes(salt);
040         return salt;
041     }
042   
043     /**
044      * 转换密钥
045      
046      * @param password
047      * @return
048      * @throws Exception
049      */
050     private static Key toKey(String password) throws Exception {
051         PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
052         SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
053         SecretKey secretKey = keyFactory.generateSecret(keySpec);
054   
055         return secretKey;
056     }
057   
058     /**
059      * 加密
060      
061      * @param data 数据
062      * @param password 密码
063      * @param salt  盐
064      * @return
065      * @throws Exception
066      */
067     public static byte[] encrypt(byte[] data, String password, byte[] salt)
068             throws Exception {
069   
070         Key key = toKey(password);
071   
072         PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
073         Cipher cipher = Cipher.getInstance(ALGORITHM);
074         cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
075   
076         return cipher.doFinal(data);
077   
078     }
079   
080     /**
081      * 解密
082      
083      * @param data  数据
084      * @param password 密码
085      * @param salt  盐
086      * @return
087      * @throws Exception
088      */
089     public static byte[] decrypt(byte[] data, String password, byte[] salt)
090             throws Exception {
091   
092         Key key = toKey(password);
093   
094         PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
095         Cipher cipher = Cipher.getInstance(ALGORITHM);
096         cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
097   
098         return cipher.doFinal(data);
099   
100     }
101 }



再给出一个测试类: 

01 import static org.junit.Assert.*;
02   
03 import org.junit.Test;
04   
05 /**
06  
07  * @author 梁栋
08  * @version 1.0
09  * @since 1.0
10  */
11 public class PBECoderTest {
12   
13     @Test
14     public void test() throws Exception {
15         String inputStr = "abc";
16         System.err.println("原文: " + inputStr);
17         byte[] input = inputStr.getBytes();
18   
19         String pwd = "efg";
20         System.err.println("密码: " + pwd);
21   
22         byte[] salt = PBECoder.initSalt();
23   
24         byte[] data = PBECoder.encrypt(input, pwd, salt);
25   
26         System.err.println("加密后: " + PBECoder.encryptBASE64(data));
27   
28         byte[] output = PBECoder.decrypt(data, pwd, salt);
29         String outputStr = new String(output);
30   
31         System.err.println("解密后: " + outputStr);
32         assertEquals(inputStr, outputStr);
33     }
34   
35 }



控制台输出: 

原文: abc
密码: efg
加密后: iCZ0uRtaAhE=
 
解密后: abc


    后续我们会介绍非对称加密算法,如RSA、DSA、DH、ECC等。 

    接下来我们介绍典型的非对称加密算法——RSA 

RSA 
    这种算法1978年就出现了,它是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman。 
    这种加密算法的特点主要是密钥的变化,上文我们看到DES只有一个密钥。相当于只有一把钥匙,如果这把钥匙丢了,数据也就不安全了。RSA同时有两把钥 匙,公钥与私钥。同时支持数字签名。数字签名的意义在于,对传输过来的数据进行校验。确保数据在传输工程中不被修改。 

流程分析: 

  1. 甲方构建密钥对儿,将公钥公布给乙方,将私钥保留。

  2. 甲方使用私钥加密数据,然后用私钥对加密后的数据签名,发送给乙方签名以及加密后的数据;乙方使用公钥、签名来验证待解密数据是否有效,如果有效使用公钥对数据解密。

  3. 乙方使用公钥加密数据,向甲方发送经过加密后的数据;甲方获得加密数据,通过私钥解密。



按如上步骤给出序列图,如下: 

  1. 各种Java加密算法_第1张图片

  2. 各种Java加密算法_第2张图片

  3. 各种Java加密算法_第3张图片



通过java代码实现如下:Coder类见

001 import java.security.Key;
002 import java.security.KeyFactory;
003 import java.security.KeyPair;
004 import java.security.KeyPairGenerator;
005 import java.security.PrivateKey;
006 import java.security.PublicKey;
007 import java.security.Signature;
008 import java.security.interfaces.RSAPrivateKey;
009 import java.security.interfaces.RSAPublicKey;
010 import java.security.spec.PKCS8EncodedKeySpec;
011 import java.security.spec.X509EncodedKeySpec;
012  
013 import java.util.HashMap;
014 import java.util.Map;
015  
016 import javax.crypto.Cipher;
017  
018 /**
019  * RSA安全编码组件
020  
021  * @author 梁栋
022  * @version 1.0
023  * @since 1.0
024  */
025 public abstract class RSACoder extends Coder {
026     public static final String KEY_ALGORITHM = "RSA";
027     public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
028  
029     private static final String PUBLIC_KEY = "RSAPublicKey";
030     private static final String PRIVATE_KEY = "RSAPrivateKey";
031  
032     /**
033      * 用私钥对信息生成数字签名
034      
035      * @param data
036      *            加密数据
037      * @param privateKey
038      *            私钥
039      
040      * @return
041      * @throws Exception
042      */
043     public static String sign(byte[] data, String privateKey) throws Exception {
044         // 解密由base64编码的私钥
045         byte[] keyBytes = decryptBASE64(privateKey);
046  
047         // 构造PKCS8EncodedKeySpec对象
048         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
049  
050         // KEY_ALGORITHM 指定的加密算法
051         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
052  
053         // 取私钥匙对象
054         PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
055  
056         // 用私钥对信息生成数字签名
057         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
058         signature.initSign(priKey);
059         signature.update(data);
060  
061         return encryptBASE64(signature.sign());
062     }
063  
064     /**
065      * 校验数字签名
066      
067      * @param data
068      *            加密数据
069      * @param publicKey
070      *            公钥
071      * @param sign
072      *            数字签名
073      
074      * @return 校验成功返回true 失败返回false
075      * @throws Exception
076      
077      */
078     public static boolean verify(byte[] data, String publicKey, String sign)
079             throws Exception {
080  
081         // 解密由base64编码的公钥
082         byte[] keyBytes = decryptBASE64(publicKey);
083  
084         // 构造X509EncodedKeySpec对象
085         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
086  
087         // KEY_ALGORITHM 指定的加密算法
088         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
089  
090         // 取公钥匙对象
091         PublicKey pubKey = keyFactory.generatePublic(keySpec);
092  
093         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
094         signature.initVerify(pubKey);
095         signature.update(data);
096  
097         // 验证签名是否正常
098         return signature.verify(decryptBASE64(sign));
099     }
100  
101     /**
102      * 解密
103      * 用私钥解密
104      
105      * @param data
106      * @param key
107      * @return
108      * @throws Exception
109      */
110     public static byte[] decryptByPrivateKey(byte[] data, String key)
111             throws Exception {
112         // 对密钥解密
113         byte[] keyBytes = decryptBASE64(key);
114  
115         // 取得私钥
116         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
117         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
118         Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
119  
120         // 对数据解密
121         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
122         cipher.init(Cipher.DECRYPT_MODE, privateKey);
123  
124         return cipher.doFinal(data);
125     }
126  
127     /**
128      * 解密
129      * 用私钥解密
130      
131      * @param data
132      * @param key
133      * @return
134      * @throws Exception
135      */
136     public static byte[] decryptByPublicKey(byte[] data, String key)
137             throws Exception {
138         // 对密钥解密
139         byte[] keyBytes = decryptBASE64(key);
140  
141         // 取得公钥
142         X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
143         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
144         Key publicKey = keyFactory.generatePublic(x509KeySpec);
145  
146         // 对数据解密
147         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
148         cipher.init(Cipher.DECRYPT_MODE, publicKey);
149  
150         return cipher.doFinal(data);
151     }
152  
153     /**
154      * 加密
155      * 用公钥加密
156      
157      * @param data
158      * @param key
159      * @return
160      * @throws Exception
161      */
162     public static byte[] encryptByPublicKey(byte[] data, String key)
163             throws Exception {
164         // 对公钥解密
165         byte[] keyBytes = decryptBASE64(key);
166  
167         // 取得公钥
168         X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
169         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
170         Key publicKey = keyFactory.generatePublic(x509KeySpec);
171  
172         // 对数据加密
173         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
174         cipher.init(Cipher.ENCRYPT_MODE, publicKey);
175  
176         return cipher.doFinal(data);
177     }
178  
179     /**
180      * 加密
181      * 用私钥加密
182      
183      * @param data
184      * @param key
185      * @return
186      * @throws Exception
187      */
188     public static byte[] encryptByPrivateKey(byte[] data, String key)
189             throws Exception {
190         // 对密钥解密
191         byte[] keyBytes = decryptBASE64(key);
192  
193         // 取得私钥
194         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
195         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
196         Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
197  
198         // 对数据加密
199         Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
200         cipher.init(Cipher.ENCRYPT_MODE, privateKey);
201  
202         return cipher.doFinal(data);
203     }
204  
205     /**
206      * 取得私钥
207      
208      * @param keyMap
209      * @return
210      * @throws Exception
211      */
212     public static String getPrivateKey(Map keyMap)
213             throws Exception {
214         Key key = (Key) keyMap.get(PRIVATE_KEY);
215  
216         return encryptBASE64(key.getEncoded());
217     }
218  
219     /**
220      * 取得公钥
221      
222      * @param keyMap
223      * @return
224      * @throws Exception
225      */
226     public static String getPublicKey(Map keyMap)
227             throws Exception {
228         Key key = (Key) keyMap.get(PUBLIC_KEY);
229  
230         return encryptBASE64(key.getEncoded());
231     }
232  
233     /**
234      * 初始化密钥
235      
236      * @return
237      * @throws Exception
238      */
239     public static Map initKey() throws Exception {
240         KeyPairGenerator keyPairGen = KeyPairGenerator
241                 .getInstance(KEY_ALGORITHM);
242         keyPairGen.initialize(1024);
243  
244         KeyPair keyPair = keyPairGen.generateKeyPair();
245  
246         // 公钥
247         RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
248  
249         // 私钥
250         RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
251  
252         Map keyMap = new HashMap(2);
253  
254         keyMap.put(PUBLIC_KEY, publicKey);
255         keyMap.put(PRIVATE_KEY, privateKey);
256         return keyMap;
257     }
258 }


再给出一个测试类:

01 import static org.junit.Assert.*;
02  
03 import org.junit.Before;
04 import org.junit.Test;
05  
06 import java.util.Map;
07  
08 /**
09  
10  * @author 梁栋
11  * @version 1.0
12  * @since 1.0
13  */
14 public class RSACoderTest {
15     private String publicKey;
16     private String privateKey;
17  
18     @Before
19     public void setUp() throws Exception {
20         Map keyMap = RSACoder.initKey();
21  
22         publicKey = RSACoder.getPublicKey(keyMap);
23         privateKey = RSACoder.getPrivateKey(keyMap);
24         System.err.println("公钥: \n\r" + publicKey);
25         System.err.println("私钥: \n\r" + privateKey);
26     }
27  
28     @Test
29     public void test() throws Exception {
30         System.err.println("公钥加密——私钥解密");
31         String inputStr = "abc";
32         byte[] data = inputStr.getBytes();
33  
34         byte[] encodedData = RSACoder.encryptByPublicKey(data, publicKey);
35  
36         byte[] decodedData = RSACoder.decryptByPrivateKey(encodedData,
37                 privateKey);
38  
39         String outputStr = new String(decodedData);
40         System.err.println("加密前: " + inputStr + "\n\r" "解密后: " + outputStr);
41         assertEquals(inputStr, outputStr);
42  
43     }
44  
45     @Test
46     public void testSign() throws Exception {
47         System.err.println("私钥加密——公钥解密");
48         String inputStr = "sign";
49         byte[] data = inputStr.getBytes();
50  
51         byte[] encodedData = RSACoder.encryptByPrivateKey(data, privateKey);
52  
53         byte[] decodedData = RSACoder
54                 .decryptByPublicKey(encodedData, publicKey);
55  
56         String outputStr = new String(decodedData);
57         System.err.println("加密前: " + inputStr + "\n\r" "解密后: " + outputStr);
58         assertEquals(inputStr, outputStr);
59  
60         System.err.println("私钥签名——公钥验证签名");
61         // 产生签名
62         String sign = RSACoder.sign(encodedData, privateKey);
63         System.err.println("签名:\r" + sign);
64  
65         // 验证签名
66         boolean status = RSACoder.verify(encodedData, publicKey, sign);
67         System.err.println("状态:\r" + status);
68         assertTrue(status);
69  
70     }
71  
72 }


控制台输出:

公钥: 

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYU/+I0+z1aBl5X6DUUOHQ7FZpmBSDbKTtx89J
EcB64jFCkunELT8qiKly7fzEqD03g8ALlu5XvX+bBqHFy7YPJJP0ekE2X3wjUnh2NxlqpH3/B/xm
1ZdSlCwDIkbijhBVDjA/bu5BObhZqQmDwIxlQInL9oVz+o6FbAZCyHBd7wIDAQAB

私钥: 

MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJhT/4jT7PVoGXlfoNRQ4dDsVmmY
FINspO3Hz0kRwHriMUKS6cQtPyqIqXLt/MSoPTeDwAuW7le9f5sGocXLtg8kk/R6QTZffCNSeHY3
GWqkff8H/GbVl1KULAMiRuKOEFUOMD9u7kE5uFmpCYPAjGVAicv2hXP6joVsBkLIcF3vAgMBAAEC
gYBvZHWoZHmS2EZQqKqeuGr58eobG9hcZzWQoJ4nq/CarBAjw/VovUHE490uK3S9ht4FW7Yzg3LV
/MB06Huifh6qf/X9NQA7SeZRRC8gnCQk6JuDIEVJOud5jU+9tyumJakDKodQ3Jf2zQtNr+5ZdEPl
uwWgv9c4kmpjhAdyMuQmYQJBANn6pcgvyYaia52dnu+yBUsGkaFfwXkzFSExIbi0MXTkhEb/ER/D
rLytukkUu5S5ecz/KBa8U4xIslZDYQbLz5ECQQCy5dutt7RsxN4+dxCWn0/1FrkWl2G329Ucewm3
QU9CKu4D+7Kqdj+Ha3lXP8F0Etaaapi7+EfkRUpukn2ItZV/AkEAlk+I0iphxT1rCB0Q5CjWDY5S
Df2B5JmdEG5Y2o0nLXwG2w44OLct/k2uD4cEcuITY5Dvi/4BftMCZwm/dnhEgQJACIktJSnJwxLV
o9dchENPtlsCM9C/Sd2EWpqISSUlmfugZbJBwR5pQ5XeMUqKeXZYpP+HEBj1nS+tMH9u2/IGEwJA
fL8mZiZXan/oBKrblAbplNcKWGRVD/3y65042PAEeghahlJMiYquV5DzZajuuT0wbJ5xQuZB01+X
nfpFpBJ2dw==

公钥加密——私钥解密
加密前: abc

解密后: abc
公钥: 

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdOj40yEB48XqWxmPILmJAc7UecIN7F32etSHF
9rwbuEh3+iTPOGSxhoSQpOED0vOb0ZIMkBXZSgsxLaBSin2RZ09YKWRjtpCA0kDkiD11gj4tzTiM
l9qq1kwSK7ZkGAgodEn3yIILVmQDuEImHOXFtulvJ71ka07u3LuwUNdB/wIDAQAB

私钥: 

MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAN06PjTIQHjxepbGY8guYkBztR5w
g3sXfZ61IcX2vBu4SHf6JM84ZLGGhJCk4QPS85vRkgyQFdlKCzEtoFKKfZFnT1gpZGO2kIDSQOSI
PXWCPi3NOIyX2qrWTBIrtmQYCCh0SffIggtWZAO4QiYc5cW26W8nvWRrTu7cu7BQ10H/AgMBAAEC
gYEAz2JWBizjI31bqhP4XiP9PuY5F3vqBW4T+L9cFbQiyumKJc58yzTWUAUGKIIn3enXLG7dNqGr
mbJro4JeFIJ3CiVDpXR9+FluIgI4SXm7ioGKF2NOMA9LR5Fu82W+pLfpTN2y2SaLYWEDZyp53BxY
j9gUxaxi1MQs+C1ZgDF2xmECQQDy70bQntbRfysP+ppCtd56YRnES1Tyekw0wryS2tr+ivQJl7JF
gp5rPAOXpgrq36xHDwUspQ0sJ0vj0O7ywxr1AkEA6SAaLhrJJrYucC0jxwAhUYyaPN+aOsWymaRh
9jA/Wc0wp29SbGTh5CcMuGpXm1g0M+FKW3dGiHgS3rVUKim4owJAbnxgapUzAgiiHxxMeDaavnHW
9C2GrtjsO7qtZOTgYI/1uT8itvZW8lJTF+9OW8/qXE76fXl7ai9dFnl5kzMk2QJBALfHz/vCsArt
mkRiwY6zApE4Z6tPl1V33ymSVovvUzHnOdD1SKQdD5t+UV/crb3QVi8ED0t2B0u0ZSPfDT/D7kMC
QDpwdj9k2F5aokLHBHUNJPFDAp7a5QMaT64gv/d48ITJ68Co+v5WzLMpzJBYXK6PAtqIhxbuPEc2
I2k1Afmrwyw=

私钥加密——公钥解密
加密前: sign

解密后: sign
私钥签名——公钥验证签名
签名:
ud1RsIwmSC1pN22I4IXteg1VD2FbiehKUfNxgVSHzvQNIK+d20FCkHCqh9djP3h94iWnIUY0ifU+
mbJkhAl/i5krExOE0hknOnPMcEP+lZV1RbJI2zG2YooSp2XDleqrQk5e/QF2Mx0Zxt8Xsg7ucVpn
i3wwbYWs9wSzIf0UjlM=

状态:
true



    简要总结一下,使用公钥加密、私钥解密,完成了乙方到甲方的一次数据传递,通过私钥加密、公钥解密,同时通过私钥签名、公钥验证签名,完成了一次甲方到乙方的数据传递与验证,两次数据传递完成一整套的数据交互! 

类似数字签名,数字信封是这样描述的: 

数字信封 
        数字信封用加密技术来保证只有特定的收信人才能阅读信的内容。 
流程: 
    信息发送方采用对称密钥来加密信息,然后再用接收方的公钥来加密此对称密钥(这部分称为数字信封),再将它和信息一起发送给接收方;接收方先用相应的私钥打开数字信封,得到对称密钥,然后使用对称密钥再解开信息。

    接下来我们分析DH加密算法,一种适基于密钥一致协议的加密算法。 
DH 
Diffie- Hellman算法(D-H算法),密钥一致协议。是由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。简单的说就是允许两名用 户在公开媒体上交换信息以生成"一致"的、可以共享的密钥。换句话说,就是由甲方产出一对密钥(公钥、私钥),乙方依照甲方公钥产生乙方密钥对(公钥、私 钥)。以此为基线,作为数据传输保密基础,同时双方使用同一种对称加密算法构建本地密钥(SecretKey)对数据加密。这样,在互通了本地密钥 (SecretKey)算法后,甲乙双方公开自己的公钥,使用对方的公钥和刚才产生的私钥加密数据,同时可以使用对方的公钥和自己的私钥对数据解密。不单 单是甲乙双方两方,可以扩展为多方共享数据通讯,这样就完成了网络交互数据的安全通讯!该算法源于中国的同余定理——中国馀数定理。  

流程分析: 

1.甲方构建密钥对儿,将公钥公布给乙方,将私钥保留;双方约定数据加密算法;乙方通过甲方公钥构建密钥对儿,将公钥公布给甲方,将私钥保留。 
2.甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥加密数据,发送给乙方加密后的数据;乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥对数据解密。 
3.乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥加密数据,发送给甲方加密后的数据;甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥对数据解密。 

  1.  

    各种Java加密算法_第4张图片
  2. 各种Java加密算法_第5张图片

  3. 各种Java加密算法_第6张图片


通过java代码实现如下:Coder类见

001 import java.security.Key;
002 import java.security.KeyFactory;
003 import java.security.KeyPair;
004 import java.security.KeyPairGenerator;
005 import java.security.PublicKey;
006 import java.security.spec.PKCS8EncodedKeySpec;
007 import java.security.spec.X509EncodedKeySpec;
008 import java.util.HashMap;
009 import java.util.Map;
010  
011 import javax.crypto.Cipher;
012 import javax.crypto.KeyAgreement;
013 import javax.crypto.SecretKey;
014 import javax.crypto.interfaces.DHPrivateKey;
015 import javax.crypto.interfaces.DHPublicKey;
016 import javax.crypto.spec.DHParameterSpec;
017  
018 /**
019  * DH安全编码组件
020  
021  * @author 梁栋
022  * @version 1.0
023  * @since 1.0
024  */
025 public abstract class DHCoder extends Coder {
026     public static final String ALGORITHM = "DH";
027  
028     /**
029      * 默认密钥字节数
030      
031      
032      * DH
033      * Default Keysize 1024  
034      * Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive).
035      
036      */
037     private static final int KEY_SIZE = 1024;
038  
039     /**
040      * DH加密下需要一种对称加密算法对数据加密,这里我们使用DES,也可以使用其他对称加密算法。
041      */
042     public static final String SECRET_ALGORITHM = "DES";
043     private static final String PUBLIC_KEY = "DHPublicKey";
044     private static final String PRIVATE_KEY = "DHPrivateKey";
045  
046     /**
047      * 初始化甲方密钥
048      
049      * @return
050      * @throws Exception
051      */
052     public static Map initKey() throws Exception {
053         KeyPairGenerator keyPairGenerator = KeyPairGenerator
054                 .getInstance(ALGORITHM);
055         keyPairGenerator.initialize(KEY_SIZE);
056  
057         KeyPair keyPair = keyPairGenerator.generateKeyPair();
058  
059         // 甲方公钥
060         DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
061  
062         // 甲方私钥
063         DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
064  
065         Map keyMap = new HashMap(2);
066  
067         keyMap.put(PUBLIC_KEY, publicKey);
068         keyMap.put(PRIVATE_KEY, privateKey);
069         return keyMap;
070     }
071  
072     /**
073      * 初始化乙方密钥
074      
075      * @param key
076      *            甲方公钥
077      * @return
078      * @throws Exception
079      */
080     public static Map initKey(String key) throws Exception {
081         // 解析甲方公钥
082         byte[] keyBytes = decryptBASE64(key);
083         X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
084         KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
085         PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
086  
087         // 由甲方公钥构建乙方密钥
088         DHParameterSpec dhParamSpec = ((DHPublicKey) pubKey).getParams();
089  
090         KeyPairGenerator keyPairGenerator = KeyPairGenerator
091                 .getInstance(keyFactory.getAlgorithm());
092         keyPairGenerator.initialize(dhParamSpec);
093  
094         KeyPair keyPair = keyPairGenerator.generateKeyPair();
095  
096         // 乙方公钥
097         DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
098  
099         // 乙方私钥
100         DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
101  
102         Map keyMap = new HashMap(2);
103  
104         keyMap.put(PUBLIC_KEY, publicKey);
105         keyMap.put(PRIVATE_KEY, privateKey);
106  
107         return keyMap;
108     }
109  
110     /**
111      * 加密
112      
113      * @param data
114      *            待加密数据
115      * @param publicKey
116      *            甲方公钥
117      * @param privateKey
118      *            乙方私钥
119      * @return
120      * @throws Exception
121      */
122     public static byte[] encrypt(byte[] data, String publicKey,
123             String privateKey) throws Exception {
124  
125         // 生成本地密钥
126         SecretKey secretKey = getSecretKey(publicKey, privateKey);
127  
128         // 数据加密
129         Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
130         cipher.init(Cipher.ENCRYPT_MODE, secretKey);
131  
132         return cipher.doFinal(data);
133     }
134  
135     /**
136      * 解密
137      
138      * @param data
139      *            待解密数据
140      * @param publicKey
141      *            乙方公钥
142      * @param privateKey
143      *            乙方私钥
144      * @return
145      * @throws Exception
146      */
147     public static byte[] decrypt(byte[] data, String publicKey,
148             String privateKey) throws Exception {
149  
150         // 生成本地密钥
151         SecretKey secretKey = getSecretKey(publicKey, privateKey);
152         // 数据解密
153         Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
154         cipher.init(Cipher.DECRYPT_MODE, secretKey);
155  
156         return cipher.doFinal(data);
157     }
158  
159     /**
160      * 构建密钥
161      
162      * @param publicKey
163      *            公钥
164      * @param privateKey
165      *            私钥
166      * @return
167      * @throws Exception
168      */
169     private static SecretKey getSecretKey(String publicKey, String privateKey)
170             throws Exception {
171         // 初始化公钥
172         byte[] pubKeyBytes = decryptBASE64(publicKey);
173  
174         KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
175         X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKeyBytes);
176         PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
177  
178         // 初始化私钥
179         byte[] priKeyBytes = decryptBASE64(privateKey);
180  
181         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKeyBytes);
182         Key priKey = keyFactory.generatePrivate(pkcs8KeySpec);
183  
184         KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory
185                 .getAlgorithm());
186         keyAgree.init(priKey);
187         keyAgree.doPhase(pubKey, true);
188  
189         // 生成本地密钥
190         SecretKey secretKey = keyAgree.generateSecret(SECRET_ALGORITHM);
191  
192         return secretKey;
193     }
194  
195     /**
196      * 取得私钥
197      
198      * @param keyMap
199      * @return
200      * @throws Exception
201      */
202     public static String getPrivateKey(Map keyMap)
203             throws Exception {
204         Key key = (Key) keyMap.get(PRIVATE_KEY);
205  
206         return encryptBASE64(key.getEncoded());
207     }
208  
209     /**
210      * 取得公钥
211      
212      * @param keyMap
213      * @return
214      * @throws Exception
215      */
216     public static String getPublicKey(Map keyMap)
217             throws Exception {
218         Key key = (Key) keyMap.get(PUBLIC_KEY);
219  
220         return encryptBASE64(key.getEncoded());
221     }
222 }



再给出一个测试类:

01 import static org.junit.Assert.*;
02  
03 import java.util.Map;
04  
05 import org.junit.Test;
06  
07 /**
08  
09  * @author 梁栋
10  * @version 1.0
11  * @since 1.0
12  */
13 public class DHCoderTest {
14  
15     @Test
16     public void test() throws Exception {
17         // 生成甲方密钥对儿
18         Map aKeyMap = DHCoder.initKey();
19         String aPublicKey = DHCoder.getPublicKey(aKeyMap);
20         String aPrivateKey = DHCoder.getPrivateKey(aKeyMap);
21  
22         System.err.println("甲方公钥:\r" + aPublicKey);
23         System.err.println("甲方私钥:\r" + aPrivateKey);
24          
25         // 由甲方公钥产生本地密钥对儿
26         Map bKeyMap = DHCoder.initKey(aPublicKey);
27         String bPublicKey = DHCoder.getPublicKey(bKeyMap);
28         String bPrivateKey = DHCoder.getPrivateKey(bKeyMap);
29          
30         System.err.println("乙方公钥:\r" + bPublicKey);
31         System.err.println("乙方私钥:\r" + bPrivateKey);
32          
33         String aInput = "abc ";
34         System.err.println("原文: " + aInput);
35  
36         // 由甲方公钥,乙方私钥构建密文
37         byte[] aCode = DHCoder.encrypt(aInput.getBytes(), aPublicKey,
38                 bPrivateKey);
39  
40         // 由乙方公钥,甲方私钥解密
41         byte[] aDecode = DHCoder.decrypt(aCode, bPublicKey, aPrivateKey);
42         String aOutput = (new String(aDecode));
43  
44         System.err.println("解密: " + aOutput);
45  
46         assertEquals(aInput, aOutput);
47  
48         System.err.println(" ===============反过来加密解密================== ");
49         String bInput = "def ";
50         System.err.println("原文: " + bInput);
51  
52         // 由乙方公钥,甲方私钥构建密文
53         byte[] bCode = DHCoder.encrypt(bInput.getBytes(), bPublicKey,
54                 aPrivateKey);
55  
56         // 由甲方公钥,乙方私钥解密
57         byte[] bDecode = DHCoder.decrypt(bCode, aPublicKey, bPrivateKey);
58         String bOutput = (new String(bDecode));
59  
60         System.err.println("解密: " + bOutput);
61  
62         assertEquals(bInput, bOutput);
63     }
64  
65 }



控制台输出:

甲方公钥:
MIHfMIGXBgkqhkiG9w0BAwEwgYkCQQD8poLOjhLKuibvzPcRDlJtsHiwXt7LzR60ogjzrhYXrgHz
W5Gkfm32NBPF4S7QiZvNEyrNUNmRUb3EPuc3WS4XAkBnhHGyepz0TukaScUUfbGpqvJE8FpDTWSG
kx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykAgIBgANDAAJAdAWBVmIzqcko
Ej6qFjLDL2+Y3FPq1iRbnOyOpDj71yKaK1K+FhTv04B0zy4DKcvAASV7/Gv0W+bgqdmffRkqrQ==

甲方私钥:
MIHRAgEAMIGXBgkqhkiG9w0BAwEwgYkCQQD8poLOjhLKuibvzPcRDlJtsHiwXt7LzR60ogjzrhYX
rgHzW5Gkfm32NBPF4S7QiZvNEyrNUNmRUb3EPuc3WS4XAkBnhHGyepz0TukaScUUfbGpqvJE8FpD
TWSGkx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykAgIBgAQyAjACJRfy1LyR
eHyD+4Hfb+xR0uoIGR1oL9i9Nk6g2AAuaDPgEVWHn+QXID13yL/uDos=

乙方公钥:
MIHfMIGXBgkqhkiG9w0BAwEwgYkCQQD8poLOjhLKuibvzPcRDlJtsHiwXt7LzR60ogjzrhYXrgHz
W5Gkfm32NBPF4S7QiZvNEyrNUNmRUb3EPuc3WS4XAkBnhHGyepz0TukaScUUfbGpqvJE8FpDTWSG
kx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykAgIBgANDAAJAVEYSfBA+I9nr
dWw3OBv475C+eBrWBBYqt0m6/eu4ptuDQHwV4MmUtKAC2wc2nNrdb1wmBhY1X8RnWkJ1XmdDbQ==

乙方私钥:
MIHSAgEAMIGXBgkqhkiG9w0BAwEwgYkCQQD8poLOjhLKuibvzPcRDlJtsHiwXt7LzR60ogjzrhYX
rgHzW5Gkfm32NBPF4S7QiZvNEyrNUNmRUb3EPuc3WS4XAkBnhHGyepz0TukaScUUfbGpqvJE8FpD
TWSGkx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykAgIBgAQzAjEAqaZiCdXp
2iNpdBlHRaO9ir70wo2n32xNlIzIX19VLSPCDdeUWkgRv4CEj/8k+/yd

原文: abc 
解密: abc 
 ===============反过来加密解密================== 
原文: def 
解密: def



如我所言,甲乙双方在获得对方公钥后可以对发送给对方的数据加密,同时也能对接收到的数据解密,达到了数据安全通信的目的!

    接下来我们介绍DSA数字签名,非对称加密的另一种实现。 
DSA 
DSA-Digital Signature Algorithm 是Schnorr和ElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignature Standard)。简单的说,这是一种更高级的验证方式,用作数字签名。不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签 名。如果数据和签名不匹配则认为验证失败!数字签名的作用就是校验数据在传输过程中不被修改。数字签名,是单向加密的升级! 

  1. 各种Java加密算法_第7张图片

  2. 各种Java加密算法_第8张图片


通过java代码实现如下:Coder类见

001 import java.security.Key;
002 import java.security.KeyFactory;
003 import java.security.KeyPair;
004 import java.security.KeyPairGenerator;
005 import java.security.PrivateKey;
006 import java.security.PublicKey;
007 import java.security.SecureRandom;
008 import java.security.Signature;
009 import java.security.interfaces.DSAPrivateKey;
010 import java.security.interfaces.DSAPublicKey;
011 import java.security.spec.PKCS8EncodedKeySpec;
012 import java.security.spec.X509EncodedKeySpec;
013 import java.util.HashMap;
014 import java.util.Map;
015  
016 /**
017  * DSA安全编码组件
018  
019  * @author 梁栋
020  * @version 1.0
021  * @since 1.0
022  */
023 public abstract class DSACoder extends Coder {
024  
025     public static final String ALGORITHM = "DSA";
026  
027     /**
028      * 默认密钥字节数
029      
030      
031      * DSA 
032      * Default Keysize 1024  
033      * Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive).
034      
035      */
036     private static final int KEY_SIZE = 1024;
037  
038     /**
039      * 默认种子
040      */
041     private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";
042  
043     private static final String PUBLIC_KEY = "DSAPublicKey";
044     private static final String PRIVATE_KEY = "DSAPrivateKey";
045  
046     /**
047      * 用私钥对信息生成数字签名
048      
049      * @param data
050      *            加密数据
051      * @param privateKey
052      *            私钥
053      
054      * @return
055      * @throws Exception
056      */
057     public static String sign(byte[] data, String privateKey) throws Exception {
058         // 解密由base64编码的私钥
059         byte[] keyBytes = decryptBASE64(privateKey);
060  
061         // 构造PKCS8EncodedKeySpec对象
062         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
063  
064         // KEY_ALGORITHM 指定的加密算法
065         KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
066  
067         // 取私钥匙对象
068         PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
069  
070         // 用私钥对信息生成数字签名
071         Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
072         signature.initSign(priKey);
073         signature.update(data);
074  
075         return encryptBASE64(signature.sign());
076     }
077  
078     /**
079      * 校验数字签名
080      
081      * @param data
082      *            加密数据
083      * @param publicKey
084      *            公钥
085      * @param sign
086      *            数字签名
087      
088      * @return 校验成功返回true 失败返回false
089      * @throws Exception
090      
091      */
092     public static boolean verify(byte[] data, String publicKey, String sign)
093             throws Exception {
094  
095         // 解密由base64编码的公钥
096         byte[] keyBytes = decryptBASE64(publicKey);
097  
098         // 构造X509EncodedKeySpec对象
099         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
100  
101         // ALGORITHM 指定的加密算法
102         KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
103  
104         // 取公钥匙对象
105         PublicKey pubKey = keyFactory.generatePublic(keySpec);
106  
107         Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
108         signature.initVerify(pubKey);
109         signature.update(data);
110  
111         // 验证签名是否正常
112         return signature.verify(decryptBASE64(sign));
113     }
114  
115     /**
116      * 生成密钥
117      
118      * @param seed
119      *            种子
120      * @return 密钥对象
121      * @throws Exception
122      */
123     public static Map initKey(String seed) throws Exception {
124         KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);
125         // 初始化随机产生器
126         SecureRandom secureRandom = new SecureRandom();
127         secureRandom.setSeed(seed.getBytes());
128         keygen.initialize(KEY_SIZE, secureRandom);
129  
130         KeyPair keys = keygen.genKeyPair();
131  
132         DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();
133         DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();
134  
135         Map map = new HashMap(2);
136         map.put(PUBLIC_KEY, publicKey);
137         map.put(PRIVATE_KEY, privateKey);
138  
139         return map;
140     }
141  
142     /**
143      * 默认生成密钥
144      
145      * @return 密钥对象
146      * @throws Exception
147      */
148     public static Map initKey() throws Exception {
149         return initKey(DEFAULT_SEED);
150     }
151  
152     /**
153      * 取得私钥
154      
155      * @param keyMap
156      * @return
157      * @throws Exception
158      */
159     public static String getPrivateKey(Map keyMap)
160             throws Exception {
161         Key key = (Key) keyMap.get(PRIVATE_KEY);
162  
163         return encryptBASE64(key.getEncoded());
164     }
165  
166     /**
167      * 取得公钥
168      
169      * @param keyMap
170      * @return
171      * @throws Exception
172      */
173     public static String getPublicKey(Map keyMap)
174             throws Exception {
175         Key key = (Key) keyMap.get(PUBLIC_KEY);
176  
177         return encryptBASE64(key.getEncoded());
178     }
179 }



再给出一个测试类:

01 import static org.junit.Assert.*;
02  
03 import java.util.Map;
04  
05 import org.junit.Test;
06  
07 /**
08  
09  * @author 梁栋
10  * @version 1.0
11  * @since 1.0
12  */
13 public class DSACoderTest {
14  
15     @Test
16     public void test() throws Exception {
17         String inputStr = "abc";
18         byte[] data = inputStr.getBytes();
19  
20         // 构建密钥
21         Map keyMap = DSACoder.initKey();
22  
23         // 获得密钥
24         String publicKey = DSACoder.getPublicKey(keyMap);
25         String privateKey = DSACoder.getPrivateKey(keyMap);
26  
27         System.err.println("公钥:\r" + publicKey);
28         System.err.println("私钥:\r" + privateKey);
29  
30         // 产生签名
31         String sign = DSACoder.sign(data, privateKey);
32         System.err.println("签名:\r" + sign);
33  
34         // 验证签名
35         boolean status = DSACoder.verify(data, publicKey, sign);
36         System.err.println("状态:\r" + status);
37         assertTrue(status);
38  
39     }
40  
41 }


控制台输出:

公钥:
MIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZp
RV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fn
xqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuE
C/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJ
FnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImo
g9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGAIu4RUlcQLp49PI0MrbssOY+3uySVnp0TULSv
5T4VaHoKzsLHgGTrwOvsGA+V3yCNl2WDu3D84bSLF7liTWgOj+SMOEaPk4VyRTlLXZWGPsf1Mfd9
21XAbMeVyKDSHHVGbMjBScajf3bXooYQMlyoHiOt/WrCo+mv7efstMM0PGo=

私钥:
MIIBTAIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2
USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4
O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmC
ouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCB
gLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhR
kImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFwIVAIegLUtmm2oQKQJTOiLugHTSjl/q

签名:
MC0CFQCMg0J/uZmF8GuRpr3TNq48w60nDwIUJCyYNah+HtbU6NcQfy8Ac6LeLQs=

状态:
true



注意状态为true,就验证成功!

ECC 
ECC-Elliptic Curves Cryptography,椭圆曲线密码编码学,是目前已知的公钥体制中,对每比特所提供加密强度最高的一种体制。在软件注册保护方面起到很大的作用,一般的序列号通常由该算法产生。 
    当我开始整理《Java加密技术(二)》的时候,我就已经在开始研究ECC了,但是关于Java实现ECC算法的资料实在是太少了,无论是国内还是国外的 资料,无论是官方还是非官方的解释,最终只有一种答案——ECC算法在jdk1.5后加入支持,目前仅仅只能完成密钥的生成与解析。 如果想要获得ECC算法实现,需要调用硬件完成加密/解密(ECC算法相当耗费资源,如果单纯使用CPU进行加密/解密,效率低下),涉及到Java Card领域,PKCS#11。 其实,PKCS#11配置很简单,但缺乏硬件设备,无法尝试! 

    尽管如此,我照旧提供相应的Java实现代码,以供大家参考。 

通过java代码实现如下:Coder类见

001 import java.math.BigInteger;
002 import java.security.Key;
003 import java.security.KeyFactory;
004 import java.security.interfaces.ECPrivateKey;
005 import java.security.interfaces.ECPublicKey;
006 import java.security.spec.ECFieldF2m;
007 import java.security.spec.ECParameterSpec;
008 import java.security.spec.ECPoint;
009 import java.security.spec.ECPrivateKeySpec;
010 import java.security.spec.ECPublicKeySpec;
011 import java.security.spec.EllipticCurve;
012 import java.security.spec.PKCS8EncodedKeySpec;
013 import java.security.spec.X509EncodedKeySpec;
014 import java.util.HashMap;
015 import java.util.Map;
016  
017 import javax.crypto.Cipher;
018 import javax.crypto.NullCipher;
019  
020 import sun.security.ec.ECKeyFactory;
021 import sun.security.ec.ECPrivateKeyImpl;
022 import sun.security.ec.ECPublicKeyImpl;
023  
024 /**
025  * ECC安全编码组件
026  
027  * @author 梁栋
028  * @version 1.0
029  * @since 1.0
030  */
031 public abstract class ECCCoder extends Coder {
032  
033     public static final String ALGORITHM = "EC";
034     private static final String PUBLIC_KEY = "ECCPublicKey";
035     private static final String PRIVATE_KEY = "ECCPrivateKey";
036  
037     /**
038      * 解密
039      * 用私钥解密
040      
041      * @param data
042      * @param key
043      * @return
044      * @throws Exception
045      */
046     public static byte[] decrypt(byte[] data, String key) throws Exception {
047         // 对密钥解密
048         byte[] keyBytes = decryptBASE64(key);
049  
050         // 取得私钥
051         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
052         KeyFactory keyFactory = ECKeyFactory.INSTANCE;
053  
054         ECPrivateKey priKey = (ECPrivateKey) keyFactory
055                 .generatePrivate(pkcs8KeySpec);
056  
057         ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(priKey.getS(),
058                 priKey.getParams());
059  
060         // 对数据解密
061         // TODO Chipher不支持EC算法 未能实现
062         Cipher cipher = new NullCipher();
063         // Cipher.getInstance(ALGORITHM, keyFactory.getProvider());
064         cipher.init(Cipher.DECRYPT_MODE, priKey, ecPrivateKeySpec.getParams());
065  
066         return cipher.doFinal(data);
067     }
068  
069     /**
070      * 加密
071      * 用公钥加密
072      
073      * @param data
074      * @param privateKey
075      * @return
076      * @throws Exception
077      */
078     public static byte[] encrypt(byte[] data, String privateKey)
079             throws Exception {
080         // 对公钥解密
081         byte[] keyBytes = decryptBASE64(privateKey);
082  
083         // 取得公钥
084         X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
085         KeyFactory keyFactory = ECKeyFactory.INSTANCE;
086  
087         ECPublicKey pubKey = (ECPublicKey) keyFactory
088                 .generatePublic(x509KeySpec);
089  
090         ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(pubKey.getW(),
091                 pubKey.getParams());
092  
093         // 对数据加密
094         // TODO Chipher不支持EC算法 未能实现
095         Cipher cipher = new NullCipher();
096         // Cipher.getInstance(ALGORITHM, keyFactory.getProvider());
097         cipher.init(Cipher.ENCRYPT_MODE, pubKey, ecPublicKeySpec.getParams());
098  
099         return cipher.doFinal(data);
100     }
101  
102     /**
103      * 取得私钥
104      
105      * @param keyMap
106      * @return
107      * @throws Exception
108      */
109     public static String getPrivateKey(Map keyMap)
110             throws Exception {
111         Key key = (Key) keyMap.get(PRIVATE_KEY);
112  
113         return encryptBASE64(key.getEncoded());
114     }
115  
116     /**
117      * 取得公钥
118      
119      * @param keyMap
120      * @return
121      * @throws Exception
122      */
123     public static String getPublicKey(Map keyMap)
124             throws Exception {
125         Key key = (Key) keyMap.get(PUBLIC_KEY);
126  
127         return encryptBASE64(key.getEncoded());
128     }
129  
130     /**
131      * 初始化密钥
132      
133      * @return
134      * @throws Exception
135      */
136     public static Map initKey() throws Exception {
137         BigInteger x1 = new BigInteger(
138                 "2fe13c0537bbc11acaa07d793de4e6d5e5c94eee8"16);
139         BigInteger x2 = new BigInteger(
140                 "289070fb05d38ff58321f2e800536d538ccdaa3d9"16);
141  
142         ECPoint g = new ECPoint(x1, x2);
143  
144         // the order of generator
145         BigInteger n = new BigInteger(
146                 "5846006549323611672814741753598448348329118574063"10);
147         // the cofactor
148         int h = 2;
149         int m = 163;
150         int[] ks = { 763 };
151         ECFieldF2m ecField = new ECFieldF2m(m, ks);
152         // y^2+xy=x^3+x^2+1
153         BigInteger a = new BigInteger("1"2);
154         BigInteger b = new BigInteger("1"2);
155  
156         EllipticCurve ellipticCurve = new EllipticCurve(ecField, a, b);
157  
158         ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, g,
159                 n, h);
160         // 公钥
161         ECPublicKey publicKey = new ECPublicKeyImpl(g, ecParameterSpec);
162  
163         BigInteger s = new BigInteger(
164                 "1234006549323611672814741753598448348329118574063"10);
165         // 私钥
166         ECPrivateKey privateKey = new ECPrivateKeyImpl(s, ecParameterSpec);
167  
168         Map keyMap = new HashMap(2);
169  
170         keyMap.put(PUBLIC_KEY, publicKey);
171         keyMap.put(PRIVATE_KEY, privateKey);
172  
173         return keyMap;
174     }
175  
176 }



    请注意上述代码中的TODO内容,再次提醒注意,Chipher不支持EC算法 ,以上代码仅供参考。Chipher、Signature、KeyPairGenerator、KeyAgreement、SecretKey均不支持EC算法。为了确保程序能够正常执行,我们使用了NullCipher类,验证程序。

照旧提供一个测试类:

01 import static org.junit.Assert.*;
02  
03 import java.math.BigInteger;
04 import java.security.spec.ECFieldF2m;
05 import java.security.spec.ECParameterSpec;
06 import java.security.spec.ECPoint;
07 import java.security.spec.ECPrivateKeySpec;
08 import java.security.spec.ECPublicKeySpec;
09 import java.security.spec.EllipticCurve;
10 import java.util.Map;
11  
12 import org.junit.Test;
13  
14 /**
15  
16  * @author 梁栋
17  * @version 1.0
18  * @since 1.0
19  */
20 public class ECCCoderTest {
21  
22     @Test
23     public void test() throws Exception {
24         String inputStr = "abc";
25         byte[] data = inputStr.getBytes();
26  
27         Map keyMap = ECCCoder.initKey();
28  
29         String publicKey = ECCCoder.getPublicKey(keyMap);
30         String privateKey = ECCCoder.getPrivateKey(keyMap);
31         System.err.println("公钥: \n" + publicKey);
32         System.err.println("私钥: \n" + privateKey);
33  
34         byte[] encodedData = ECCCoder.encrypt(data, publicKey);
35  
36         byte[] decodedData = ECCCoder.decrypt(encodedData, privateKey);
37  
38         String outputStr = new String(decodedData);
39         System.err.println("加密前: " + inputStr + "\n\r" "解密后: " + outputStr);
40         assertEquals(inputStr, outputStr);
41     }
42 }



控制台输出:

公钥: 
MEAwEAYHKoZIzj0CAQYFK4EEAAEDLAAEAv4TwFN7vBGsqgfXk95ObV5clO7oAokHD7BdOP9YMh8u
gAU21TjM2qPZ

私钥: 
MDICAQAwEAYHKoZIzj0CAQYFK4EEAAEEGzAZAgEBBBTYJsR3BN7TFw7JHcAHFkwNmfil7w==

加密前: abc

解密后: abc

    本篇的主要内容为Java证书体系的实现。 

在构建Java代码实现前,我们需要完成证书的制作。 
1.生成keyStroe文件 
在命令行下执行以下命令:

keytool -genkey -validity 36000 -alias www.zlex.org -keyalg RSA -keystore d:\zlex.keystore



其中 
-genkey表示生成密钥 
-validity指定证书有效期,这里是36000天 
-alias指定别名,这里是www.zlex.org 
-keyalg
指定算法,这里是RSA 
-keystore
指定存储位置,这里是d:\zlex.keystore 

在这里我使用的密码为 123456 

控制台输出:

输入keystore密码:
再次输入新密码:
您的名字与姓氏是什么?
  [Unknown]:  www.zlex.org
您的组织单位名称是什么?
  [Unknown]:  zlex
您的组织名称是什么?
  [Unknown]:  zlex
您所在的城市或区域名称是什么?
  [Unknown]:  BJ
您所在的州或省份名称是什么?
  [Unknown]:  BJ
该单位的两字母国家代码是什么
  [Unknown]:  CN
CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 正确吗?
  [否]:  Y

输入的主密码
        (如果和 keystore 密码相同,按回车):
再次输入新密码:


这时,在D盘下会生成一个zlex.keystore的文件。 

2.生成自签名证书 
光有keyStore文件是不够的,还需要证书文件,证书才是直接提供给外界使用的公钥凭证。 
导出证书:

keytool -export -keystore d:\zlex.keystore -alias www.zlex.org -file d:\zlex.cer -rfc



其中 
-export指定为导出操作 
-keystore指定keystore文件 
-alias
指定导出keystore文件中的别名 
-file
指向导出路径 
-rfc
以文本格式输出,也就是以BASE64编码输出 
这里的密码是 123456 

控制台输出:

输入keystore密码:
保存在文件中的认证 



当然,使用方是需要导入证书的! 
可以通过自签名证书完成CAS单点登录系统的构建! 

Ok,准备工作完成,开始Java实现! 

通过java代码实现如下:Coder类见

001 import java.io.FileInputStream;
002 import java.security.KeyStore;
003 import java.security.PrivateKey;
004 import java.security.PublicKey;
005 import java.security.Signature;
006 import java.security.cert.Certificate;
007 import java.security.cert.CertificateFactory;
008 import java.security.cert.X509Certificate;
009 import java.util.Date;
010  
011 import javax.crypto.Cipher;
012  
013 /**
014  * 证书组件
015  
016  * @author 梁栋
017  * @version 1.0
018  * @since 1.0
019  */
020 public abstract class CertificateCoder extends Coder {
021  
022  
023     /**
024      * Java密钥库(Java Key Store,JKS)KEY_STORE
025      */
026     public static final String KEY_STORE = "JKS";
027  
028     public static final String X509 = "X.509";
029  
030     /**
031      * 由KeyStore获得私钥
032      
033      * @param keyStorePath
034      * @param alias
035      * @param password
036      * @return
037      * @throws Exception
038      */
039     private static PrivateKey getPrivateKey(String keyStorePath, String alias,
040             String password) throws Exception {
041         KeyStore ks = getKeyStore(keyStorePath, password);
042         PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
043         return key;
044     }
045  
046     /**
047      * 由Certificate获得公钥
048      
049      * @param certificatePath
050      * @return
051      * @throws Exception
052      */
053     private static PublicKey getPublicKey(String certificatePath)
054             throws Exception {
055         Certificate certificate = getCertificate(certificatePath);
056         PublicKey key = certificate.getPublicKey();
057         return key;
058     }
059  
060     /**
061      * 获得Certificate
062      
063      * @param certificatePath
064      * @return
065      * @throws Exception
066      */
067     private static Certificate getCertificate(String certificatePath)
068             throws Exception {
069         CertificateFactory certificateFactory = CertificateFactory
070                 .getInstance(X509);
071         FileInputStream in = new FileInputStream(certificatePath);
072  
073         Certificate certificate = certificateFactory.generateCertificate(in);
074         in.close();
075  
076         return certificate;
077     }
078  
079     /**
080      * 获得Certificate
081      
082      * @param keyStorePath
083      * @param alias
084      * @param password
085      * @return
086      * @throws Exception
087      */
088     private static Certificate getCertificate(String keyStorePath,
089             String alias, String password) throws Exception {
090         KeyStore ks = getKeyStore(keyStorePath, password);
091         Certificate certificate = ks.getCertificate(alias);
092  
093         return certificate;
094     }
095  
096     /**
097      * 获得KeyStore
098      
099      * @param keyStorePath
100      * @param password
101      * @return
102      * @throws Exception
103      */
104     private static KeyStore getKeyStore(String keyStorePath, String password)
105             throws Exception {
106         FileInputStream is = new FileInputStream(keyStorePath);
107         KeyStore ks = KeyStore.getInstance(KEY_STORE);
108         ks.load(is, password.toCharArray());
109         is.close();
110         return ks;
111     }
112  
113     /**
114      * 私钥加密
115      
116      * @param data
117      * @param keyStorePath
118      * @param alias
119      * @param password
120      * @return
121      * @throws Exception
122      */
123     public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,
124             String alias, String password) throws Exception {
125         // 取得私钥
126         PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
127  
128         // 对数据加密
129         Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
130         cipher.init(Cipher.ENCRYPT_MODE, privateKey);
131  
132         return cipher.doFinal(data);
133  
134     }
135  
136     /**
137      * 私钥解密
138      
139      * @param data
140      * @param keyStorePath
141      * @param alias
142      * @param password
143      * @return
144      * @throws Exception
145      */
146     public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,
147             String alias, String password) throws Exception {
148         // 取得私钥
149         PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
150  
151         // 对数据加密
152         Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
153         cipher.init(Cipher.DECRYPT_MODE, privateKey);
154  
155         return cipher.doFinal(data);
156  
157     }
158  
159     /**
160      * 公钥加密
161      
162      * @param data
163      * @param certificatePath
164      * @return
165      * @throws Exception
166      */
167     public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
168             throws Exception {
169  
170         // 取得公钥
171         PublicKey publicKey = getPublicKey(certificatePath);
172         // 对数据加密
173         Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
174         cipher.init(Cipher.ENCRYPT_MODE, publicKey);
175  
176         return cipher.doFinal(data);
177  
178     }
179  
180     /**
181      * 公钥解密
182      
183      * @param data
184      * @param certificatePath
185      * @return
186      * @throws Exception
187      */
188     public static byte[] decryptByPublicKey(byte[] data, String certificatePath)
189             throws Exception {
190         // 取得公钥
191         PublicKey publicKey = getPublicKey(certificatePath);
192  
193         // 对数据加密
194         Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
195         cipher.init(Cipher.DECRYPT_MODE, publicKey);
196  
197         return cipher.doFinal(data);
198  
199     }
200  
201     /**
202      * 验证Certificate
203      
204      * @param certificatePath
205      * @return
206      */
207     public static boolean verifyCertificate(String certificatePath) {
208         return verifyCertificate(new Date(), certificatePath);
209     }
210  
211     /**
212      * 验证Certificate是否过期或无效
213      
214      * @param date
215      * @param certificatePath
216      * @return
217      */
218     public static boolean verifyCertificate(Date date, String certificatePath) {
219         boolean status = true;
220         try {
221             // 取得证书
222             Certificate certificate = getCertificate(certificatePath);
223             // 验证证书是否过期或无效
224             status = verifyCertificate(date, certificate);
225         catch (Exception e) {
226             status = false;
227         }
228         return status;
229     }
230  
231     /**
232      * 验证证书是否过期或无效
233      
234      * @param date
235      * @param certificate
236      * @return
237      */
238     private static boolean verifyCertificate(Date date, Certificate certificate) {
239         boolean status = true;
240         try {
241             X509Certificate x509Certificate = (X509Certificate) certificate;
242             x509Certificate.checkValidity(date);
243         catch (Exception e) {
244             status = false;
245         }
246         return status;
247     }
248  
249     /**
250      * 签名
251      
252      * @param keyStorePath
253      * @param alias
254      * @param password
255      
256      * @return
257      * @throws Exception
258      */
259     public static String sign(byte[] sign, String keyStorePath, String alias,
260             String password) throws Exception {
261         // 获得证书
262         X509Certificate x509Certificate = (X509Certificate) getCertificate(
263                 keyStorePath, alias, password);
264         // 获取私钥
265         KeyStore ks = getKeyStore(keyStorePath, password);
266         // 取得私钥
267         PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password
268                 .toCharArray());
269  
270         // 构建签名
271         Signature signature = Signature.getInstance(x509Certificate
272                 .getSigAlgName());
273         signature.initSign(privateKey);
274         signature.update(sign);
275         return encryptBASE64(signature.sign());
276     }
277  
278     /**
279      * 验证签名
280      
281      * @param data
282      * @param sign
283      * @param certificatePath
284      * @return
285      * @throws Exception
286      */
287     public static boolean verify(byte[] data, String sign,
288             String certificatePath) throws Exception {
289         // 获得证书
290         X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
291         // 获得公钥
292         PublicKey publicKey = x509Certificate.getPublicKey();
293         // 构建签名
294         Signature signature = Signature.getInstance(x509Certificate
295                 .getSigAlgName());
296         signature.initVerify(publicKey);
297         signature.update(data);
298  
299         return signature.verify(decryptBASE64(sign));
300  
301     }
302  
303     /**
304      * 验证Certificate
305      
306      * @param keyStorePath
307      * @param alias
308      * @param password
309      * @return
310      */
311     public static boolean verifyCertificate(Date date, String keyStorePath,
312             String alias, String password) {
313         boolean status = true;
314         try {
315             Certificate certificate = getCertificate(keyStorePath, alias,
316                     password);
317             status = verifyCertificate(date, certificate);
318         catch (Exception e) {
319             status = false;
320         }
321         return status;
322     }
323  
324     /**
325      * 验证Certificate
326      
327      * @param keyStorePath
328      * @param alias
329      * @param password
330      * @return
331      */
332     public static boolean verifyCertificate(String keyStorePath, String alias,
333             String password) {
334         return verifyCertificate(new Date(), keyStorePath, alias, password);
335     }
336 }



再给出一个测试类:

01 import static org.junit.Assert.*;
02  
03 import org.junit.Test;
04  
05 /**
06  
07  * @author 梁栋
08  * @version 1.0
09  * @since 1.0
10  */
11 public class CertificateCoderTest {
12     private String password = "123456";
13     private String alias = "www.zlex.org";
14     private String certificatePath = "d:/zlex.cer";
15     private String keyStorePath = "d:/zlex.keystore";
16  
17     @Test
18     public void test() throws Exception {
19         System.err.println("公钥加密——私钥解密");
20         String inputStr = "Ceritifcate";
21         byte[] data = inputStr.getBytes();
22  
23         byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
24                 certificatePath);
25  
26         byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
27                 keyStorePath, alias, password);
28         String outputStr = new String(decrypt);
29  
30         System.err.println("加密前: " + inputStr + "\n\r" "解密后: " + outputStr);
31  
32         // 验证数据一致
33         assertArrayEquals(data, decrypt);
34  
35         // 验证证书有效
36         assertTrue(CertificateCoder.verifyCertificate(certificatePath));
37  
38     }
39  
40     @Test
41     public void testSign() throws Exception {
42         System.err.println("私钥加密——公钥解密");
43  
44         String inputStr = "sign";
45         byte[] data = inputStr.getBytes();
46  
47         byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
48                 keyStorePath, alias, password);
49  
50         byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
51                 certificatePath);
52  
53         String outputStr = new String(decodedData);
54         System.err.println("加密前: " + inputStr + "\n\r" "解密后: " + outputStr);
55         assertEquals(inputStr, outputStr);
56  
57         System.err.println("私钥签名——公钥验证签名");
58         // 产生签名
59         String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,
60                 password);
61         System.err.println("签名:\r" + sign);
62  
63         // 验证签名
64         boolean status = CertificateCoder.verify(encodedData, sign,
65                 certificatePath);
66         System.err.println("状态:\r" + status);
67         assertTrue(status);
68  
69     }
70 }



控制台输出:

公钥加密——私钥解密
加密前: Ceritificate

解密后: Ceritificate

私钥加密——公钥解密
加密前: sign

解密后: sign
私钥签名——公钥验证签名
签名:
pqBn5m6PJlfOjH0A6U2o2mUmBsfgyEY1NWCbiyA/I5Gc3gaVNVIdj/zkGNZRqTjhf3+J9a9z9EI7
6F2eWYd7punHx5oh6hfNgcKbVb52EfItl4QEN+djbXiPynn07+Lbg1NOjULnpEd6ZhLP1YwrEAuM
OfvX0e7/wplxLbySaKQ=

状态:
true



由此完成了证书验证体系! 

同样,我们可以对代码做签名——代码签名! 
通过工具JarSigner可以完成代码签名。 
这里我们对tools.jar做代码签名,命令如下:

jarsigner -storetype jks -keystore zlex.keystore -verbose tools.jar www.zlex.org


控制台输出:

输入密钥库的口令短语:
 正在更新: META-INF/WWW_ZLEX.SF
 正在更新: META-INF/WWW_ZLEX.RSA
  正在签名: org/zlex/security/Security.class
  正在签名: org/zlex/tool/Main$1.class
  正在签名: org/zlex/tool/Main$2.class
  正在签名: org/zlex/tool/Main.class

警告:
签名者证书将在六个月内过期。



此时,我们可以对签名后的jar做验证! 
验证tools.jar,命令如下:

jarsigner -verify -verbose -certs tools.jar


控制台输出:

         402 Sat Jun 20 16:25:14 CST 2009 META-INF/MANIFEST.MF
         532 Sat Jun 20 16:25:14 CST 2009 META-INF/WWW_ZLEX.SF
         889 Sat Jun 20 16:25:14 CST 2009 META-INF/WWW_ZLEX.RSA
sm       590 Wed Dec 10 13:03:42 CST 2008 org/zlex/security/Security.class

      X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
      [证书将在 09-9-18 下午3:27 到期]

sm       705 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main$1.class

      X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
      [证书将在 09-9-18 下午3:27 到期]

sm       779 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main$2.class

      X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
      [证书将在 09-9-18 下午3:27 到期]

sm     12672 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main.class

      X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
      [证书将在 09-9-18 下午3:27 到期]


  s = 已验证签名
  m = 在清单中列出条目
  k = 在密钥库中至少找到了一个证书
  i = 在身份作用域内至少找到了一个证书

jar 已验证。

警告:
此 jar 包含签名者证书将在六个月内过期的条目。



代码签名认证的用途主要是对发布的软件做验证,支持 Sun Java .jar (Java Applet) 文件(J2SE)和 J2ME MIDlet Suite 文件。 

    在中,我们模拟了一个基于RSA非对称加密网络的安全通信。现在我们深度了解一下现有的安全网络通信——SSL。 
    我们需要构建一个由CA机构签发的有效证书,这里我们使用上文中生成的自签名证书zlex.cer 
    这里,我们将证书导入到我们的密钥库。 

keytool -import -alias www.zlex.org -file d:/zlex.cer -keystore d:/zlex.keystore



其中 
-import表示导入 
-alias指定别名,这里是www.zlex.org 
-file指定算法,这里是d:/zlex.cer 
-keystore指定存储位置,这里是d:/zlex.keystore 
在这里我使用的密码为654321 

控制台输出:

输入keystore密码:
再次输入新密码:
所有者:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
签发人:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
序列号:4a1e48df
有效期: Thu May 28 16:18:39 CST 2009 至Wed Aug 26 16:18:39 CST 2009
证书指纹:
         MD5:19:CA:E6:36:E2:DF:AD:96:31:97:2F:A9:AD:FC:37:6A
         SHA1:49:88:30:59:29:45:F1:69:CA:97:A9:6D:8A:CF:08:D2:C3:D5:C0:C4
         签名算法名称:SHA1withRSA
         版本: 3
信任这个认证? [否]:  y
认证已添加至keystore中



OK,最复杂的准备工作已经完成。 
接下来我们将域名www.zlex.org定位到本机上。打开C:\Windows\System32\drivers\etc\hosts文件,将www.zlex.org绑定在本机上。在文件末尾追加127.0.0.1       www.zlex.org。现在通过地址栏访问http://www.zlex.org,或者通过ping命令,如果能够定位到本机,域名映射就搞定了。 
现在,配置tomcat。先将zlex.keystore拷贝到tomcat的conf目录下,然后配置server.xml。将如下内容加入配置文件


注意clientAuth="false"测试阶段,置为false,正式使用时建议使用true。现在启动tomcat,访问https://www.zlex.org/。各种Java加密算法 
显然,证书未能通过认证,这个时候你可以选择安装证书(上文中的zlex.cer文件就是证书),作为受信任的根证书颁发机构导入,再次重启浏览器(IE,其他浏览器对于域名www.zlex.org不支持本地方式访问),访问https://www.zlex.org/,你会看到地址栏中会有个小锁各种Java加密算法,就说明安装成功。所有的浏览器联网操作已经在RSA加密解密系统的保护之下了。但似乎我们感受不到。 
这个时候很多人开始怀疑,如果我们要手工做一个这样的https的访问是不是需要把浏览器的这些个功能都实现呢?不需要! 

接着上篇内容,给出如下代码实现:

001 import java.io.FileInputStream;
002 import java.security.KeyStore;
003 import java.security.PrivateKey;
004 import java.security.PublicKey;
005 import java.security.Signature;
006 import java.security.cert.Certificate;
007 import java.security.cert.CertificateFactory;
008 import java.security.cert.X509Certificate;
009 import java.util.Date;
010  
011 import javax.crypto.Cipher;
012 import javax.net.ssl.HttpsURLConnection;
013 import javax.net.ssl.KeyManagerFactory;
014 import javax.net.ssl.SSLContext;
015 import javax.net.ssl.SSLSocketFactory;
016 import javax.net.ssl.TrustManagerFactory;
017  
018 /**
019  * 证书组件
020  
021  * @author 梁栋
022  * @version 1.0
023  * @since 1.0
024  */
025 public abstract class CertificateCoder extends Coder {
026  
027     /**
028      * Java密钥库(Java Key Store,JKS)KEY_STORE
029      */
030     public static final String KEY_STORE = "JKS";
031  
032     public static final String X509 = "X.509";
033     public static final String SunX509 = "SunX509";
034     public static final String SSL = "SSL";
035  
036     /**
037      * 由KeyStore获得私钥
038      
039      * @param keyStorePath
040      * @param alias
041      * @param password
042      * @return
043      * @throws Exception
044      */
045     private static PrivateKey getPrivateKey(String keyStorePath, String alias,
046             String password) throws Exception {
047         KeyStore ks = getKeyStore(keyStorePath, password);
048         PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
049         return key;
050     }
051  
052     /**
053      * 由Certificate获得公钥
054      
055      * @param certificatePath
056      * @return
057      * @throws Exception
058      */
059     private static PublicKey getPublicKey(String certificatePath)
060             throws Exception {
061         Certificate certificate = getCertificate(certificatePath);
062         PublicKey key = certificate.getPublicKey();
063         return key;
064     }
065  
066     /**
067      * 获得Certificate
068      
069      * @param certificatePath
070      * @return
071      * @throws Exception
072      */
073     private static Certificate getCertificate(String certificatePath)
074             throws Exception {
075         CertificateFactory certificateFactory = CertificateFactory
076                 .getInstance(X509);
077         FileInputStream in = new FileInputStream(certificatePath);
078  
079         Certificate certificate = certificateFactory.generateCertificate(in);
080         in.close();
081  
082         return certificate;
083     }
084  
085     /**
086      * 获得Certificate
087      
088      * @param keyStorePath
089      * @param alias
090      * @param password
091      * @return
092      * @throws Exception
093      */
094     private static Certificate getCertificate(String keyStorePath,
095             String alias, String password) throws Exception {
096         KeyStore ks = getKeyStore(keyStorePath, password);
097         Certificate certificate = ks.getCertificate(alias);
098  
099         return certificate;
100     }
101  
102     /**
103      * 获得KeyStore
104      
105      * @param keyStorePath
106      * @param password
107      * @return
108      * @throws Exception
109      */
110     private static KeyStore getKeyStore(String keyStorePath, String password)
111             throws Exception {
112         FileInputStream is = new FileInputStream(keyStorePath);
113         KeyStore ks = KeyStore.getInstance(KEY_STORE);
114         ks.load(is, password.toCharArray());
115         is.close();
116         return ks;
117     }
118  
119     /**
120      * 私钥加密
121      
122      * @param data
123      * @param keyStorePath
124      * @param alias
125      * @param password
126      * @return
127      * @throws Exception
128      */
129     public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,
130             String alias, String password) throws Exception {
131         // 取得私钥
132         PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
133  
134         // 对数据加密
135         Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
136         cipher.init(Cipher.ENCRYPT_MODE, privateKey);
137  
138         return cipher.doFinal(data);
139  
140     }
141  
142     /**
143      * 私钥解密
144      
145      * @param data
146      * @param keyStorePath
147      * @param alias
148      * @param password
149      * @return
150      * @throws Exception
151      */
152     public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,
153             String alias, String password) throws Exception {
154         // 取得私钥
155         PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
156  
157         // 对数据加密
158         Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
159         cipher.init(Cipher.DECRYPT_MODE, privateKey);
160  
161         return cipher.doFinal(data);
162  
163     }
164  
165     /**
166      * 公钥加密
167      
168      * @param data
169      * @param certificatePath
170      * @return
171      * @throws Exception
172      */
173     public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
174             throws Exception {
175  
176         // 取得公钥
177         PublicKey publicKey = getPublicKey(certificatePath);
178         // 对数据加密
179         Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
180         cipher.init(Cipher.ENCRYPT_MODE, publicKey);
181  
182         return cipher.doFinal(data);
183  
184     }
185  
186     /**
187      * 公钥解密
188      
189      * @param data
190      * @param certificatePath
191      * @return
192      * @throws Exception
193      */
194     public static byte[] decryptByPublicKey(byte[] data, String certificatePath)
195             throws Exception {
196         // 取得公钥
197         PublicKey publicKey = getPublicKey(certificatePath);
198  
199         // 对数据加密
200         Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
201         cipher.init(Cipher.DECRYPT_MODE, publicKey);
202  
203         return cipher.doFinal(data);
204  
205     }
206  
207     /**
208      * 验证Certificate
209      
210      * @param certificatePath
211      * @return
212      */
213     public static boolean verifyCertificate(String certificatePath) {
214         return verifyCertificate(new Date(), certificatePath);
215     }
216  
217     /**
218      * 验证Certificate是否过期或无效
219      
220      * @param date
221      * @param certificatePath
222      * @return
223      */
224     public static boolean verifyCertificate(Date date, String certificatePath) {
225         boolean status = true;
226         try {
227             // 取得证书
228             Certificate certificate = getCertificate(certificatePath);
229             // 验证证书是否过期或无效
230             status = verifyCertificate(date, certificate);
231         catch (Exception e) {
232             status = false;
233         }
234         return status;
235     }
236  
237     /**
238      * 验证证书是否过期或无效
239      
240      * @param date
241      * @param certificate
242      * @return
243      */
244     private static boolean verifyCertificate(Date date, Certificate certificate) {
245         boolean status = true;
246         try {
247             X509Certificate x509Certificate = (X509Certificate) certificate;
248             x509Certificate.checkValidity(date);
249         catch (Exception e) {
250             status = false;
251         }
252         return status;
253     }
254  
255     /**
256      * 签名
257      
258      * @param keyStorePath
259      * @param alias
260      * @param password
261      
262      * @return
263      * @throws Exception
264      */
265     public static String sign(byte[] sign, String keyStorePath, String alias,
266             String password) throws Exception {
267         // 获得证书
268         X509Certificate x509Certificate = (X509Certificate) getCertificate(
269                 keyStorePath, alias, password);
270         // 获取私钥
271         KeyStore ks = getKeyStore(keyStorePath, password);
272         // 取得私钥
273         PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password
274                 .toCharArray());
275  
276         // 构建签名
277         Signature signature = Signature.getInstance(x509Certificate
278                 .getSigAlgName());
279         signature.initSign(privateKey);
280         signature.update(sign);
281         return encryptBASE64(signature.sign());
282     }
283  
284     /**
285      * 验证签名
286      
287      * @param data
288      * @param sign
289      * @param certificatePath
290      * @return
291      * @throws Exception
292      */
293     public static boolean verify(byte[] data, String sign,
294             String certificatePath) throws Exception {
295         // 获得证书
296         X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
297         // 获得公钥
298         PublicKey publicKey = x509Certificate.getPublicKey();
299         // 构建签名
300         Signature signature = Signature.getInstance(x509Certificate
301                 .getSigAlgName());
302         signature.initVerify(publicKey);
303         signature.update(data);
304  
305         return signature.verify(decryptBASE64(sign));
306  
307     }
308  
309     /**
310      * 验证Certificate
311      
312      * @param keyStorePath
313      * @param alias
314      * @param password
315      * @return
316      */
317     public static boolean verifyCertificate(Date date, String keyStorePath,
318             String alias, String password) {
319         boolean status = true;
320         try {
321             Certificate certificate = getCertificate(keyStorePath, alias,
322                     password);
323             status = verifyCertificate(date, certificate);
324         catch (Exception e) {
325             status = false;
326         }
327         return status;
328     }
329  
330     /**
331      * 验证Certificate
332      
333      * @param keyStorePath
334      * @param alias
335      * @param password
336      * @return
337      */
338     public static boolean verifyCertificate(String keyStorePath, String alias,
339             String password) {
340         return verifyCertificate(new Date(), keyStorePath, alias, password);
341     }
342  
343     /**
344      * 获得SSLSocektFactory
345      
346      * @param password
347      *            密码
348      * @param keyStorePath
349      *            密钥库路径
350      
351      * @param trustKeyStorePath
352      *            信任库路径
353      * @return
354      * @throws Exception
355      */
356     private static SSLSocketFactory getSSLSocketFactory(String password,
357             String keyStorePath, String trustKeyStorePath) throws Exception {
358         // 初始化密钥库
359         KeyManagerFactory keyManagerFactory = KeyManagerFactory
360                 .getInstance(SunX509);
361         KeyStore keyStore = getKeyStore(keyStorePath, password);
362         keyManagerFactory.init(keyStore, password.toCharArray());
363  
364         // 初始化信任库
365         TrustManagerFactory trustManagerFactory = TrustManagerFactory
366                 .getInstance(SunX509);
367         KeyStore trustkeyStore = getKeyStore(trustKeyStorePath, password);
368         trustManagerFactory.init(trustkeyStore);
369  
370         // 初始化SSL上下文
371         SSLContext ctx = SSLContext.getInstance(SSL);
372         ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory
373                 .getTrustManagers(), null);
374         SSLSocketFactory sf = ctx.getSocketFactory();
375  
376         return sf;
377     }
378  
379     /**
380      * 为HttpsURLConnection配置SSLSocketFactory
381      
382      * @param conn
383      *            HttpsURLConnection
384      * @param password
385      *            密码
386      * @param keyStorePath
387      *            密钥库路径
388      
389      * @param trustKeyStorePath
390      *            信任库路径
391      * @throws Exception
392      */
393     public static void configSSLSocketFactory(HttpsURLConnection conn,
394             String password, String keyStorePath, String trustKeyStorePath)
395             throws Exception {
396         conn.setSSLSocketFactory(getSSLSocketFactory(password, keyStorePath,
397                 trustKeyStorePath));
398     }
399 }



增加了configSSLSocketFactory方法供外界调用,该方法为 HttpsURLConnection配置了SSLSocketFactory。当HttpsURLConnection配置了 SSLSocketFactory后,我们就可以通过HttpsURLConnection的getInputStream、 getOutputStream,像往常使用HttpURLConnection做操作了。尤其要说明一点,未配置SSLSocketFactory 前,HttpsURLConnection的getContentLength()获得值永远都是-1。 

给出相应测试类:

001 import static org.junit.Assert.*;
002  
003 import java.io.DataInputStream;
004 import java.io.InputStream;
005 import java.net.URL;
006  
007 import javax.net.ssl.HttpsURLConnection;
008  
009 import org.junit.Test;
010  
011 /**
012  
013  * @author 梁栋
014  * @version 1.0
015  * @since 1.0
016  */
017 public class CertificateCoderTest {
018     private String password = "123456";
019     private String alias = "www.zlex.org";
020     private String certificatePath = "d:/zlex.cer";
021     private String keyStorePath = "d:/zlex.keystore";
022     private String clientKeyStorePath = "d:/zlex-client.keystore";
023     private String clientPassword = "654321";
024  
025     @Test
026     public void test() throws Exception {
027         System.err.println("公钥加密——私钥解密");
028         String inputStr = "Ceritifcate";
029         byte[] data = inputStr.getBytes();
030  
031         byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
032                 certificatePath);
033  
034         byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
035                 keyStorePath, alias, password);
036         String outputStr = new String(decrypt);
037  
038         System.err.println("加密前: " + inputStr + "\n\r" "解密后: " + outputStr);
039  
040         // 验证数据一致
041         assertArrayEquals(data, decrypt);
042  
043         // 验证证书有效
044         assertTrue(CertificateCoder.verifyCertificate(certificatePath));
045  
046     }
047  
048     @Test
049     public void testSign() throws Exception {
050         System.err.println("私钥加密——公钥解密");
051  
052         String inputStr = "sign";
053         byte[] data = inputStr.getBytes();
054  
055         byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
056                 keyStorePath, alias, password);
057  
058         byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
059                 certificatePath);
060  
061         String outputStr = new String(decodedData);
062         System.err.println("加密前: " + inputStr + "\n\r" "解密后: " + outputStr);
063         assertEquals(inputStr, outputStr);
064  
065         System.err.println("私钥签名——公钥验证签名");
066         // 产生签名
067         String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,
068                 password);
069         System.err.println("签名:\r" + sign);
070  
071         // 验证签名
072         boolean status = CertificateCoder.verify(encodedData, sign,
073                 certificatePath);
074         System.err.println("状态:\r" + status);
075         assertTrue(status);
076  
077     }
078  
079     @Test
080     public void testHttps() throws Exception {
081         URL url = new URL("https://www.zlex.org/examples/");
082         HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
083  
084         conn.setDoInput(true);
085         conn.setDoOutput(true);
086  
087         CertificateCoder.configSSLSocketFactory(conn, clientPassword,
088                 clientKeyStorePath, clientKeyStorePath);
089  
090         InputStream is = conn.getInputStream();
091  
092         int length = conn.getContentLength();
093  
094         DataInputStream dis = new DataInputStream(is);
095         byte[] data = new byte[length];
096         dis.readFully(data);
097  
098         dis.close();
099         System.err.println(new String(data));
100         conn.disconnect();
101     }
102 }


注意testHttps方法,几乎和我们往常做HTTP访问没有差别,我们来看控制台输出:



Apache Tomcat Examples



Apache Tomcat Examples

  • Servlets examples
  • JSP Examples


通过浏览器直接访问https://www.zlex.org/examples/你 也会获得上述内容。也就是说应用甲方作为服务器构建tomcat服务,乙方可以通过上述方式访问甲方受保护的SSL应用,并且不需要考虑具体的加密解密问 题。甲乙双方可以经过相应配置,通过双方的tomcat配置有效的SSL服务,简化上述代码实现,完全通过证书配置完成SSL双向认证!

    我们使用自签名证书完成了认证。接下来,我们使用第三方CA签名机构完成证书签名。 
    这里我们使用thawte提供的测试用21天免费ca证书。 
    1.要在该网站上注明你的域名,这里使用www.zlex.org作为测试用域名(请勿使用该域名作为你的域名地址,该域名受法律保护!请使用其他非注册域名!)。 
    2.如果域名有效,你会收到邮件要求你访问https://www.thawte.com/cgi/server/try.exe获得ca证书。 
    3.复述密钥库的创建。

keytool -genkey -validity 36000 -alias www.zlex.org -keyalg RSA -keystore d:\zlex.keystore



在这里我使用的密码为 123456 

控制台输出:

输入keystore密码:
再次输入新密码:
您的名字与姓氏是什么?
  [Unknown]:  www.zlex.org
您的组织单位名称是什么?
  [Unknown]:  zlex
您的组织名称是什么?
  [Unknown]:  zlex
您所在的城市或区域名称是什么?
  [Unknown]:  BJ
您所在的州或省份名称是什么?
  [Unknown]:  BJ
该单位的两字母国家代码是什么
  [Unknown]:  CN
CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 正确吗?
  [否]:  Y

输入的主密码
        (如果和 keystore 密码相同,按回车):
再次输入新密码:



    4.通过如下命令,从zlex.keystore中导出CA证书申请。 
   

keytool -certreq -alias www.zlex.org -file d:\zlex.csr -keystore d:\zlex.keystore -v

你会获得zlex.csr文件,可以用记事本打开,内容如下格式:

-----BEGIN NEW CERTIFICATE REQUEST-----
MIIBnDCCAQUCAQAwXDELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAkJKMQswCQYDVQQHEwJCSjENMAsG
A1UEChMEemxleDENMAsGA1UECxMEemxleDEVMBMGA1UEAxMMd3d3LnpsZXgub3JnMIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQCR6DXU9Mp+mCKO7cv9JPsj0n1Ec/GpM09qvhpgX3FNad/ZWSDc
vU77YXZSoF9hQp3w1LC+eeKgd2MlVpXTvbVwBNVd2HiQPp37ic6BUUjSaX8LHtCl7l0BIEye9qQ2
j8G0kak7e8ZA0s7nb3Ymq/K8BV7v0MQIdhIc1bifK9ZDewIDAQABoAAwDQYJKoZIhvcNAQEFBQAD
gYEAMA1r2fbZPtNx37U9TRwadCH2TZZecwKJS/hskNm6ryPKIAp9APWwAyj8WJHRBz5SpZM4zmYO
oMCI8BcnY2A4JP+R7/SwXTdH/xcg7NVghd9A2SCgqMpF7KMfc5dE3iygdiPu+UhY200Dvpjx8gmJ
1UbH3+nqMUyCrZgURFslOUY=
-----END NEW CERTIFICATE REQUEST-----


    5.将上述文件内容拷贝到https://www.thawte.com/cgi/server/try.exe中,点击next,获得回应内容,这里是p7b格式。 
内容如下:

-----BEGIN PKCS7-----
MIIF3AYJKoZIhvcNAQcCoIIFzTCCBckCAQExADALBgkqhkiG9w0BBwGgggWxMIID
EDCCAnmgAwIBAgIQA/mx/pKoaB+KGX2hveFU9zANBgkqhkiG9w0BAQUFADCBhzEL
MAkGA1UEBhMCWkExIjAgBgNVBAgTGUZPUiBURVNUSU5HIFBVUlBPU0VTIE9OTFkx
HTAbBgNVBAoTFFRoYXd0ZSBDZXJ0aWZpY2F0aW9uMRcwFQYDVQQLEw5URVNUIFRF
U1QgVEVTVDEcMBoGA1UEAxMTVGhhd3RlIFRlc3QgQ0EgUm9vdDAeFw0wOTA1Mjgw
MDIxMzlaFw0wOTA2MTgwMDIxMzlaMFwxCzAJBgNVBAYTAkNOMQswCQYDVQQIEwJC
SjELMAkGA1UEBxMCQkoxDTALBgNVBAoTBHpsZXgxDTALBgNVBAsTBHpsZXgxFTAT
BgNVBAMTDHd3dy56bGV4Lm9yZzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
keg11PTKfpgiju3L/ST7I9J9RHPxqTNPar4aYF9xTWnf2Vkg3L1O+2F2UqBfYUKd
8NSwvnnioHdjJVaV0721cATVXdh4kD6d+4nOgVFI0ml/Cx7Qpe5dASBMnvakNo/B
tJGpO3vGQNLO5292JqvyvAVe79DECHYSHNW4nyvWQ3sCAwEAAaOBpjCBozAMBgNV
HRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBABgNVHR8E
OTA3MDWgM6Axhi9odHRwOi8vY3JsLnRoYXd0ZS5jb20vVGhhd3RlUHJlbWl1bVNl
cnZlckNBLmNybDAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9v
Y3NwLnRoYXd0ZS5jb20wDQYJKoZIhvcNAQEFBQADgYEATPuxZbtJJSPmXvfrr1yz
xqM06IwTZ6UU0lZRG7I0WufMjNMKdpn8hklUhE17mxAhGSpewLVVeLR7uzBLFkuC
X7wMXxhoYdJZtNai72izU6Rd1oknao7diahvRxPK4IuQ7y2oZ511/4T4vgY6iRAj
q4q76HhPJrVRL/sduaiu+gYwggKZMIICAqADAgECAgEAMA0GCSqGSIb3DQEBBAUA
MIGHMQswCQYDVQQGEwJaQTEiMCAGA1UECBMZRk9SIFRFU1RJTkcgUFVSUE9TRVMg
T05MWTEdMBsGA1UEChMUVGhhd3RlIENlcnRpZmljYXRpb24xFzAVBgNVBAsTDlRF
U1QgVEVTVCBURVNUMRwwGgYDVQQDExNUaGF3dGUgVGVzdCBDQSBSb290MB4XDTk2
MDgwMTAwMDAwMFoXDTIwMTIzMTIxNTk1OVowgYcxCzAJBgNVBAYTAlpBMSIwIAYD
VQQIExlGT1IgVEVTVElORyBQVVJQT1NFUyBPTkxZMR0wGwYDVQQKExRUaGF3dGUg
Q2VydGlmaWNhdGlvbjEXMBUGA1UECxMOVEVTVCBURVNUIFRFU1QxHDAaBgNVBAMT
E1RoYXd0ZSBUZXN0IENBIFJvb3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB
ALV9kG+Os6x/DOhm+tKUQfzVMWGhE95sFmEtkMMTX2Zi4n6i6BvzoReJ5njzt1LF
cqu4EUk9Ji20egKKfmqRzmQFLP7+1niSdfJEUE7cKY40QoI99270PTrLjJeaMcCl
+AYl+kD+RL5BtuKKU3PurYcsCsre6aTvjMcqpTJOGeSPAgMBAAGjEzARMA8GA1Ud
EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAgozj7BkD9O8si2V0v+EZ/t7E
fz/LC8y6mD7IBUziHy5/53ymGAGLtyhXHvX+UIE6UWbHro3IqVkrmY5uC93Z2Wew
A/6edK3KFUcUikrLeewM7gmqsiASEKx2mKRKlu12jXyNS5tXrPWRDvUKtFC1uL9a
12rFAQS2BkIk7aU+ghYxAA==
-----END PKCS7-----

将其存储为zlex.p7b 
    6.将由CA签发的证书导入密钥库。

keytool -import -trustcacerts -alias www.zlex.org -file d:\zlex.p7b -keystore d:\zlex.keystore -v



在这里我使用的密码为 123456 

    控制台输出:

输入keystore密码:

回复中的最高级认证:

所有者:CN=Thawte Test CA Root, OU=TEST TEST TEST, O=Thawte Certification, ST=FOR
 TESTING PURPOSES ONLY, C=ZA
签发人:CN=Thawte Test CA Root, OU=TEST TEST TEST, O=Thawte Certification, ST=FOR
 TESTING PURPOSES ONLY, C=ZA
序列号:0
有效期: Thu Aug 01 08:00:00 CST 1996 至Fri Jan 01 05:59:59 CST 2021
证书指纹:
         MD5:5E:E0:0E:1D:17:B7:CA:A5:7D:36:D6:02:DF:4D:26:A4
         SHA1:39:C6:9D:27:AF:DC:EB:47:D6:33:36:6A:B2:05:F1:47:A9:B4:DA:EA
         签名算法名称:MD5withRSA
         版本: 3

扩展:

#1: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
  CA:true
  PathLen:2147483647
]


... 是不可信的。 还是要安装回复? [否]:  Y
认证回复已安装在 keystore中
[正在存储 d:\zlex.keystore]



    7.域名定位 
    将域名www.zlex.org定位到本机上。打开C:\Windows\System32\drivers\etc\hosts文件,将 www.zlex.org绑定在本机上。在文件末尾追加127.0.0.1       www.zlex.org。现在通过地址栏访问http://www.zlex.org,或者通过ping命令,如果能够定位到本机,域名映射就搞定 了。 

    8.配置server.xml



将文件zlex.keystore拷贝到tomcat的conf目录下,重新启动tomcat。访问https://www.zlex.org/,我们发现联网有些迟钝。大约5秒钟后,网页正常显示,同时有如下图所示: 
各种Java加密算法 
浏览器验证了该CA机构的有效性。 

打开证书,如下图所示: 
各种Java加密算法_第9张图片 

调整测试类: 

001 import static org.junit.Assert.*;
002  
003 import java.io.DataInputStream;
004 import java.io.InputStream;
005 import java.net.URL;
006  
007 import javax.net.ssl.HttpsURLConnection;
008  
009 import org.junit.Test;
010  
011 /**
012  
013  * @author 梁栋
014  * @version 1.0
015  * @since 1.0
016  */
017 public class CertificateCoderTest {
018     private String password = "123456";
019     private String alias = "www.zlex.org";
020     private String certificatePath = "d:/zlex.cer";
021     private String keyStorePath = "d:/zlex.keystore";
022  
023     @Test
024     public void test() throws Exception {
025         System.err.println("公钥加密——私钥解密");
026         String inputStr = "Ceritifcate";
027         byte[] data = inputStr.getBytes();
028  
029         byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
030                 certificatePath);
031  
032         byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
033                 keyStorePath, alias, password);
034         String outputStr = new String(decrypt);
035  
036         System.err.println("加密前: " + inputStr + "\n\r" "解密后: " + outputStr);
037  
038         // 验证数据一致
039         assertArrayEquals(data, decrypt);
040  
041         // 验证证书有效
042         assertTrue(CertificateCoder.verifyCertificate(certificatePath));
043  
044     }
045  
046     @Test
047     public void testSign() throws Exception {
048         System.err.println("私钥加密——公钥解密");
049  
050         String inputStr = "sign";
051         byte[] data = inputStr.getBytes();
052  
053         byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
054                 keyStorePath, alias, password);
055  
056         byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
057                 certificatePath);
058  
059         String outputStr = new String(decodedData);
060         System.err.println("加密前: " + inputStr + "\n\r" "解密后: " + outputStr);
061         assertEquals(inputStr, outputStr);
062  
063         System.err.println("私钥签名——公钥验证签名");
064         // 产生签名
065         String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,
066                 password);
067         System.err.println("签名:\r" + sign);
068  
069         // 验证签名
070         boolean status = CertificateCoder.verify(encodedData, sign,
071                 certificatePath);
072         System.err.println("状态:\r" + status);
073         assertTrue(status);
074  
075     }
076  
077     @Test
078     public void testHttps() throws Exception {
079         URL url = new URL("https://www.zlex.org/examples/");
080         HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
081  
082         conn.setDoInput(true);
083         conn.setDoOutput(true);
084  
085         CertificateCoder.configSSLSocketFactory(conn, password, keyStorePath,
086                 keyStorePath);
087  
088         InputStream is = conn.getInputStream();
089  
090         int length = conn.getContentLength();
091  
092         DataInputStream dis = new DataInputStream(is);
093         byte[] data = new byte[length];
094         dis.readFully(data);
095  
096         dis.close();
097         conn.disconnect();
098         System.err.println(new String(data));
099     }
100 }



再次执行,验证通过! 
由此,我们了基于SSL协议的认证过程。测试类的testHttps方法模拟了一次浏览器的HTTPS访问。

       
     

你可能感兴趣的:(各种Java加密算法)