美文网首页加密
AES-256-CBC加密

AES-256-CBC加密

作者: VittaWang | 来源:发表于2018-01-16 18:16 被阅读0次

    项目里是给接口的参数加密。
    原则是 : 把参数转化为map对象,得到json 字符串,加密这个字符串。
    想要验证前端加密,后端解密。建议前后端用同样的公钥私钥加密同样一个字符串,比对值,主要是前后端约定好规则就好了。

    ①AES 加密 工具类(java 默认128位,微信用的是256位的。256 位的需要改一个jdk配置;)
    (jdk 8 一下,需要jar 包 ,具体百度 )
    (jdk 8 以上需要执行下面类 静态代码块里的代码)(我是jdk9,所以用了静态语句块里的代码)

    import java.io.UnsupportedEncodingException;
    import java.security.InvalidAlgorithmParameterException;
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;
    import java.security.NoSuchProviderException;
    import javax.crypto.BadPaddingException;
    import javax.crypto.Cipher;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.NoSuchPaddingException;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    
    /**
     * -------------------------------------
     * 作者:vitta @<WVitta@126.com>
     * -------------------------------------
     * 时间:2018/1/12 16:47
     * -------------------------------------
     * 描述: AES-256-CBC 对称加密封装类,应用于接口加密
     * -------------------------------------
     * 备注:
     * -------------------------------------
     */
    public class AES256 {
    
        static {
            /*AES 加密默认128位的key,这里改成256位的(类在下面粘出来了)*/
            UnlimitedKeyStrengthJurisdictionPolicy.ensure();
        }
    
        /*加密算法*/
        private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
    
        /**
         *
         * @param srcData 要加密的数组(String 需要base64 编码)
         * @param key 公钥,32位byte数组
         * @param iv 私钥,16位byte数组
         * @return 加密后的byte数组
         * @throws Exception 找不到加密算法等
         */
        public static byte[] AES_cbc_encrypt(byte[] srcData, byte[] key, byte[] iv) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
            Cipher.getMaxAllowedKeyLength(ALGORITHM);
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv));
            return cipher.doFinal(srcData);
        }
    
        /**
         *
         * @param encData 要解密的数组
         * @param key 公钥
         * @param iv 私钥
         * @return 解密后的byte数组
         * @throws Exception 找不到解密算法等
         */
        private static byte[] AES_cbc_decrypt(byte[] encData, byte[] key, byte[] iv) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
            Cipher.getMaxAllowedKeyLength(ALGORITHM);
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));
            return cipher.doFinal(encData);
        }
    
    }
    
    import javax.crypto.Cipher;
    import java.lang.reflect.Field;
    import java.lang.reflect.Modifier;
    import java.security.NoSuchAlgorithmException;
    import java.security.Permission;
    import java.security.PermissionCollection;
    import java.util.Map;
    
    // https://stackoverflow.com/questions/1179672/how-to-avoid-installing-unlimited-strength-jce-policy-files-when-deploying-an
    /**
     * -------------------------------------
     * 作者:vitta@<WVitta@126.com>
     * -------------------------------------
     * 时间:2018/1/15 11:18
     * -------------------------------------
     * 描述:AES 128 位 转成 256 位,没有限制的方法
     * -------------------------------------
     * 备注:
     * -------------------------------------
     */
    public class UnlimitedKeyStrengthJurisdictionPolicy {
    
    
        private static boolean isRestrictedCryptography() throws NoSuchAlgorithmException {
            return Cipher.getMaxAllowedKeyLength("AES/ECB/NoPadding") <= 128;
        }
    
        private static void removeCryptographyRestrictions() {
            try {
                if (!isRestrictedCryptography()) {
                    System.out.println("Cryptography restrictions removal not needed");
                    return;
                }
                /*
                 * Do the following, but with reflection to bypass access checks:
                 *
                 * JceSecurity.isRestricted = false;
                 * JceSecurity.defaultPolicy.perms.clear();
                 * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
                 */
                Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
                Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
                Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");
    
                Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
                isRestrictedField.setAccessible(true);
                Field modifiersField = Field.class.getDeclaredField("modifiers");
                modifiersField.setAccessible(true);
                modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
                isRestrictedField.set(null, false);
    
                Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
                defaultPolicyField.setAccessible(true);
                PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);
    
                Field perms = cryptoPermissions.getDeclaredField("perms");
                perms.setAccessible(true);
                ((Map<?, ?>) perms.get(defaultPolicy)).clear();
    
                Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
                instance.setAccessible(true);
                defaultPolicy.add((Permission) instance.get(null));
    
                System.out.println("Successfully removed cryptography restrictions");
            } catch (Exception e) {
                System.out.println("Failed to remove cryptography restrictions" + e);
            }
        }
    
        static {
            removeCryptographyRestrictions();
        }
    
        public static void ensure() {
            // just force loading of this class
        }
    }
    

    ② OkHttpClient 的拦截器

    import android.support.annotation.NonNull;
    import android.text.TextUtils;
    import android.util.Base64;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    import cn.xiaomatv.xiaomatv.contants.Params;
    import cn.xiaomatv.xiaomatv.util.AES256;
    import cn.xiaomatv.xiaomatv.util.GsonHelper;
    import okhttp3.FormBody;
    import okhttp3.HttpUrl;
    import okhttp3.Interceptor;
    import okhttp3.MediaType;
    import okhttp3.Request;
    import okhttp3.RequestBody;
    import okhttp3.Response;
    import okio.Buffer;
    
    /**
     * -------------------------------------
     * 作者:vitta@<WVitta@126.com>
     * -------------------------------------
     * 时间:2018/1/16 10:14
     * -------------------------------------
     * 描述:OKHttp 网络请求加密拦截器
     * -------------------------------------
     * 备注:
     * -------------------------------------
     */
    public class EncryptInterceptor implements Interceptor {
    
        /**
         * 获取公钥(得到的都是32位的byte数组)
         */
        public static byte[] getPublicKey() {
            return MessageDigest.getInstance("MD5").digest("gongyue".getBytes());
        }
    
        /**
         * 获取私钥
         *
         * @return 每次都重新获取的私钥(算法是自己定义的)
         */
        public static byte[] getPrivateKey() {
            /*五位随机数*/
            double random = (Math.random() * 10000) + 10000;
            String random_s = String.valueOf(random);
            String s = String.valueOf(System.currentTimeMillis()).substring(0, 10) + "|" + random_s.substring(0, random_s.indexOf("."));
            return s.getBytes();
        }
    
        @Override
        public Response intercept(@NonNull Chain chain) throws IOException {
            Request request = chain.request();
            if (request.method().equals("GET")) {
                Map<String, Object> map = new HashMap<>();
                HttpUrl oldUrl = request.url();
                Set<String> names = oldUrl.queryParameterNames();
                for (String name : names) {
                    map.put(name, oldUrl.queryParameter(name));
                }
                String oldParamsJson = GsonHelper.object2JsonStr(map);
                if (TextUtils.isEmpty(oldParamsJson)) {
                    return chain.proceed(request);
                }
                //对参数的json串加密
                byte[] after_encrypt = new byte[0];
                byte[] privateKey = getPrivateKey();
                try {
                    //加密的字符串需要base64编码 (确保里面没有特殊符号(表情))
                    after_encrypt = AES256.AES_cbc_encrypt(Base64.encode(oldParamsJson.getBytes(), Base64.DEFAULT), getPublicKey(), privateKey);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //组装url,重新发送请求
                String s = oldUrl.toString();
                String url_path = s.substring(0, s.indexOf("?"));
                //接口传递的byte 数组 需要base64 编码
                String url = url_path + "?" + Params.TYPE + "=2&" + Params.HASH + "=" + new String(Base64.encode(privateKey, Base64.DEFAULT)) + "&" + Params.TEXT + "=" + new String(Base64.encode(after_encrypt, Base64.DEFAULT));
                return chain.proceed(request.newBuilder().url(url).build());
            } else if (request.method().equals("POST")) {
                String oldParamsJson = "";
                Map<String, Object> map = new HashMap<>();
                RequestBody requestBody = request.body();
                if (requestBody instanceof FormBody) {//表单提交
                    int size = ((FormBody) requestBody).size();
                    for (int i = 0; i < size; i++) {
                        map.put(((FormBody) requestBody).encodedName(i), ((FormBody) requestBody).encodedValue(i));
                    }
                    oldParamsJson = GsonHelper.object2JsonStr(map);
                } else {//buffer 流
                    Buffer buffer = new Buffer();
                    requestBody.writeTo(buffer);
                    oldParamsJson = buffer.readUtf8();
                }
                if (TextUtils.isEmpty(oldParamsJson)){
                    return chain.proceed(request);
                }
                //对参数的json串加密
                byte[] after_encrypt = new byte[0];
                byte[] privateKey = getPrivateKey();
                try {
                    //加密的字符串需要base64编码 (确保里面没有特殊符号(表情))
                    after_encrypt = AES256.AES_cbc_encrypt(Base64.encode(oldParamsJson.getBytes(), Base64.DEFAULT), getPublicKey(), privateKey);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                Map<String,Object> newMap = new HashMap<>();
                newMap.put(Params.TYPE,"2");
                newMap.put(Params.HASH,new String(Base64.encode(privateKey,Base64.DEFAULT)));
                newMap.put(Params.TEXT,new String(Base64.encode(after_encrypt,Base64.DEFAULT)));
                return chain.proceed(request.newBuilder().post(RequestBody.create(MediaType.parse("JSON"), GsonHelper.object2JsonStr(newMap))).build());
            }
            return null;
        }
    }
    

    注意Base64 : Base64 是编码算法,用于传输;在传递给接口私钥和加密串的时候,不直接传得到的byte数组,应该base64 编码一下再传递;
    要加密的字符串也编码过了。

    ③最后,配置OkHttpClient拦截器

    private static OkHttpClient mOkHttpClient =
                new OkHttpClient.Builder()
                        .addInterceptor(new EncryptInterceptor())
                        .addInterceptor(MyApp.loggingInterceptor)//可以添加各种拦截器,这个是log 拦截器
                        .readTimeout(TimeConstants.READ_TIMEOUT, TimeUnit.MILLISECONDS)//设置读取超时时间
                        .writeTimeout(TimeConstants.WRITE_TIMEOUT, TimeUnit.MILLISECONDS)//设置写的超时时间
                        .connectTimeout(TimeConstants.CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)//设置连接超时时间
                        .build();
    

    相关文章

      网友评论

        本文标题:AES-256-CBC加密

        本文链接:https://www.haomeiwen.com/subject/ayheoxtx.html