当前位置:   article > 正文

aes iv值_基于AES和RSA的数据加密方案

aes_iv

36bdf72d6da6aeecc0bcb3fbac5ed63f.png

前言

在网络通信中,通信传输数据容易被截取或篡改,如果在传输用户隐私数据过程中,被不法分子截取或篡改,就可能导致用户受到伤害,比如被诈骗,所以对客户端与服务端的传输数据加密,是网络通信中必不可少的。

加密过程

数据加密方案

采用公钥密码体制RSA算法对数据加密

现在安全是保证了,但还要考虑到性能问题,由于RSA算法对数据加密时运算速度慢,所以直接把所有传输数据都用RSA加密,会导致网络通信慢,这对用户将是不好的体验。由于对称密钥密码体制中的AES运算速度快且安全性高,所以结合AES对传输数据加密是非常好的方案。

下面是对客户端与服务端通信数据加密比较通用的方案:

客户端生成AES密钥,并保存AES密钥

客户端用AES密钥对请求传输数据进行加密

客户端使用RSA公钥对AES密钥加密,然后把值放到自定义的一个请求头中

客户端向服务端发起请求

服务端拿到自定义的请求头值,然后使用RSA私钥解密,拿到AES密钥

服务端使用AES密钥对请求数据解密

服务端对响应数据使用AES密钥加密

服务端向客户端发出响应

客户端拿到服务端加密数据,并使用之前保存的AES密钥解密

注意:传输数据使用AES密钥加密,RSA公钥对AES密钥加密。RSA公钥和私钥由服务端生成,公钥放在客户端,私钥放在服务端。公钥私钥要私密保护,不能随便给人。

具体流程图如下:

13e16493b3217d3c7d00b02e9aa5221c.png

上面网络通信过程是安全的,可以保证通信数据即使被截取了,也无法获得任何有效信息;即使被篡改了,也无法被客户端和服务端验证通过。

数据加密细节

AES加解密

生成AES密钥和使用AES密钥加密、解密,有下面重要的几点:

1.密钥长度的选择:AES能支持的密钥长度可以为128,192,256位(也即16,24,32个字节),这里选择128位。

2.算法/模式/填充的选择:

算法/模式/填充      字节加密后数据长度     不满16字节加密后长度

AES/CBC/NoPadding        16     不支持

AES/CBC/PKCS5Padding  32     16

AES/CBC/ISO10126Paddind     32     16

AES/CFB/NoPadding         16     原始数据长度

AES/CFB/PKCS5Padding   32     16

AES/CFB/ISO10126Padding     32     16

AES/ECB/NoPadding        16     不支持

AES/ECB/PKCS5Padding  32     16

AES/ECB/ISO10126Padding     32     16

AES/ECB/ISO10126Padding     32     16

AES/OFB/NoPadding        16     原始数据长度

AES/OFB/PKCS5Padding  32     16

AES/OFB/ISO10126Padding     32     16

AES/PCBC/NoPadding      16     不支持

AES/PCBC/PKCS5Padding         32     16

AES/PCBC/ISO10126Padding  32     16

这里选择AES/CBC/PKCS5Padding。

3.添加向量 IvParameterSpec:增强算法强度。

4.编码格式选择:UTF-8。

代码实现:

private final int AES_KEY_LENGTH = 16;//密钥长度16字节,128位

   private final String AES_ALGORITHM = "AES";//算法名字

   private final String AES_TRANSFORMATION ="AES/CBC/PKCS5Padding";//算法/模式/填充

   private final String AES_IV = "0112030445060709";//使用CBC模式,需要一个向量iv,可增加加密算法的强度

   private final String AES_STRING ="abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLOP";

   private final Charset UTF_8 = Charset.forName("UTF-8");//编码格式

   /**

    * 使用AES加密

     *

    * @param aesKey AES Key

    * @param data   被加密的数据

    * @return AES加密后的数据

    */

   private byte[] encodeAES(byte[] aesKey, String data) {

       if (aesKey == null || aesKey.length != AES_KEY_LENGTH) {

           return null;

       }

        SecretKeySpec keySpec = newSecretKeySpec(aesKey, AES_ALGORITHM);

       try {

           Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);

           IvParameterSpec iv = new IvParameterSpec(AES_IV.getBytes(UTF_8));

           cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);

           return cipher.doFinal(data.getBytes(UTF_8));

       } catch (Exception e) {

           Log.d(TAG, e.getMessage(), e);

       }

       return null;

    }

   /**

    * 使用AES解密

    *

    * @param aesKey AES Key

    * @param data   被解密的数据

    * @return AES解密后的数据

    */

   private String decodeAES(byte[] aesKey, byte[] data) {

       if (aesKey == null || aesKey.length != AES_KEY_LENGTH) {

           return null;

       }

       SecretKeySpec keySpec = new SecretKeySpec(aesKey, AES_ALGORITHM);

       try {

           Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);

           IvParameterSpec iv = new IvParameterSpec(AES_IV.getBytes(UTF_8));

           cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);

           return new String(cipher.doFinal(data), UTF_8);

       } catch (Exception e) {

           Log.d(TAG, e.getMessage(), e);

       }

       return null;

    }

   private int getRandom(int count) {

       return (int) Math.round(Math.random() * (count));

    }

   /**

    * 生成AES key

    *

    * @return AES key

    */

   private String initAESKey() {

       StringBuilder sb = new StringBuilder();

       int len = AES_STRING.length();

       for (int i = 0; i < AES_KEY_LENGTH; i++) {

           sb.append(AES_STRING.charAt(getRandom(len - 1)));

       }

       return sb.toString();

    }

现在AES密钥和AES加密、解密都有了,在通常情况下,还会对加密、解密过程进行Base64 编码、解码。

Base64编码,选择 URL_SAFE 标识,也就是“-”和“_”会被替换为“+”和“/”,:

/**

    * 对数据进行Base64编码,使用的是{@link android.util.Base64},而且flags需要使用 {@linkandroid.util.Base64#URL_SAFE,android.util#Base64.NO_PADDING,android.util.Base64#NO_WRAP}。

    *

    * @param input 来源数据

    * @return Base64编码的数据

    */

   private String encodeBase64(byte[] input) {

       return new String(Base64.encode(input, Base64.URL_SAFE |Base64.NO_PADDING | Base64.NO_WRAP), UTF_8);

    }

Base64解码,和编码对应:

/**

    * 对数据进行Base64解码,使用的是{@link android.util.Base64},而且flags需要使用 {@linkandroid.util.Base64#URL_SAFE,android.util.Base64#NO_WRAP},主要是为了和Base64加密对应。

    *

    * @param str 需要解码的数据

    * @return Base64解码后的数据

    */

   private byte[] decodeBase64(String str) {

       return Base64.decode(str.getBytes(UTF_8), Base64.URL_SAFE |Base64.DEFAULT);

    }

RSA公钥加密

RSA公钥是从服务端拿到的,这个公钥不能被泄漏,必须做到安全保护。

使用RSA公钥加密,也有几个重要点:

1.拿到的公钥是Base64 编码后的,所以首先需要对公钥Base64解码。

2.算法/模式/填充的选择:RSA/ECB/PKCS1Padding

3.编码格式选择:UTF-8。

注意:使用RSA公钥加密的流程对应的就是服务端使用RSA私钥解密的流程,所以需要和服务端沟通商量好。

  private final String RSA_PUB_KEY = "服务端给的公钥";

   private final String RSA_TRANSFORMATION ="RSA/ECB/PKCS1Padding";

   /**

    * 公钥加密

    *

    * @param data           要加密的数据

    * @param key            公钥

    * @param transformation 算法/模式/填充

     * @return 加密后的数据

    */

   public byte[] encryptByPublicKey(byte[] data, String key, Stringtransformation)

           throws GeneralSecurityException {

       byte[] keyBytes = Base64.decode(key.getBytes(UTF_8), Base64.NO_WRAP);

       X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

       KeyFactory keyFactory = KeyFactory.getInstance("RSA");

       PublicKey pubKey = keyFactory.generatePublic(keySpec);

       Cipher cipher = Cipher.getInstance(transformation);

       cipher.init(Cipher.ENCRYPT_MODE, pubKey);

       return cipher.doFinal(data);

}

改造方案

前端

1、  生成AES秘钥,并存放到缓存中,逻辑为缓存中如果取不到AES秘钥,则生成,取到则使用;

2、  用AES秘钥对数据进行加密;

3、  用提前分配好的RSA公钥,对AES秘钥串进行加密,并将加密后的AES秘钥密文存放到HTTP头中;

4、  向服务端发起请求;

后端

1、  接受消息后,需要前置处理密文;

2、  接收到的HTTP请求头,取出AES秘钥密文,用RSA秘钥进行解密,得到AES秘钥;

3、  用AES秘钥对数据进行解密,得到JSON串;

4、  接口返回串,需要对数据进行AES加密;

5、  客户端接收到返回值后,对数据进行AES解密;

RSA密钥对生成方法

公私玥可使用OPENSSL工具生成。

在Windows环境下,可自行下载OPENSSL工具( http://www.openssl.org/related/binaries.html)。

在Windows环境下,打开OPENSSL安装目录bin文件下面的openssl.exe。

1)生成RSA私钥:

genrsa -out rsa_private_key.pem 1024

该命令会生成1024位的私钥,生成成功的界面如下:

774e05a70fd84f3f6fd0d0f46462399f.png

此时我们就可以在当前路径下看到rsa_private_key.pem文件了。

2)把RSA私钥转换成PKCS8格式
输入命令pkcs8 -topk8 -inform PEM -in rsa_private_key.pem-outform PEM –nocrypt,并回车
得到生成功的结果,这个结果就是PKCS8格式的私钥,如下图:

3d061551f4300cff811b000c158b706f.png

3) 生成RSA公钥

输入命令rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem,并回车,
得到生成成功的结果,如下图:

6d511ae881564f68b8a5f30db1286fa5.png

此时,我们可以看到一个文件名为rsa_public_key.pem的文件,打开它,可以看到-----BEGINPUBLIC KEY-----开头,

-----ENDPUBLIC KEY-----结尾的没有换行的字符串,这个就是公钥。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/177647
推荐阅读
相关标签
  

闽ICP备14008679号