• 接口安全加密传输(对称加密与非对称加密)


    URL特殊字符转码

    对称加密与非对称加密

    DES RSA加密原理

    移动APP接口安全加密设计

    基于令牌方式实现接口参数安全传输

    验签 单向加密

      

    URL转码:

    不管是以何种方式传递url时,如果要传递的url中包含特殊字符,如想要传递一个+,但是这个+会被url会被编码成空格,想要传递&,被url处理成分隔符。

    尤其是当传递的url是经过Base64加密或者RSA加密后的,存在特殊字符时,这里的特殊字符一旦被url处理,就不是原先你加密的结果了。

    url特殊符号及对应的编码:

    符号

    url中的含义

    编码

    +

    URL 中+号表示空格

    %2B

    空格

    URL中的空格可以用+号或者编码

    %20

    /

    分隔目录和子目录

    %2F

    ?

     

     

     

     

     

     

     

    分隔实际的URL和参数

    %3F

    %

    指定特殊字符

    %25

    #

    表示书签

    %23

    &

    URL中指定的参数间的分隔符

    %26

    =

    URL中指定参数的值

    %3D

    rpc远程通讯 实现加密有一些特殊字符  正好和http协议特殊字符形同 导致转成空格 

    localhost:8080/userName=1+1  这样的就接收到的是 1 1  空格代替了!

    Java中提供了HTTP特殊字符转码

      

            String url = "http://127.0.0.1:8080/tranIndex?";
            // 参数转码
            String strParam = "name=" + URLEncoder.encode("1+1", "utf-8");
            String newUrl = url + strParam;
            String result = HttpClientUtils.httpGet(newUrl);
            System.out.println("result:" + result);

    转码: URLEncoder.encode

    解码: URLecoder.decoder

    对称加密和非对称加密:

     防止别人抓包分析HTTP请求 篡改数据

    对称加密:

     加密和解密都是同一个密钥。才加解密过程当中,使用同一个密钥。

      

    客户端加密  服务器端解密  使用的是同一个密钥

    抓包抓到的是 加密后的名问数据 

    对称加密: 加密解密都是同一个密钥。加密过程中,使用同一个密钥。


    对称加密速度快

    服务器端与服务器端进行通信(后台与后台通讯)

    比如: 构建网站与机构网站进行合作的情况可以使用对称加密(同一个网段里面 抓包分析抓不到包)

    密钥是长度8的倍数 

    密码不可能使用对称加密 别人可以反向获取  密码使用单向加密  不可以被逆向破解   密码使用MD5加密 再加盐

    信息加密与密钥管理

    单向散列加密

    散列是信息的提炼,通常其长度要比信息小得多,且为一个固定长度。加密性强的散列一定是不可逆的,这就意味着通过散列结果,无法推出任何部分的原始信息。任何输入信息的变化,哪怕仅一位,都将导致散列结果的明显变化,这称之为雪崩效应。散列还应该是防冲突的,即找不出具有相同散列结果的两条信息。具有这些特性的散列结果就可以用于验证信息是否被修改。
    单向散列函数一般用于产生消息摘要,密钥加密等,常见的有:
    1、MD5(Message Digest Algorithm 5):是RSA数据安全公司开发的一种单向散列算法,非可逆,相同的明文产生相同的密文。
    2、SHA(Secure Hash Algorithm):可以对任意长度的数据运算生成一个160位的数值;
    SHA-1与MD5的比较
    因为二者均由MD4导出,SHA-1和MD5彼此很相似。相应的,他们的强度和其他特性也是相似,但还有以下几点不同:
    1、对强行供给的安全性:最显著和最重要的区别是SHA-1摘要比MD5摘要长32 位。使用强行技术,产生任何一个报文使其摘要等于给定报摘要的难度对MD5是2128数量级的操作,而对SHA-1则是2160数量级的操作。这样,SHA-1对强行攻击有更大的强度。
    2、对密码分析的安全性:由于MD5的设计,易受密码分析的攻击,SHA-1显得不易受这样的攻击。
    3、速度:在相同的硬件上,SHA-1的运行速度比MD5慢。

    1、特征:雪崩效应、定长输出和不可逆。
    2、作用是:确保数据的完整性。
    3、加密算法:md5(标准密钥长度128位)、sha1(标准密钥长度160位)、md4、CRC-32
    4、加密工具:md5sum、sha1sum、openssl dgst。
    5、计算某个文件的hash值,例如:md5sum/shalsum FileName,openssl dgst –md5/-sha

    MD5加密

    在线MD5解密与加密

    http://www.cmd5.com/

    Java操作MD5加密
    MD5加盐实现方式

    一般使用的加盐:

    md5(Password+UserName),即将用户名和密码字符串相加再MD5,这样的MD5摘要基本上不可反查。

    但有时候用户名可能会发生变化,发生变化后密码即不可用了(验证密码实际上就是再次计算摘要的过程)。

    ----------

    因此我们做了一个非常简单的加盐算法,每次保存密码到数据库时,都生成一个随机16位数字,将这16位数字和密码相加再求MD5摘要,然后在摘要中再将这16位数字按规则掺入形成一个48位的字符串。

    在验证密码时再从48位字符串中按规则提取16位数字,和用户输入的密码相加再MD5。按照这种方法形成的结果肯定是不可直接反查的,且同一个密码每次保存时形成的摘要也都是不同的。

    信息加密技术

    对称加密

    对称密码技术:发件人和收件人使用其共同拥有的单个密钥 ,这种密钥既用于加密,也用于解密,叫做机密密钥(也称为对称密钥或会话密钥)。

        能够提供信息机密性(没有密钥信息不能被解密)、完整性(被改变的信息不能被解密)的服务。

        对称式密码学又称:单钥密码学、秘密密钥密码学、会话密钥密码学、私钥密码学、共享秘钥密码学

    常见的对称式加密技术

     DES(数据加密标准):分组式加密,算法源于Lucifer,作为NIST对称式加密标准;64位(有效位56位、校验8位),分组算法

      3DES:128位,分组算法

        IDEA(国际数据加密算法):128位,比DES快,分组算法

        Blowfish:32-448位,算法公开,分组算法

        RC4:流密码,密钥长度可变

        RC5:分组密码,密钥长度可变,最大2048位

        Rijndael:128位/196位/256位

        AES(高级加密标准):DES升级版,算法出自Rinjindael

    对称密码的优点

     用户只需记忆一个密钥,就可用于加密、解密;

     与非对称加密方法相比,加密解密的计算量小,速度快,简单易用,适合于对海量数据进行加密处理 。

    对称密码的缺点

    如果密钥交换不安全,密钥的安全性就会丧失。特别是在电子商务环境下,当客户是未知的、不可信的实体时,如何使客户安全地获得密钥就成为一大难题。

        如果用户较多情况下的密钥管理问题。N*(N-1)/2

        如果密钥多个用户被共享,不能提供抗抵赖性

    对称密码案例

     假设Alice和Bob是认识的,两人为了保证通信消息不被其它人截取,预先约定了一个密码,用来加密在他们之间传送的消息,这样即使有人截取了消息没有密码也无法知道消息的内容。由此便实现了机密性

     

    基于DES实现加密和解密
    DES加密工具类

    DES加密介绍 DES是一种对称加密算法,所谓对称加密算法即:加密和解密使用相同密钥的算法。DES加密算法出自IBM的研究,后来被美国政府正式采用,之后开始广泛流传,但是近些年使用越来越少,因为DES使用56位密钥,以现代计算能力,24小时内即可被破解。虽然如此,在某些简单应用中,我们还是可以使用DES加密算法,本文简单讲解DES的JAVA实现 。注意:DES加密和解密过程中,密钥长度都必须是8的倍数

    保证密钥相同

    public static void main(String args[]) {
            // 待加密内容
            String str = "cryptology";
            // 密码,长度要是8的倍数
            String password = "95880288";
    
            byte[] result = DES.encrypt(str.getBytes(), password);
            System.out.println("加密后:" + new String(result));
            // 直接将如上内容解密
            try {
                byte[] decryResult = DES.decrypt(result, password);
                System.out.println("解密后:" + new String(decryResult));
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }

    对称加密优缺点:

      反编译出密钥来 就完蛋了    同一个密钥

      对称加密的使用场景 服务器端与服务器端进行加密 比较好一些  没法用抓包

      

    非对称加密

     使用一对密钥:一个用于加密信息,另一个则用于解密信息。

        两个密钥之间存在着相互依存关系:即用其中任一个密钥加密的信息只能用另一个密钥进行解密。

        其中加密密钥不同于解密密钥,公钥加密私钥解密,反之也可私钥加密公钥解密。

        密钥依据性质划分,将其中的一个向外界公开,称为公钥;另一个则自己保留,称为私钥。公钥(Public key)常用于数据加密(用对方公钥加密)或签名验证(用对方公钥解密),私钥(Private key)常用于数据解密(发送方用接收方公钥加密)或数字签名(用自己私钥加密)。

        机密性、完整性、抗抵赖性

     

    1.使用过程:

    乙方生成两把密钥(公钥和私钥)

    甲方获取乙方的公钥,然后用它对信息加密。

    乙方得到加密后的信息,用私钥解密,乙方也可用私钥加密字符串

    甲方获取乙方私钥加密数据,用公钥解密

    优点:难破解

    缺点: 加密速度慢

    常用算法:

    RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)

     移动APP接口安全 (HTTPs传输,使用令牌,使用非对称加密)

     对称加密的话 对称加密情况下 密钥都是相同的 反编译移动打包apk

     既然移动app端不能用对称加密,用非对称加密

    非对称加密。 私钥(不公开)和公钥(公开)

    首先非对称加密,是一对密钥(必须组合公钥和密钥进行组合)

     1,使用第三方工具生成非对称加密

     2,如果使用公钥加密,必须使用私钥解密

          如果使用私钥加密, 那么必须要使用公钥解密。

         安全但是效率低,应用场景: 支付宝 第三方支付对接

      移动APP             服务器端   

     公钥加密 私钥解密!

     移动App端使用公钥加密  移动app客户端保存公钥        app使用公钥加密        服务器端保存私钥  服务器端使用私钥进行保密   私钥在服务器端很给力的 黑客获取不到

      

    app和服务器 有个约定   生成一堆非对称密钥对 服务器端保存私钥  客户端保存公钥

    公钥A 私钥B

    公钥1 公钥2   

    必须成对的才可以解密!

     RSA加密:

     实际过程中,开发人员先把公钥私钥生成好。服务器端保存私钥,公钥发给客户端。

    生成公钥和私:

    /**
         * 生成公钥和私钥
         */
        public static void generateKey() {
            // 1.初始化秘钥
            KeyPairGenerator keyPairGenerator;
            try {
                keyPairGenerator = KeyPairGenerator.getInstance("RSA");
                SecureRandom sr = new SecureRandom(); // 随机数生成器
                keyPairGenerator.initialize(512, sr); // 设置512位长的秘钥
                KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 开始创建
                RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
                RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
                // 进行转码
                publicKey = Base64.encodeBase64String(rsaPublicKey.getEncoded());
                // 进行转码
                privateKey = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
            } catch (NoSuchAlgorithmException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    测试:

    import javax.crypto.Cipher;
    
    
    public class Test001 {
    
        public static void main(String[] args) {
    
            // 实现步骤:
            // 1.生成公钥和私钥密钥对
            RSAUtil.generateKey();
            System.out.println("私钥:" + RSAUtil.privateKey);
            System.out.println("公钥:" + RSAUtil.publicKey);
            String content = "toov5comeon";
            System.out.println("加密前:"+content);
            // 2.使用公钥进行加密
            String encryptByPublicKey = RSAUtil.encryptByPublicKey(content, RSAUtil.publicKey, Cipher.ENCRYPT_MODE); //最后一个参数表示进行加密     返回加密后的结果 
            System.out.println("加密后:" + encryptByPublicKey);
            String encryptByprivateKey = RSAUtil.encryptByprivateKey(encryptByPublicKey, RSAUtil.privateKey,
                    Cipher.DECRYPT_MODE);
            // 3.使用私钥进行解密
            System.out.println("解密后:" + encryptByprivateKey);
            // 正常在开发中的时候,后端开发人员生成好密钥对,服务器端保存私钥 客户端保存公钥
    
        }
    
    }

    注意了 这可是有加号的  在RPC远程调用时候 可能这种特殊字符会被转换了哦 特殊字符处理

    防止抓包篡改数据:

      基于令牌的方式实现参数安全传输:

       

     A (主站)    B(支付)

     在跳转支付系统之前生成参数令牌

    A调用B 支付系统生成令牌参数,返回参数令牌。

    A跳转到B使用令牌方式跳转

    支付系统接收令牌参数获取对应的userId和全额参数

     访问时候,携带moeny userId ,服务器获取到然后生成token,并存入到redis(实际是存在数据库中) 中 key为token  value为money  userId

     调用支付时候 携带 token,服务器端通过token去查询出money userid 进行操作

     核心代码:

    import java.util.UUID;
    
    import org.apache.commons.lang.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.fasterxml.jackson.databind.deser.Deserializers.Base;
    import com.itmayiedu.base.BaseApiService;
    import com.itmayiedu.base.BaseRedisService;
    import com.itmayiedu.base.ResponseBase;
    
    @RestController
    public class PayController extends BaseApiService {
        @Autowired
        private BaseRedisService baseRedisService;
    
        private static final Long TOKENTIME = (long) (30 * 60);
    
        // 先获取参数接口,返回令牌
        // 使用令牌传递参数 (不是前端调用是服务器调用)
        @RequestMapping("/getPayToken")
        public String getPayToken(Long userId, Long money) {
            // 生成令牌  令牌是随便生成的
            String payToken = UUID.randomUUID().toString();
            // 存放在redis中
            baseRedisService.setString(payToken, userId + "---" + money, TOKENTIME);
            return payToken;
        }
    
        @RequestMapping("/pay")
        public ResponseBase pay(String payToken) {  //进行支付时候 就传递上次生成的token就好了 
            if (StringUtils.isEmpty(payToken)) {
                return setResultError("token 不能为空!");
            }
            String result = (String) baseRedisService.getString(payToken);
            if (StringUtils.isEmpty(result)) {
                return setResultError("参数不能为空!");
            }
            // 直接处理操作数据库
            return setResultSuccessData(result);
        }
    
    }

    辅助代码:

    Base:

    import org.springframework.stereotype.Component;
    
    import com.itmayiedu.utils.Constants;
    
    @Component
    public class BaseApiService {
    
        public ResponseBase setResultError(Integer code, String msg) {
            return setResult(code, msg, null);
        }
    
        // 返回错误,可以传msg
        public ResponseBase setResultError(String msg) {
            return setResult(Constants.HTTP_RES_CODE_500, msg, null);
        }
    
        // 返回成功,可以传data值
        public ResponseBase setResultSuccessData(Object data) {
            return setResult(Constants.HTTP_RES_CODE_200, Constants.HTTP_RES_CODE_200_VALUE, data);
        }
    
        public ResponseBase setResultSuccessData(Integer code, Object data) {
            return setResult(code, Constants.HTTP_RES_CODE_200_VALUE, data);
        }
    
        // 返回成功,沒有data值
        public ResponseBase setResultSuccess() {
            return setResult(Constants.HTTP_RES_CODE_200, Constants.HTTP_RES_CODE_200_VALUE, null);
        }
    
        // 返回成功,沒有data值
        public ResponseBase setResultSuccess(String msg) {
            return setResult(Constants.HTTP_RES_CODE_200, msg, null);
        }
    
        // 通用封装
        public ResponseBase setResult(Integer code, String msg, Object data) {
            return new ResponseBase(code, msg, data);
        }
    
    }

    redis:

    import java.util.concurrent.TimeUnit;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Component;
    
    @Component
    public class BaseRedisService {
    
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        public void setString(String key, Object data, Long timeout) {
            if (data instanceof String) {
                String value = (String) data;
                stringRedisTemplate.opsForValue().set(key, value);
            }
            if (timeout != null) {
                stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
            }
        }
    
        public Object getString(String key) {
            return stringRedisTemplate.opsForValue().get(key);
        }
    
        public void delKey(String key) {
            stringRedisTemplate.delete(key);
        }
    
    }

    response:

    import lombok.Getter;
    import lombok.Setter;
    import lombok.extern.slf4j.Slf4j;
    
    @Getter
    @Setter
    @Slf4j
    public class ResponseBase {
    
        private Integer rtnCode;
        private String msg;
        private Object data;
    
        public ResponseBase() {
    
        }
    
        public ResponseBase(Integer rtnCode, String msg, Object data) {
            super();
            this.rtnCode = rtnCode;
            this.msg = msg;
            this.data = data;
        }
    
        public static void main(String[] args) {
            ResponseBase responseBase = new ResponseBase();
            responseBase.setData("123456");
            responseBase.setMsg("success");
            responseBase.setRtnCode(200);
            System.out.println(responseBase.toString());
            log.info("itmayiedu...");
        }
    
        @Override
        public String toString() {
            return "ResponseBase [rtnCode=" + rtnCode + ", msg=" + msg + ", data=" + data + "]";
        }
    
    }
  • 相关阅读:
    Appuim学习路-Appuim介绍
    mac下将根目录/更改组到普通用户,导致sudo不能用
    shell获取函数的返回值
    grep匹配字符串出现的次数
    python实现测试报告的bug统计
    ios开发者账号、证书相关内容
    java基础三 [深入多态,接口和多态](阅读Head First Java记录)
    java基础二(阅读Head First Java记录)
    mac下搭建appium
    窗口管理器 Openbox 入门指南
  • 原文地址:https://www.cnblogs.com/toov5/p/10321364.html
Copyright © 2020-2023  润新知