按:下面的文字涉及早已在工程中广泛采用的混合加密方式,对此熟知者就不用往下看了,以免浪费时间。
我们知道,现代加密方式有两大类:一类是对称加密方式,其优点是加密速度快,缺点是密钥不便于传递,其中典型例子是AES;一类是非对称加密方式,优点是交换钥匙方便,缺点是加密时间长。在实际应用,我们可以取其所长,弃其所短,这就是混合加密方式,有的场合也成为Hybrid方式。
具体来说混合加密方式的工作过程大体是这样:首先,客户端将明文用本地的AES钥匙加密,然后从服务器端得到服务器端的RSA公钥,用它来对本地的AES钥匙加密,然后把两端密文拼合在一起送给服务器端;服务器端得到密文后,将其拆分成密钥文和密文两段,然后,用本地的RSA私钥对密钥文进行解密,得到加密密文的AES钥匙,然后用AES钥匙对密文解密,得到明文。在此过程中,对明文加密和对密文解密都采用了对称加密解密方式,速度快,且都在服务器客户机的一侧进行,没有通过网络传输,安全性高;而网络传输的是服务器的RSA公钥和经其加密的AES钥匙,即使被截获也没有什么好担心的。如果要双向传递则把这个过程反过来就可以了。
以上过程的示意UML SEQUENCE图如下:
下面用代码来辅助说明一下。
客户端进行加密并传输密文到服务器端的代码,其中,服务器端的RSA公钥已经用别的方法得到了,下面serverPublicKey变量就存储了它:
????????
????????InputStream??inStram=s.getInputStream();
????????OutputStream?outStream=s.getOutputStream();
????????
????????//?输出
????????PrintWriter?out=new?PrintWriter(outStream,true);
????????
????????//?待加密的明文
????????StringBuilder?sb1=new?StringBuilder();
????????sb1.append("<request>");
????????sb1.append("<command>register</command>");
????????sb1.append("<username>何杨</username>");
????????sb1.append("<password>123456</password>");
????????sb1.append("</request>");
????????String?plainText=sb1.toString();
????????
????????//?对明文进行AES加密
????????byte[]?aesArr=aesCoder.getEncryptByteArray(plainText);?//?对明文进行AES加密
????????String?cipherText=Base64.encodeBase64String(aesArr);//?得到AES加密后的密文
????????
????????//?使用RSA对AES密钥进行加密
????????String?key=aesCoder.getAesKey();//?取得AES的密钥
????????byte[]?rsaArr=rsaCoder.getEncryptArray(key,?serverPublicKey);
????????String?encryptedKey=Base64.encodeBase64String(rsaArr);
????????
????????//?在发出的密文前附带经服务器RSA公钥加密的AES密钥
????????String?request="<key>"+encryptedKey+"</key>"+cipherText;
????????
????????out.print(request);
????????out.flush();
????????s.shutdownOutput();//?输出结束
从上面这段代码可以看出,想发送到服务器端的明文是:
通过这段代码的处理后,最终发送到服务器端的密文是,
b6PMzQxKHzipSYw3ishH/3KxoYF8bkQGn2PkMNsn+xL1Gz6XgJcQ+B700hYvVT2FFPfelVz3VNlB
KhwVIE6h8LyD4w/SxhE=
</key>J4TsMoB3l8Cy91a9v6O0TADXZvKEkDPZ3E5noeu2dImfdsM55urhEY7lFAAsXm0AB4/jUL1h1lNP
cafz9srORh7h8NCb4760XnrBA5Q2JQrqwr1TGsB3oGq2Ha+FOLoFcI2Ab/wjEiAhe/kB6ZTgTA==
服务器端的处理代码:
????????String?cipherText="";//?经客户端AES加密的密文
????????
????????//?用正则表达式得到密钥文和密文
????????String?regex="<key>(.+)</key>(.+)";
????????Pattern?pattern=Pattern.compile(regex);
????????Matcher?matcher=pattern.matcher(request);
????????????
????????while(matcher.find()){
????????????cipheredAesKey=matcher.group(1);
????????????cipherText=matcher.group(2);
????????????break;
????????}
????????//?得到经过服务器RSA私钥解密后的AES密钥
????????String?plainAesKey="";
????????try?{
????????????byte[]?cipheredAesKeyArr=Base64.decodeBase64(cipheredAesKey);
????????????plainAesKey=model.getRsaCoder().getDecryptString(cipheredAesKeyArr);
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????????return?null;
????????}
????????
????????//?使用AES密钥解密出明文
????????byte[]?cipherTextArr=Base64.decodeBase64(cipherText);
????????String?plainText=model.getAesCoder().getDecryptString(cipherTextArr,?plainAesKey);
这段代码的输入是:
而经过拆分和解密后,AES密钥是:
用得到的AES密钥解密后,最终得到的明文部分是:
到这里,密文的还原工作就完成了。如果服务器端要向客户端发回处理后的结果,把上述过程再做一遍就可以了,注意一点,客户端要把自己的RSA公钥发过来,也就是说传递的文本中还要增加一个节点,这样服务器端就有了客户端的RSA公钥对服务器端的AES钥匙进行加密。
这种方式看似比纯RSA方式和AES方式都复杂了一点,但考虑到网络传输的安全性和速度,多写一些代码是完全值得的。
上文中用到的AESSecurityCoder类代码如下:
import?java.security.Key;
import?java.security.NoSuchAlgorithmException;
import?javax.crypto.Cipher;
import?javax.crypto.KeyGenerator;
import?javax.crypto.SecretKey;
import?javax.crypto.spec.SecretKeySpec;
import?org.apache.commons.codec.binary.Hex;
/**
?*?AES加密解密类
?*?说明:
?*?作者:何杨(heyang78@gmail.com)
?*?创建时间:2010-12-25?下午12:19:12
?*?修改时间:2010-12-25?下午12:19:12
?*/
public?class?AESSecurityCoder{
????//?加密方法
????private?static?final?String?Algorithm="AES";
????
????//?进行加密解密的密钥
????private?String?aesKey="";
????
????/**
?????*?构造函数
?????*?@throws?NoSuchAlgorithmException?
?????*/
????public?AESSecurityCoder()?throws?NoSuchAlgorithmException{
????????KeyGenerator?kg=KeyGenerator.getInstance(Algorithm);
????????kg.init(256);
????????SecretKey?sk=kg.generateKey();
????????byte[]?arr=sk.getEncoded();
????????
????????aesKey=new?String(Hex.encodeHex(arr));
????}
????
????/**
?????*?取得解密后的字符串
?????*?
?????*?说明:
?????*?@param?encryptArr
?????*?@return
?????*?创建时间:2010-12-1?下午03:33:31
?????*/
????public?String?getDecryptString(byte[]?encryptArr){
????????try{
????????????Cipher?cp=Cipher.getInstance(Algorithm);
????????????cp.init(Cipher.DECRYPT_MODE,?getKey());
????????????byte[]?arr=cp.doFinal(encryptArr);
????????????
????????????return?new?String(arr);
????????}
????????catch(Exception?ex){
????????????System.out.println("无法进行解密,原因是"+ex.getMessage());
????????????return?null;
????????}
????}
????
????/**
?????*?传入密钥,得到解密后的字符串
?????*?
?????*?说明:
?????*?@param?encryptArr
?????*?@param?aesKey
?????*?@return
?????*?创建时间:2010-12-25?下午01:55:42
?????*/
????public?String?getDecryptString(byte[]?encryptArr,String?aesKeyIn){
????????try{
????????????Cipher?cp=Cipher.getInstance(Algorithm);
????????????
????????????byte[]?arr1=Hex.decodeHex(aesKeyIn.toCharArray());
????????????cp.init(Cipher.DECRYPT_MODE,?new?SecretKeySpec(arr1,Algorithm));
????????????byte[]?arr=cp.doFinal(encryptArr);
????????????
????????????return?new?String(arr);
????????}
????????catch(Exception?ex){
????????????System.out.println("无法进行解密,原因是"+ex.getMessage());
????????????return?null;
????????}
????}
????
????/**
?????*?取得加密后的字节数组
?????*?
?????*?说明:
?????*?@param?originalString
?????*?@return
?????*?创建时间:2010-12-1?下午03:33:49
?????*/
????public?byte[]?getEncryptByteArray(String?originalString){
????????try{
????????????Cipher?cp=Cipher.getInstance(Algorithm);
????????????cp.init(Cipher.ENCRYPT_MODE,?getKey());
????????????return?cp.doFinal(originalString.getBytes());
????????}
????????catch(Exception?ex){
????????????System.out.println("无法进行加密,原因是"+ex.getMessage());
????????????return?null;
????????}
????}
????
????/**
?????*?取得密钥
?????*?
?????*?说明:
?????*?@return
?????*?@throws?Exception
?????*?创建时间:2010-12-1?下午03:33:17
?????*/
????private?Key?getKey()?throws?Exception{
????????byte[]?arr=Hex.decodeHex(aesKey.toCharArray());
????????
????????return?new?SecretKeySpec(arr,Algorithm);
????}
????/**
?????*?取得AES加密钥匙
?????*?
?????*?说明:
?????*?@return
?????*?创建时间:2010-12-25?下午12:27:16
?????*/
????public?String?getAesKey()?{
????????return?aesKey;
????}
}
上文中用到的RSASecurityCoder类代码如下:
import?java.security.KeyFactory;
import?java.security.KeyPair;
import?java.security.KeyPairGenerator;
import?java.security.PrivateKey;
import?java.security.PublicKey;
import?java.security.interfaces.RSAPrivateKey;
import?java.security.interfaces.RSAPublicKey;
import?java.security.spec.PKCS8EncodedKeySpec;
import?java.security.spec.X509EncodedKeySpec;
import?javax.crypto.Cipher;
import?org.apache.commons.codec.binary.Base64;
/**
?*?RSA加密解密类
?*?说明:
?*?作者:何杨(heyang78@gmail.com)
?*?创建时间:2010-12-1?下午06:14:38
?*?修改时间:2010-12-1?下午06:14:38
?*/
public?class?RSASecurityCoder{
????//?非对称加密密钥算法
????private?static?final?String?Algorithm="RSA";
????
????//?密钥长度,用来初始化
????private?static?final?int?Key_Size=1024;
????
????//?公钥
????private?byte[]?publicKey;
????
????//?私钥
????private?byte[]?privateKey;
????
????/**
?????*?构造函数,在其中生成公钥和私钥
?????*?@throws?Exception
?????*/
????public?RSASecurityCoder()?throws?Exception{
????????//?得到密钥对生成器
????????KeyPairGenerator?kpg=KeyPairGenerator.getInstance(Algorithm);
????????kpg.initialize(Key_Size);
????????
????????//?得到密钥对
????????KeyPair?kp=kpg.generateKeyPair();
????????
????????//?得到公钥
????????RSAPublicKey?keyPublic=(RSAPublicKey)kp.getPublic();
????????publicKey=keyPublic.getEncoded();
????????
????????//?得到私钥
????????RSAPrivateKey?keyPrivate=(RSAPrivateKey)kp.getPrivate();
????????privateKey=keyPrivate.getEncoded();
????}
????
????/**
?????*?用公钥对字符串进行加密
?????*?
?????*?说明:
?????*?@param?originalString
?????*?@param?publicKeyArray
?????*?@return
?????*?@throws?Exception
?????*?创建时间:2010-12-1?下午06:29:51
?????*/
????public?byte[]?getEncryptArray(String?originalString,byte[]?publicKeyArray)?throws?Exception{
????????//?得到公钥
????????X509EncodedKeySpec?keySpec=new?X509EncodedKeySpec(publicKeyArray);
????????KeyFactory?kf=KeyFactory.getInstance(Algorithm);
????????PublicKey?keyPublic=kf.generatePublic(keySpec);
????????
????????//?加密数据
????????Cipher?cp=Cipher.getInstance(Algorithm);
????????cp.init(Cipher.ENCRYPT_MODE,?keyPublic);
????????return?cp.doFinal(originalString.getBytes());
????}
????
????
????/**
?????*?使用私钥进行解密
?????*?
?????*?说明:
?????*?@param?encryptedDataArray
?????*?@return
?????*?@throws?Exception
?????*?创建时间:2010-12-1?下午06:35:28
?????*/
????public?String?getDecryptString(byte[]?encryptedDataArray)?throws?Exception{
????????//?得到私钥
????????PKCS8EncodedKeySpec?keySpec=new?PKCS8EncodedKeySpec(privateKey);
????????KeyFactory?kf=KeyFactory.getInstance(Algorithm);
????????PrivateKey?keyPrivate=kf.generatePrivate(keySpec);
????????
????????//?解密数据
????????Cipher?cp=Cipher.getInstance(Algorithm);
????????cp.init(Cipher.DECRYPT_MODE,?keyPrivate);
????????byte[]?arr=cp.doFinal(encryptedDataArray);
????????
????????//?得到解密后的字符串
????????return?new?String(arr);
????}
????/**
?????*?取得数组形式的公钥
?????*?
?????*?说明:
?????*?@return
?????*?创建时间:2010-12-25?上午07:50:04
?????*/
????public?byte[]?getPublicKey()?{
????????return?publicKey;
????}
????
????/**
?????*?取得字符串形式的公钥
?????*?
?????*?说明:
?????*?@return
?????*?创建时间:2010-12-25?上午07:51:11
?????*/
????public?String?getPublicKeyString()?{
????????return??Base64.encodeBase64String(getPublicKey());
????}
????
????public?static?void?main(String[]?arr)?throws?Exception{
????????String?str="你好,世界!?Hello,world!";
????????System.out.println("准备用公钥加密的字符串为:"+str);
????????
????????//?用公钥加密
????????RSASecurityCoder?rsaCoder=new?RSASecurityCoder();
????????byte[]?publicKey=rsaCoder.getPublicKey();????????
????????byte[]?encryptArray=rsaCoder.getEncryptArray(str,?publicKey);
????????
????????System.out.print("用公钥加密后的结果为:");
????????for(byte?b:encryptArray){
????????????System.out.print(b);
????????}
????????System.out.println();
????????
????????//?用私钥解密
????????String?str1=rsaCoder.getDecryptString(encryptArray);
????????System.out.println("用私钥解密后的字符串为:"+str1);
????}
}
好了,感谢您看到这里,希望它没有太多耽误您的宝贵时间。
详细解决方案
在网络通讯中采取混合方式对信息进行加密
热度:412 发布时间:2012-10-29 10:03:53.0
??????? Socket?s=new?Socket("127.0.0.1",8888);
<request><command>register</command><username>何杨</username><password>123456</password></request>
<key>1B2FM07HS4iB+vjeehb/RqHTnEXAr1cj/CR6z+SDPI58ZG5TK54iEoi8cvdIL0oj60X7axrAL3YO
其中key节点的内容是加密的AES密钥,后面是AES加密后的密文。如果这段文字在网络上被截获,截获者可能会猜测出key节点是密钥,后半段是密文,但密钥部分是被服务器的公钥进行RSA加密的,只有用服务器的私钥来解密;而密钥文解不出来的话,截获者对后端密文也是无能为力。这就可以让人放心了,如果服务器端没有潜伏一个余则成和截获者里应外合的话。
??????? String?cipheredAesKey="";//?经服务器RSA公钥加密的客户端AES钥匙密文
<key>P9SQ2DtWqrdH3hJbQNWRb51OEs9c7KpsgjRg0yPT5LZJoqJBeYmq3r/1T050n136OelvTh+XtaZaXbCJAvfnF4fvtAKdXqPp+lzUNgPYk8R0OaVDUIi8pNi1rb/+GvtY2ZucFYL1BOwO8ARwvXf8f52Cl+Vdu5TdinXVjmwSPZY=</key>u0ube9sy7bsIy8aaUSJofoswY+R3WXD8yJbOzEZWiDniyXNNyrHNiygfRHj3TKwVQXRck/OVPXptMvUjCVqmg118TN0tc4sKoOKHaSmUtvGC2WW3K5anxlFzdUIZMIhvpDF1nWoaTXvEJ1nOuwhIig==
它和客户端传过来的内容是一样的。83aeacfa1b59eb2dc557a9f3d5df6af83ee9a1646652f1d2b55ea6ec76a95bde
<request><command>register</command><username>何杨</username><password>123456</password></request>
package?com.heyang.common.code;
package?com.heyang.common.code;