精华内容
下载资源
问答
  • 混合加密

    2018-12-24 10:50:18
    前言 上一篇文章我们提到了公开密钥加密方法 并且剖析了它的优点和缺点,优点主要是加密解密比较安全,攻击人没有拿到私钥的情况,使用此种加密解密方式几乎不可能被破解;...混合加密 当我们使用对称加...

    前言

    上一篇文章我们提到了非对称密钥加密方法

    并且剖析了它的优点和缺点,优点主要是加密解密比较安全,在攻击人没有拿到私钥的情况下,使用此种加密解密方式几乎不可能被破解;缺点有两个,第一个是使用此种方式的性能比较不好,第二个是在公钥的交付过程中可能会被攻击人拦截到并且替换成自己的公钥,让甲方和乙方在自以为很安全的情况下就泄露了自己的信息。此篇博文我们先来解决一下第一个问题。

    混合加密

    当我们使用对称加密解密的时候,存在对称加密钥匙被劫持的问题,但是速度较快;当我们使用非对称加密解密的时候,存在速度较慢的问题,但是比较安全。那么,如果将两种方式结合一下呢?

    那就是我们的混合加密方式了,下面是混合加密的流程:

    A方想通过互联网向B方发送数据:
    在这里插入图片描述
    A方需要对数据进行加密,因为使用的是对称加密,所以加密密钥也是解密密钥,A方需要将这把钥匙发送给B方:
    在这里插入图片描述

    使用混合加密的目的是把对称加密的钥匙通过非对称加密的方式发送给B方,防止对称加密的钥匙半路被拦截,因此,B方先要创建自己的公钥和私钥:
    在这里插入图片描述

    然后B方把自己的公钥发送给A方:
    在这里插入图片描述

    A方使用B方的公钥对自己的对称钥匙进行加密:
    在这里插入图片描述
    发送给B方之后,B方再用自己的私钥对A的对称加密钥匙进行解密:
    在这里插入图片描述
    至此,B方通过安全的途径得到了A方的对称加密的钥匙,可以用来解密了(注意,安全只是相对之前单纯地传送对称加密钥匙而言,此处的安全相当于半裸之于全裸,因为我们还是需要数字签名来做到真正的安全,后面会做说明)
    在这里插入图片描述
    现在,A方可以使用对称加密钥匙加密数据,B方可以通过同一把钥匙解密数据了:
    在这里插入图片描述

    但是,我们还是没有解决在公钥的传输过程中被攻击人替换成假公钥的问题,因此,攻击人可以使用中间人攻击方法让A方使用假公钥加密,然后自己通过假私钥解密,再使用B的真公钥加密,然后发回给B,以此做到窃取AB之间使用的对称加密钥匙的目的。

    因此,我们还是需要数字证书

    • 参考资料:
      算法动画图解APP(侵删)
    展开全文
  • 按:前面(http://www.blogjava.net/heyang/archive/2010/12/25/341518.html)已经提到过混合加密方式,今天这里再来赘述一下完成的代码和流程,熟悉者就不用往看了。整个流程的UML Sequence图(Amaterus UML用得还...

    按:前面(http://www.blogjava.net/heyang/archive/2010/12/25/341518.html)已经提到过混合加密方式,今天这里再来赘述一下完成的代码和流程,熟悉者就不用往下看了。

    整个流程的UML Sequence图(Amaterus UML用得还不熟练请见谅,会意就好)


    下面是以上八个步骤中使用到的代码和信息。

    步骤一
    取得服务器端RSA公钥的Socket通信代码

        public static byte[] getPublicKey() throws Exception{
            Socket s
    =new Socket("127.0.0.1",8888);
            
            InputStream  inStram
    =s.getInputStream();
            OutputStream outStream
    =s.getOutputStream();
            
            
    // 输出
            PrintWriter out=new PrintWriter(outStream,true);
            
            out.print(
    "getPublicKey");
            out.flush();

            s.shutdownOutput();
    // 输出结束
            
            
    // 输入
            Scanner in=new Scanner(inStram);
            StringBuilder sb
    =new StringBuilder();
            
    while(in.hasNextLine()){
                String line
    =in.nextLine();
                sb.append(line);
            }
            String response
    =sb.toString();
            
            
    byte[] arr=Base64.decodeBase64(response);
            
            s.close();
            
    return arr;
        }

    步骤二:
    客户端加密过程代码:

            // 待加密的明文
            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);

    步骤三:
    将密文,AES密钥,本地RSA公钥送到服务器端的代码(粗体部分):

            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密钥
            StringBuilder sb3=new StringBuilder();
            sb3.append("<aeskey>"+encryptedKey+"</aeskey>");
            sb3.append("<rsakey>"+rsaCoder.getPublicKeyString()+"</rsakey>");
            sb3.append("<text>"+cipherText+"</text>");
            
            // 请求送出前用Base64加密
            String request=Base64SecurityUtil.getEncryptString(sb3.toString());
          
            out.print(request);
           out.flush();
           s.shutdownOutput();
    // 输出结束

    步骤四:
    服务器端解密过程代码(变量request中就是客户端送来的请求文):

           // 得到请求后先用Base64解密
            request=Base64SecurityUtil.getDecryptString(request);
            
            // 用正则表达式得到密钥文,客户端的RSA公钥和密文
            String regex="<(\\w+)>((.|\\s)+)</\\1>";

            Pattern pattern=Pattern.compile(regex);
            Matcher matcher=pattern.matcher(request);
                
            String cipheredAesKey="";// 经服务器RSA公钥加密的客户端AES钥匙密文
            String clientRsaKey="";// 客户端的RSA公钥
            String cipherText="";// 经客户端AES加密的密文
            
            Map<String,String> map=new HashMap<String,String>();
            while(matcher.find()){
                map.put(matcher.group(1), matcher.group(2));
            }
            
            if(map.size()==3){
                cipheredAesKey=map.get("aeskey");
                clientRsaKey=map.get("rsakey");
                cipherText=map.get("text");
            }
            else{
                return "无法用正则表达式解析服务器端请求";
            }

            // 得到经过服务器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加密
            byte[] aesArr=model.getAesCoder().getEncryptByteArray(retval); // 对明文进行AES加密
            String cipherRetval=Base64.encodeBase64String(aesArr);// 得到AES加密后的密文
            
            
    // 使用RSA对AES密钥进行加密
            String key=model.getAesCoder().getAesKey();// 取得AES的密钥
            String aesKey="";
            
    try{
                
    byte[] clientRsaKeyArr=null;
                clientRsaKeyArr
    =Base64.decodeBase64(clientRsaKey);
                
    byte[] rsaArr=model.getRsaCoder().getEncryptArray(key, clientRsaKeyArr);
                aesKey
    =Base64.encodeBase64String(rsaArr);
            }
            
    catch(Exception ex){
                ex.printStackTrace();
            }
            
            
    // 在发出的密文前附带经服务器RSA公钥加密的AES密钥
            StringBuilder sb3=new StringBuilder();
            sb3.append(
    "<aeskey>"+aesKey+"</aeskey>");
            sb3.append(cipherRetval);

    步骤七:
    将响应发还给客户端的代码(粗体部分):

                InputStream  inStram=incoming.getInputStream();
                OutputStream outStream
    =incoming.getOutputStream();
                
                Scanner in
    =new Scanner(inStram);
                PrintWriter out
    =new PrintWriter(outStream,true);
                
                
    // 得到客户端的请求
                StringBuilder sb=new StringBuilder();
                
    while(in.hasNextLine()){
                    String line
    =in.nextLine();
                    sb.append(line);
                }
                
                
                String request
    =sb.toString();
                String response
    ="";
                
    if("getPublicKey".equals(request)){
                    
    // 求服务器公钥
                    response=model.getPublicKey();
                }
                
    else{
                    response
    =getResponse(request);
                }
                
                
    // 向客户端送出反馈
                out.print(response);
                out.flush();
                out.close();

    步骤八:
    客户端解密服务器端响应的过程:

            String cipheredAesKey="";// 经服务器RSA公钥加密的客户端AES钥匙密文
            String cipheredResponse="";// 经客户端AES加密的密文
            
            
    // 用正则表达式得到密钥文,客户端的RSA公钥和密文
            String regex="<aeskey>(.+)</aeskey>(.+)";
            Pattern pattern
    =Pattern.compile(regex);
            Matcher matcher
    =pattern.matcher(response);
                
            
    while(matcher.find()){
                cipheredAesKey
    =matcher.group(1);
                cipheredResponse
    =matcher.group(2);
                
    break;
            }
            
            
    // 得到经过服务器RSA私钥解密后的AES密钥
            String plainAesKey="";
            
    try {
                
    byte[] cipheredAesKeyArr=Base64.decodeBase64(cipheredAesKey);
                plainAesKey
    =rsaCoder.getDecryptString(cipheredAesKeyArr);
            } 
    catch (Exception e) {
                e.printStackTrace();
            }
            
            
    // 使用AES密钥解密出明文
            byte[] cipheredResponseArr=Base64.decodeBase64(cipheredResponse);
            String plainResponse
    =aesCoder.getDecryptString(cipheredResponseArr, plainAesKey);
            System.out.println(plainResponse);

    好了,整个过程的代码都贴出来了,感谢您花费宝贵时间看到这里。另外三个加密解密类的代码如下:
    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);
        }
    }

    Base64SecurityUtil类:
    package com.heyang.common.code;

    import org.apache.commons.codec.binary.Base64;


    /**
     * 常规Base64加密解密实用工具类
     * 说明:
     * 作者:何杨(heyang78@gmail.com)
     * 创建时间:2010-11-29 上午07:52:01
     * 修改时间:2010-11-29 上午07:52:01
     
    */
    public class Base64SecurityUtil{
        
    /**
         * 得到Base64加密后的字符串
         * 
         * 说明:
         * 
    @param originalString
         * 
    @return
         * 创建时间:2010-11-29 上午07:53:30
         
    */
        
    public static String getEncryptString(String originalString){
            
    byte[] arr = Base64.encodeBase64(originalString.getBytes(), true);
            
    return new String(arr);
        }
        
        
    /**
         * 得到Base64解密后的字符串
         * 
         * 说明:
         * 
    @param encryptString
         * 
    @return
         * 创建时间:2010-11-29 上午07:56:02
         
    */
        
    public static String getDecryptString(String encryptString){
            
    byte[] arr = Base64.decodeBase64(encryptString.getBytes());
            
    return new String(arr);
        }
        
        
    /**
         * 测试
         * 
         * 说明:
         * 
    @param args
         * 创建时间:2010-11-29 上午07:56:39
         
    */
        
    public static void main(String[] args){
            String str
    ="Hello world!你好,世界。";
            
            String str1
    =Base64SecurityUtil.getEncryptString(str);
            System.out.println(
    "经Base64加密后的密文为"+str1);
            
            String str2
    =Base64SecurityUtil.getDecryptString(str1);
            System.out.println(
    "经Base64解密后的原文为"+str2);
            
        }
    }

     

    http://www.blogjava.net/heyang/archive/2010/12/26/341556.html

    展开全文
  • 按:下面的文字涉及早已工程中广泛采用的混合加密方式,对此熟知者就不用往看了,以免浪费时间。 我们知道,现代加密方式有两大类:一类是对称加密方式,其优点是加密速度快,缺点是密钥不便于传递,其中典型...

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

    我们知道,现代加密方式有两大类:一类是对称加密方式,其优点是加密速度快,缺点是密钥不便于传递,其中典型例子是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);
        }
    }


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

    展开全文
  • 根据秘钥的使用方式,将密码分为两种:对称加密(AES)非对称加密(RSA)对称密码中,加密、解密时使用的是同一个密钥:如图所示流程:对称加密流程使用对称加密时,我们不可避免的会遇到秘钥的配送问题,假设...

    55d59a8a920544737f45d3a55e6a15ba.png

    根据秘钥的使用方式,将密码分为两种:

    对称加密(AES)

    非对称加密(RSA)

    在对称密码中,加密、解密时使用的是同一个密钥:

    如下图所示流程:

    对称加密流程

    在使用对称加密时,我们不可避免的会遇到秘钥的配送问题,假设我们Alice要发送一个通过秘钥加密过的消息给到Bob,那么:

    只有将密钥发送给Bob,Bob才能完成解密

    在发送密钥过程中,可能会被Eve窃取密钥,最后Eve也能完成解密

    如下流程:

    消息发送及可能被窃取流程

    对于这种秘钥可能会被第三方窃取的情况,我们可以通过下述几种方案解决:

    事先共享密钥

    密钥分配中心

    Diffie-Hellman密钥交换

    公钥密码

    公钥密码

    秘钥分为加密秘钥和解密秘钥两个,两者不是同一个秘钥,也被称为非对称密码(Asymmetric Cryptography)。

    其中注意点:

    加密密钥,一般是公开的,因此该密钥称为公钥(public key)

    解密密钥,由消息接收者自己保管的,不能公开,因此也称为私钥(private key)

    公钥和私钥是一 一对应的,是不能单独生成的,一对公钥和密钥统称为密钥对(key pair)

    由公钥加密的密文,必须使用与该公钥对应的私钥才能解密

    由私钥加密的密文,必须使用与该私钥对应的公钥才能解密

    公钥密码使用流程

    公钥密码解决了秘钥的配送问题:

    由消息的接收者,生成一对公钥、私钥

    将公钥发给消息的发送者

    消息的发送者使用公钥加密消息

    RSA加密的由来:

    目前使用最广泛的公钥密码算法是RSA

    RSA的名字,由它的3位开发者,即Ron Rivest、Adi Shamir、Leonard Adleman的姓氏首字母组成。

    混合密码系统

    鉴于以上两种加密方式都各有优缺点:

    对称加密缺点:无法很好的解决秘钥配送的优缺点

    公钥密码缺点:加密与解密速度比较慢

    混合密码系统,是将对称密码和公钥密码的优势相结合的方法,解决了公钥密码速度慢的问题,并通过公钥密码解决了对称密码的密钥配送问题。网络上的密码通信所用的SSL/TLS都运用了混合密码系统。

    混合密码的加密:

    会话密钥(session key)

    为本次通信随机生成的临时密钥

    作为对称密码的密钥,用于加密消息,提高速度

    加密步骤(发送消息)

    首先,消息发送者要拥有消息接收者的公钥

    生成会话密钥,作为对称密码的密钥,加密消息

    用消息接收者的公钥,加密会话密钥

    将前2步生成的加密结果,一并发给消息接收者

    发送出去的内容包括

    用会话密钥加密的消息(加密方法:对称密码)

    用公钥加密的会话密钥(加密方法:公钥密码)

    混合密码加密流程

    解密步骤(收到消息)

    消息接收者用自己的私钥解密出会话密钥

    再用第1步解密出来的会话密钥,解密消息

    混合密码解密流程

    混合密码加解密流程小结:

    Alice >>>>> Bob

    发送过程,加密过程

    1.Bob先生成一对公钥、私钥

    2.Bob把公钥共享给Alice

    3.Alice随机生成一个会话密钥(临时密钥)

    4.Alice用会话密钥加密需要发送的消息(使用的是对称密码加密)

    5.Alice用Bob的公钥加密会话密钥(使用的是公钥密码加密,也就是非对称密码加密)

    6.Alice把第4、5步的加密结果,一并发送给Bob

    接收过程,解密过程

    1.Bob利用自己的私钥解密会话密钥(使用的是公钥密码解密,也就是非对称密码解密)

    2.Bob利用会话密钥解密发送过来的消息(使用的是对称密码解密)

    总的来说,AES加密发送者发送消息会将秘钥与秘钥加密过的明文一起发送给接受者,这种情况下无法保证秘钥传输过程中的安全配送问题;而RSA密码则是生成一对不同的公钥和私钥,是成对的。事先接受者先将公钥共享给发送方,然后发送方利用此公钥对明文内容进行加密,随后发送给接受方,接收方收到消息拿配对的私钥进行解密,得到明文,过程复杂,效率较低。

    而混合加密则采取两家之长,即解决了秘钥的安全配送问题,同时也提高了加密与解密效率。混合加密先是接收方将生成的公钥共享给发送方一份,发送方使用此公钥将生成的会话秘钥进行加密,同时发送方使用会话秘钥对明文内容进行加密,完成加密后,将两份加密过的内容一起发送给接收方,接收方接收到消息,先通过配对的私钥解密出会话秘钥,然后使用会话秘钥对密文进行解密,得到明文内容。到此一个完成混合加密流程就算是完成了。

    展开全文
  • 根据秘钥的使用方式,将密码分为两种:对称加密(AES)非对称加密(RSA)对称密码中,加密、解密时使用的是同一个密钥:如图所示流程:对称加密流程使用对称加密时,我们不可避免的会遇到秘钥的配送问题,假设...
  • 面试官:“为什么要用混合加密方式?” 姚小毛:“非对称加密跟对称加密都各有优缺点。 非对称安全性好点,由发送方跟接收方分别持有公钥、私钥。 但是缺点是做大数据量的加密传输时,传输速度会比较慢。” 面试...
  • 针对AES密钥管理中存在的安全性不高和RSA不适合...然后,采用DBMS外层加密方式和字段级的加密粒度,BLE门禁管理系统中实现对“敏感数据”的加/解密处理,以及混合加密体制密钥的管理,保证了数据的安全性与可靠性。
  • 针对手指静脉图像移动互联网传输中可能出现的不安全因素,首先对手指静脉图像的提取进行了改进,采用基于灰度形态学的膨胀运算、加权方式、模糊增强和灰度拉伸的方法获得更加清晰的图像,再对静脉图像采用基于小...
  • 将明文的S盒替换和密钥扩展的S盒替换放在同一个模块里,模块里用同时连续赋值方式实现,从而减少寄存器的个数。把S盒、字节替换、行移位、Tab盒、列混合变换、密钥扩展基本子模块都整合到一个模块中,相比各模块...
  • WinSCP和PUTTY的混合使用

    千次阅读 2018-06-08 20:57:20
    winscp是支持多语言的可连接...但是选择连接方式FTP SFTP时候,还有FTP加密方式选择要注意区别?????之后进行连接可能跳出这个这是要告诉你登录的主机密钥指纹,点 Yes 就保存起来,以后就不会再弹出这...
  • 大概思路是混合加密的方式,即对称加密方式混合非对称加密方式。 非对称加密会更加安全,功能也更强大,但他复杂而且速度慢。 对称加密速度快,但要保证这个公共密钥的正确性和真实性。 所以两者结合,确定...
  • 上网获取信息的过程中,我们接触最多的信息加密传输方式也莫过于 HTTPS 了。每当访问一个站点,浏览器的地址栏中出现绿色图标时,意味着该站点支持 HTTPS 信息传输方式。我们知道 HTTPS 是我们常见的 HTTP 协议与...
  • HTTPS安全哪里?

    2016-07-14 16:32:49
    上网获取信息的过程中,我们接触最多的信息加密传输方式也莫过于 HTTPS 了。每当访问一个站点,浏览器的地址栏中出现绿色图标时,意味着该站点支持 HTTPS 信息传输方式。我们知道 HTTPS 是我们常见的 HTTP 协议与...
  • 【Bugly干货分享】“HTTPS”安全哪里?

    万次阅读 热门讨论 2016-05-30 18:58:52
    背景最近基于兴趣学学习了 HTTPS 相关的知识,此记录学习心得。上网获取信息的过程中,我们接触最多的信息加密传输方式也莫过于 HTTPS 了。每当访问一个站点,浏览器的地址栏中出现绿色图标时,意味着该站点...
  • 上网获取信息的过程中,我们接触最多的信息加密传输方式也莫过于 HTTPS 了。每当访问一个站点,浏览器的地址栏中出现绿色图标时,意味着该站点支持 HTTPS 信息传输方式。我们知道 HTTPS 是我们常见的 HTTP 协议与...
  • 1.HTTPS协议采用的是对称和非对称混合的加密机制,交换密钥环节使用非对称加密方式,之后的建立通信交换报文阶段则使用对称加密。具体的做法是:发送密文的一方通过对方的公钥进行加密处理“对称的密钥“,然后...
  • 计算机网络_wyyhuli

    2020-08-11 14:18:20
    基础概念相关 ...工作方式是客户端与...内容加密:采用混合加密技术,中间者无法直接查看明文内容 验证身份:通过证书认证客户端访问的是自己的服务器 保护数据完整性:防止传输的内容被中间人冒充或者篡改 http与https
  • 古典密码大部分加密方式是利用替换式密码或移项式密码,有时是二者的混合。一般情况,一种古典密码体制包含一个字母表,以及一个操作规则或一种操作设备,到了现代基本不可信赖。 1.凯撒密码 是一种最简单广为人知...
  • 这种方式下,用户可以自行决定对周围的联系人是否信任,并可以决定信任度的高低。用户只接收信任的朋友传送来的公钥,并且这些公钥都带有签名。这种方式反映了社会交往的本质,比较适合一般场合下的安全通信。 本...
  • 最近升级了OpenWrt tag18.06.1,发现增加了许多新功能,一直没时间去研究怎么用。 挂迅雷下载ED2K的时候,偶然发现wifi会慢慢的挂掉,直到新的...还有一种可能性,对应另一种解决方式加密模式选择“混合”...
  • VMware view 5.0讲解

    2012-06-08 11:17:28
    它将用户的会话显示以图像的方式进行压缩传输,只传输变化部分,PCoIP提供多台显示器及2560*1600分辨率和最多4台32位显示器的支持,此外它还支持把字体设置成清晰模式,TCP/UDP混合协议,自适应网络带宽,协议支持...
  • 2.改进复制广告代码功能,各类浏览器可以一键复制代码;3.修正在IE8下混合类型的广告效果不切换的问题;4.管理员登录页面美化;5.新增统计网页文字标题的功能;6.新增右混合广告和左下角混合广告的功能;7....
  • 1、使用“企业邮箱”与外商客户收发邮件,一般情况,收费的“企业邮箱”安全性高于免费邮箱,因为有独立的发信通道和SSL加密传输加持。 2、定期修改密码,密码可以由英文字母,数字,特殊字符混合组成。 3、比如...
  • 多备份企业文件云备份软件是一款电脑和服务器文件自动定时备份和恢复的数据保护软件,它能实现混合备份,既把数据备份本地,也能将数据备份到云端;兼容windows、Linux等主流平台。 对比传统文件备份软件,多备份...

空空如也

空空如也

1 2 3 4 5
收藏数 99
精华内容 39
关键字:

在混合加密方式下