Web通信双向数据加密方案分享

Owen Jia 2017年07月21日 1,995次浏览

一 场景

公司项目在页面和服务器交互使用ajax方式,但是浏览器中是可以看到请求的参数结构:接口、请求和返回参数;这种方式容易被别人通过browser F12查看并模拟攻击接口,由此展开了数据传输两端加密的学习之路;

为了做到request&response的数据全部都加密处理,并希望密钥能够变化生成;

留下脚印,也分享给其他攻城师们参考;

二 使用技术

页面上使用CryptoJS一个插件(https://github.com/brix/crypto-js),要使用CryptoJS.pad.ZeroPadding模式;

CryptoJS (crypto.js) 为 JavaScript 提供了各种各样的加密算法。目前已支持的算法包括:

  • MD5

  • SHA-1

  • SHA-256

  • AES

  • Rabbit

  • MARC4

  • HMAC

    • HMAC-MD5

    • HMAC-SHA1

    • HMAC-SHA256

  • PBKDF2

java使用Cipher,值得注意的是要使用"AES/CBC/NoPadding"模式;

两表的key和iv都有16位的长度;

这三点是调通代码的前提,我也是慢慢调试出来,希望后面的大家少走弯路;以下代码可以直接使用哦!

三 代码直接展示

1)前段js代码


var xxx = {
        key : CryptoJS.enc.Latin1.parse('123456abc2345678'),
        iv : CryptoJS.enc.Latin1.parse('1234567abc345678'),
        encrypt: function(data){
            var encrypted = CryptoJS.AES.encrypt(data,this.key,{iv:this.iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.ZeroPadding});
            return encrypted.toString();
        },
        decrypt: function(data){
            try{
                var decrypted = CryptoJS.AES.decrypt(data,this.key,{iv:this.iv,padding:CryptoJS.pad.ZeroPadding});
                return decrypted.toString(CryptoJS.enc.Utf8);
            }catch(e){
                return data;
            }
        }
    }
return xxx;

2)java端代码


/**
     * AES java-javascript 只支持部填充摸索
     */
    public static final String format = "AES/CBC/NoPadding";
    /**
     * AES 加密key,必须为16位
     */
    public static String key = "123456abc2345678";
    /**
     * AES 偏移量,必须为16位
     */
    public static String iv = "123456abc2345678";
       
    /**
     * 使用AES加密
     * @since 0.0.11
     */
    public static String encrypt(String data){
        if(StringUtils.isEmpty(data)){
            return null;
        }
        try {
            Cipher cipher = Cipher.getInstance(format);
            int blockSize = cipher.getBlockSize();
            byte[] dataBytes = data.getBytes();
            int plaintextLength = dataBytes.length;
            if (plaintextLength % blockSize != 0) {
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }


            byte[] plaintext = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
            
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());


            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);


            return new sun.misc.BASE64Encoder().encode(encrypted);


        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 使用AES解密
     * @since 0.0.11
     */
    public static String decrypt(String data){
        if(StringUtils.isEmpty(data)){
            return null;
        }
        try
        {
            byte[] encrypted1 = new BASE64Decoder().decodeBuffer(data);
            
            Cipher cipher = Cipher.getInstance(format);
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
            
            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
 
            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original);
            return originalString;
        }
        catch (Exception e) {
            e.printStackTrace();
            return data;
        }
    }

3)加密策略

加密算法偏移(eg.iv),服务器每次启动都会随机生成;

加密算法密钥(key),使用结合用户id随机生成;

四 心得体会

做完这个后发现,这段代码的使用场景还是很丰富的;可以使用传统的pc网站,移动端的页面;web和java端的数据交互以后放心多了;

在实际项目中,我的key和iv都是采用服务端启动时自动生成,再加载给页面保证安全性;