Android 网络传输 加密与验证

来源:互联网 发布:matlab plot 矩阵 编辑:程序博客网 时间:2024/05/16 06:13

    最近开始接触到网络传输加密与验证,起初很蒙圈,经过投入一定时间的摸索,终于打通了整个流程,在此记录下笔记。

    首先说说加密与验证涉及到的一些概念以及这样子做的必要性。至于为什么要加密这个很容易理解,但如何优雅的加密,使别有用心的攻击者破不了,同时也不影响用户的使用。加密的方式就想得很重要,从加密解密的密钥的个数来说,加密的方式有两类:对称加密与非对称加密。 对称加密的钥匙就一把,加密用它,解密也用它,就像家里锁门一样,锁门用它,开门也用它,别人拿不到你这把锁,就开不了门。而非对称加密来说,它有两把钥匙,一把密钥,一把公钥,用密钥加密的只能用公钥才能解密,用公钥加密的只有密钥才能解密,这个理解稍微复杂点,至于为什么这么做,生活中很难找出这样子的场景,但是它的存在在网络传输中起着不可替代的重要作用。举例来说,假如有一台服务器,面对很多个客户端用户,客户端通过注册的方式可以增加,客户端与服务端需要通信,这种情况下,假如使用对称加密,一方加密,另一方解密,密钥也必须通过网络传输,因为这不像我们人沟通那么简单,可以直接口头悄悄话就告诉了,大多图谋不轨的都是半路劫杀,假如其他攻击服务器截取了信息并伪装成这台服务器,客户端搞不清楚啊,所以除了加密,如何确定每条信息在传输中没有被修改而且每条信息都是服务器发过来来得。这样子的话,如何保证信息半路没有被串改,并且保证确认对方身份,对称加密就显得太不靠谱。这种情况下,非对称加密就应运而生。一般来讲,密钥掌握在服务器手中,公钥在客户端中,而公钥是服务器在和客户端开始通信的时候和证明服务器身份的证书一并传送到客户端手中的,而这个证书是什么呢。就是权威机构给颁发的一种证书,就像人的身份证一样,一证一人,只不过这个证书需要申请和付费的,服务器申请拿到了这个证书,攻击者是没办法乔装的,获取将要传输的内容 hash值,生成签名文件,将签名文件和内容都通过密钥加密, 客服端收到信息使用公钥解密 这样子就可以确认对方身份而且保证信息的没被修改。

   上面简单的尝试说了一些概念,接下来说下项目流程,其中主要是学习luaviewSDK的时候接触的,光加密这一快,就够啃好久,淘宝的技术还是很牛逼的。首先使用Openssl生成RSA(一种非对称加密算法)密钥对,它是直接公钥密钥封装到两个文件中,可以用密钥生成证书并去权威机构申请(本次中就没有测试这块了),现在开始传输内容,将要传输的内容使用aes(一种对称加密方式)加密,而这个ase加密算法的密码呢?使用的是公钥的文件的输入流的md5码。将使用aes加密过的文本使用非对称加密方法即RSA的私钥文件生成要传输文件的签名文件,这样就产生了两个文件,一个签名文件,一个加密后的内容文件。传输到了客户端,客户端使用公钥首先去验证签名文件,查看文件是否被修改以及确认发送者的身份,通过了然后再取公钥的md5码,去解密通过aes算法加了密的文本文件, 这样子就获取到了真正要传输内容,够复杂吧,现在贴下操作过程:

   1.使用openssl生成RSA的密钥对,下载地址:http://slproweb.com/products/Win32OpenSSL.html

      创建私钥:
           openssl genrsa -out private.pem 1024   //密钥长度,1024觉得不够安全的话可以用2048,但是代价也相应增大
      创建公钥:
           openssl rsa -in private.pem -pubout -out public.pem
      创建证书请求:
         //使用私钥生成一个证书请求,证书请求提交到CA认证中心后会得到一份证书,当然,测试用时,就不必提交CA认证中心(收费)
          openssl req -new -out cert.csr -key private.pem
     自签署根证书:
        //自签署,就是不通过CA认证中心自行进行证书签名,这里用是x509
        openssl x509 -req -in cert.csr -out public.der -outform der -signkey private.pem -days 3650 //10年有效 

  2、获取公钥输入流的md5码 ,使用该码作为文本aes算法加密解密的密码。生成加密文件 此处主要是贴 获取文件的md5码和aes的加密代码:
       
/** * 获取md5码 * * @param s * @param method encrypt type * @return */private static byte[] encryptByMd5(byte[] s) {    try {        MessageDigest digest = MessageDigest.getInstance("MD5");        digest.update(s);        return digest.digest();    } catch (NoSuchAlgorithmException e) {        e.printStackTrace();    }    return null;}
/** * 使用AES算法对二进制数组加密 * @param byteContent 需要加密的内容 * @param byteKey  AES的密码 * @return */private  byte[] encryptByAES(byte[] byteContent,  byte[] byteKey) {    try {        SecretKeySpec key = new SecretKeySpec(byteKey, "AES");        Cipher cipher = Cipher.getInstance(algorithmStr_Aes);//algorithmStr        cipher.init(Cipher.ENCRYPT_MODE, key);//   ʼ        byte[] result = cipher.doFinal(byteContent);        return result;    } catch (NoSuchAlgorithmException e) {        e.printStackTrace();    } catch (NoSuchPaddingException e) {        e.printStackTrace();    } catch (InvalidKeyException e) {        e.printStackTrace();    } catch (IllegalBlockSizeException e) {        e.printStackTrace();    } catch (BadPaddingException e) {        e.printStackTrace();    }    return null;}


3、以上能获取到需要的加密过的传输内容了,接下来就是对加密后内容生成签名文件, 主要是贴 使用私钥对加密文本生成签名的代码:
   
/** * 从文件中加载私钥 * @return 是否成功 * @throws Exception */public void loadPrivateKey(InputStream in) throws Exception{    try {        BufferedReader br= new BufferedReader(new InputStreamReader(in));        String readLine= null;        StringBuilder sb= new StringBuilder();        while((readLine= br.readLine())!=null){            if(readLine.charAt(0)=='-'){                continue;            }else{                sb.append(readLine);                sb.append('\r');            }        }        loadPrivateKey(sb.toString());    } catch (IOException e) {        throw new Exception("私钥数据读取错误");    } catch (NullPointerException e) {        throw new Exception("私钥输入流为空");    }}
public void loadPrivateKey(String privateKeyStr) throws Exception{    try {        BASE64Decoder base64Decoder= new BASE64Decoder();        byte[] buffer= base64Decoder.decodeBuffer(privateKeyStr);        PKCS8EncodedKeySpec keySpec= new PKCS8EncodedKeySpec(buffer);        KeyFactory keyFactory= KeyFactory.getInstance("RSA");        privateKey= (RSAPrivateKey) keyFactory.generatePrivate(keySpec);    } catch (NoSuchAlgorithmException e) {        throw new Exception("无此算法");    } catch (InvalidKeySpecException e) {        throw new Exception("私钥非法");    } catch (IOException e) {        throw new Exception("私钥数据内容读取错误");    } catch (NullPointerException e) {        throw new Exception("私钥数据为空");    }}
/** * 根据RSA的密钥给文件二进制数组生成签名数组 * @param infomation * @return */public  byte[] getFileSignByPrivateKey(byte[] infomation){    byte[] publicInfo=null;    RSAUtil rsaUtil = new RSAUtil(context);    try {        Signature mySig = Signature.getInstance(algorithm);//用指定算法产生签名对象        mySig.initSign(privateKey);  //用私钥初始化签名对象        mySig.update(infomation);  //将待签名的数据传送给签名对象        publicInfo = mySig.sign();  //返回签名结果字节数组    } catch (Exception e) {        e.printStackTrace();    }    return publicInfo;}
4、以上我们就对要传输的内容加过密和生成了相应签名了,客户端拿到了这些文件后,接下来是要验证签名和解密,首先我们使用公钥来验证签名,贴主要验证签名代码:

/** * 验证签名 * @param content 加密过的内容 * @param publicKey 公钥内容 * @param sign   加密内容的签名文件内容 * @return */public  boolean rsa(byte[] content, byte[] publicKey, byte[] sign) {    InputStream inputStream = null;    try {            inputStream = new ByteArrayInputStream(publicKey);            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");            final Certificate cert = certFactory.generateCertificate(inputStream);            PublicKey   pk = cert.getPublicKey();        final Signature sig = Signature.getInstance("SHA1WithRSA");        sig.initVerify(pk);        sig.update(content);        if (sig.verify(sign)) {            System.out.println("验证成功");            return true;        } else {            System.out.println("验证失败");            return false;        }    } catch (SignatureException e) {        e.printStackTrace();    } catch (InvalidKeyException e) {        e.printStackTrace();    } catch (NoSuchAlgorithmException e) {        e.printStackTrace();    } catch (Exception e) {        e.printStackTrace();    } finally {        try {            if (inputStream != null) {                inputStream.close();            }        } catch (Exception e) {            e.printStackTrace();        }    }    return false;}

5、如果验证成功,接下来是使用公钥的MD5码来解密使用aes算法加密过的传输内容了。

private String  decrypt(byte[] encrypted) {    try {
        final InputStream inputStream = context.getAssets().open("luaview/luaview_rsa_public_key.der");
        byte[]  keys = IOUtil.toBytes(inputStream);        byte[]  md5 = EncryptUtil.md5(keys);        System.out.println("aes解密md5-----------:"+byteArrayToString(md5));        SecretKeySpec key = new SecretKeySpec(md5, "AES");        Cipher cipher = Cipher.getInstance(algorithmStr);//algorithmSt        cipher.init(Cipher.DECRYPT_MODE, key);//   ʼ        byte[] data= cipher.doFinal(encrypted);        return  new String(data);    } catch (NoSuchAlgorithmException e) {        e.printStackTrace();    } catch (NoSuchPaddingException e) {        e.printStackTrace();    } catch (InvalidKeyException e) {        e.printStackTrace();    } catch (Exception e) {        e.printStackTrace();    }    return null;}
以上就是大概的流程了,最后感叹一下,发现写博客真不是一件容易的事,要想写好就更难了。
 主要参考:http://blog.sina.com.cn/s/blog_6f27f7d20102wmw5.html
           http://blog.csdn.net/chaijunkun/article/details/7275632/

1 0
原创粉丝点击