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

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 + "]";
    }

}

猜你喜欢

转载自www.cnblogs.com/toov5/p/10321364.html