`

在网络通讯中采用混合方式对信息进行加密

    博客分类:
  • web
阅读更多

按:下面的文字涉及早已在工程中广泛采用的混合加密方式,对此熟知者就不用往下看了,以免浪费时间。

我们知道,现代加密方式有两大类:一类是对称加密方式,其优点是加密速度快,缺点是密钥不便于传递,其中典型例子是AES;一类是非对称加密方式,优点是交换钥匙方便,缺点是加密时间长。在实际应用,我们可以取其所长,弃其所短,这就是混合加密方式,有的场合也成为Hybrid方式。

具体来说混合加密方式的工作过程大体是这样:首先,客户端将明文用本地的AES钥匙加密,然后从服务器端得到服务器端的RSA公钥,用它来对本地的AES钥匙加密,然后把两端密文拼合在一起送给服务器端;服务器端得到密文后,将其拆分成密钥文和密文两段,然后,用本地的RSA私钥对密钥文进行解密,得到加密密文的AES钥匙,然后用AES钥匙对密文解密,得到明文。在此过程中,对明文加密和对密文解密都采用了对称加密解密方式,速度快,且都在服务器客户机的一侧进行,没有通过网络传输,安全性高;而网络传输的是服务器的RSA公钥和经其加密的AES钥匙,即使被截获也没有什么好担心的。如果要双向传递则把这个过程反过来就可以了。

以上过程的示意UML SEQUENCE图如下:



下面用代码来辅助说明一下。

客户端进行加密并传输密文到服务器端的代码,其中,服务器端的RSA公钥已经用别的方法得到了,下面serverPublicKey变量就存储了它:

        Socket s=new Socket("127.0.0.1",8888);
        
        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();
// 输出结束

从上面这段代码可以看出,想发送到服务器端的明文是:
<request><command>register</command><username>何杨</username><password>123456</password></request>

通过这段代码的处理后,最终发送到服务器端的密文是,
<key>1B2FM07HS4iB+vjeehb/RqHTnEXAr1cj/CR6z+SDPI58ZG5TK54iEoi8cvdIL0oj60X7axrAL3YO
b6PMzQxKHzipSYw3ishH/3KxoYF8bkQGn2PkMNsn+xL1Gz6XgJcQ+B700hYvVT2FFPfelVz3VNlB
KhwVIE6h8LyD4w/SxhE=
</key>J4TsMoB3l8Cy91a9v6O0TADXZvKEkDPZ3E5noeu2dImfdsM55urhEY7lFAAsXm0AB4/jUL1h1lNP
cafz9srORh7h8NCb4760XnrBA5Q2JQrqwr1TGsB3oGq2Ha+FOLoFcI2Ab/wjEiAhe/kB6ZTgTA==
其中key节点的内容是加密的AES密钥,后面是AES加密后的密文。如果这段文字在网络上被截获,截获者可能会猜测出key节点是密钥,后半段是密文,但密钥部分是被服务器的公钥进行RSA加密的,只有用服务器的私钥来解密;而密钥文解不出来的话,截获者对后端密文也是无能为力。这就可以让人放心了,如果服务器端没有潜伏一个余则成和截获者里应外合的话。

服务器端的处理代码:
        String cipheredAesKey="";// 经服务器RSA公钥加密的客户端AES钥匙密文
        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);

这段代码的输入是:
<key>P9SQ2DtWqrdH3hJbQNWRb51OEs9c7KpsgjRg0yPT5LZJoqJBeYmq3r/1T050n136OelvTh+XtaZaXbCJAvfnF4fvtAKdXqPp+lzUNgPYk8R0OaVDUIi8pNi1rb/+GvtY2ZucFYL1BOwO8ARwvXf8f52Cl+Vdu5TdinXVjmwSPZY=</key>u0ube9sy7bsIy8aaUSJofoswY+R3WXD8yJbOzEZWiDniyXNNyrHNiygfRHj3TKwVQXRck/OVPXptMvUjCVqmg118TN0tc4sKoOKHaSmUtvGC2WW3K5anxlFzdUIZMIhvpDF1nWoaTXvEJ1nOuwhIig==
它和客户端传过来的内容是一样的。

而经过拆分和解密后,AES密钥是:
83aeacfa1b59eb2dc557a9f3d5df6af83ee9a1646652f1d2b55ea6ec76a95bde

用得到的AES密钥解密后,最终得到的明文部分是:
<request><command>register</command><username>何杨</username><password>123456</password></request>

到这里,密文的还原工作就完成了。如果服务器端要向客户端发回处理后的结果,把上述过程再做一遍就可以了,注意一点,客户端要把自己的RSA公钥发过来,也就是说传递的文本中还要增加一个节点,这样服务器端就有了客户端的RSA公钥对服务器端的AES钥匙进行加密。

这种方式看似比纯RSA方式和AES方式都复杂了一点,但考虑到网络传输的安全性和速度,多写一些代码是完全值得的。

上文中用到的AESSecurityCoder类代码如下:
package com.heyang.common.code;

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类代码如下:
package com.heyang.common.code;

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);
    }
}

好了,感谢您看到这里,希望它没有太多耽误您的宝贵时间。

分享到:
评论

相关推荐

    计算机网络系统设计解决方案.doc

    设计的过程中必须依据国家各种有关安全法规政策,对硬件支撑 平台的访问控制、信息加密等方面进行完善考虑,在网络架构上根据功能划分相应的区 域,使用如防火墙、入侵防御、加密设备对信息过滤、隔离、数据加密等...

    VC与Labview、Matlab编程论文资料[2].rar

    基于MFCSockets实现安全可靠的网络通讯.pdf 基于MFC与ADO接口的企业采购管理系统设计.pdf 基于MFC和UG的飞艇参数化设计系统研究.pdf 基于MFC和Vega的子母弹抛撒仿真研究.pdf 基于MFC实现的小型局域网聊天室.pdf 基于...

    VC与Labview、Matlab编程论文资料

    基于MFCSockets实现安全可靠的网络通讯.pdf 基于MFC与ADO接口的企业采购管理系统设计.pdf 基于MFC和UG的飞艇参数化设计系统研究.pdf 基于MFC和Vega的子母弹抛撒仿真研究.pdf 基于MFC实现的小型局域网聊天室.pdf 基于...

    VC与Labview、Matlab编程论文资料[4].rar

    基于MFCSockets实现安全可靠的网络通讯.pdf 基于MFC与ADO接口的企业采购管理系统设计.pdf 基于MFC和UG的飞艇参数化设计系统研究.pdf 基于MFC和Vega的子母弹抛撒仿真研究.pdf 基于MFC实现的小型局域网聊天室.pdf 基于...

    mySHOP系统服务器安装手册.doc

    责任声明 本文包含的信息代表富基融通公司目前对本文所涉及内容的观点,由于用户需求,市 场和产品情况的不断变化,本文中的信息并不代表富基融通公司未来的观点,富基融通 公司不能保证本文信息在未来时间的有效性...

    物联网国内DDA技术介绍

    在通讯距离、低成本网络覆盖、低功耗设计、抗干扰设计、通讯可靠性、数据安全性、海量终端 接入、鲁棒性、易用性、自适应频段选择等多项通讯技术指标上,领先国内、国外其它无线通讯技术。非常适用于万物互联时 代,...

    门禁系统设计方案.doc

    多信息识别载体:但随着系统规模的扩展与对系统更高级别安全性、便利性的要求 ,具备人体固有的、特定的、唯一的特点的生物识别(指纹等),作为具有更高可靠性 、更方便性的认证识别方式越来越多地与卡片识别混合...

    JAVA上百实例源码以及开源项目

     通过网络或磁盘等方式,把公钥编码传送给李四,李四接收到张三编码后的公钥,将其解码,李四用张三的公钥加密信息,并发送给李四,张三用自己的私钥解密从李四处收到的信息…… Java利用DES私钥对称加密代码实例 ...

    易语言700模块打包

    操作其它程序中的超级列表框1.0(1.0).zip 查找窗口内所有组件句柄(1.0).zip 查杀程序(1.0).zip 磁盘操作(1.0).zip 超级列表快速排序(2.0).zip 超级列表框补丁1.0(1.0).zip 传奇世界登陆模块(1.9).zip 磁性...

    JAVA上百实例源码以及开源项目源代码

     通过网络或磁盘等方式,把公钥编码传送给李四,李四接收到张三编码后的公钥,将其解码,李四用张三的公钥加密信息,并发送给李四,张三用自己的私钥解密从李四处收到的信息…… Java利用DES私钥对称加密代码实例 ...

    Java资源包01

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包1

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包11

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包2

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包3

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包6

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包5

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包10

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包4

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

Global site tag (gtag.js) - Google Analytics