SpringCloud使用RAS+AES实现加密传输

数据传输加密

在开发应用过程中,客户端与服务端经常需要进行数据传输,涉及到重要隐私信息时,开发者自然会想到对其进行加密,即使传输过程中被“有心人”截取,也不会将信息泄露。对于加密算法,相信不少开发者也有所耳闻,比如MD5加密,Base64加密,DES加密,AES加密,RSA加密等等。在这里我主要向大家介绍一下我在开发过程中使用到的加密算法,RSA加密算法+AES加密算法。简单地介绍一下这两种算法吧

RSA

之所以叫RSA算法,是因为算法的三位发明者RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准,主要的算法原理就不多加介绍,如果对此感兴趣的话,建议去百度一下RSA算法。需要了解的是RSA算法属于非对称加密算法,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。简单的说是“公钥加密,私钥解密;私钥加密,公钥解密”。

AES

高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。

客户端传输重要信息给服务端,服务端返回的信息不需加密的情况

客户端传输重要信息给服务端,服务端返回的信息不需加密,例如绑定银行卡的时候,需要传递用户的银行卡号,手机号等重要信息,客户端这边就需要对这些重要信息进行加密,使用RSA公钥加密,服务端使用RSA解密,然后返回一些普通信息,比如状态码code,提示信息msg,提示操作是成功还是失败。这种场景下,仅仅使用RSA加密是可以的。

客户端传输重要信息给服务端,服务端返回的信息需加密的情况

客户端传输重要信息给服务端,服务端返回的信息需加密,例如客户端登录的时候,传递用户名和密码等资料,需要进行加密,服务端验证登录信息后,返回令牌token需要进行加密,客户端解密后保存。此时就需要结合这两种算法了。至于整个流程是怎样的,在下面会慢慢通过例子向你介绍,因为如果一开始就这么多文字类的操作,可能会让读者感到一头雾水。

在Spring Cloud 项目中使用 RSA+AES 进行加密

粗略总结一下过程就是:客户端存放 RSA 的公钥,将 AES 的秘钥(客户端随机生成)通过RSA的公钥加密,业务数据使用AES 的秘钥进行加密,然后数据传送到服务端。服务端使用RSA私钥对RSA公钥加密过得AES秘钥进行解密得到AES秘钥。然后使用AES秘钥对业务数据解密,返回数据的时候使用AES 秘钥进行加密。
在Spring Cloud中加解密放在Gateway中,我的项目中gateway 使用的是zuul。

1.首先在gateway中加一个前置过滤器

@Slf4j
@Component
public class SignDecryptFilter extends BaseFilter {

    @Autowired
    private CheckRequireDecryptUtils checkRequireDecryptUtils;
//前置过滤器
    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 3;
    }

    @Override
    public boolean shouldFilter() {
        /*return checkRequireDecryptUtils.checkRequireDecrypt();*/
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        getLogger().info("SignDecryptFilter>>>Options>>>{}", request.getMethod());
        if (!RequestContext.getCurrentContext().getBoolean(NEXT_SHOULD_FILTER)) {
            return false;
        }
        if (request.getMethod().equals("OPTIONS")) {

            return false;
        }
        return RequestContext.getCurrentContext().getBoolean(NEXT_SHOULD_FILTER);
    }

    @Override
    public Object run() {
        //验签并解密数据
        checkRequireDecryptUtils.checkAnddecryptWith();
        return null;
    }
}
public void checkAnddecryptWith() {
        RequestContext ctx = RequestContext.getCurrentContext();
        try {
            this.checkAnddecrypt();
//      throw new DecryptException(DecryptException.IllegalInputStream);
        } catch (Exception e) {
            ctx.setSendZuulResponse(false);//不需要进行路由,也就是不会调用api服务提供者
            ctx.setResponseStatusCode(401);
            ctx.set("isOK", false);//可以把一些值放到ctx中,便于后面的filter获取使用

            ResultModel model = new ResultModel();
            model.setMsg("异常信息:" + e.getMessage());
            model.setCode(String.valueOf(DecryptException.IllegalInputStream));
            model.setData("异常类:" + e.getClass());
            ctx.setResponseBody(JSON.toJSONString(model));

      /*ctx.setResponseBody(ResultFactory
          .resultMsg(Commond.R_FAIL, "异常类:" + e.getClass(),
              "异常信息:" + e.getMessage()));//返回内容给客户端 // 返回错误内容*/
            //ctx.setResponseBody(e.getMessage());//返回内容给客户端 // 返回错误内容
            ctx.getResponse().setContentType("application/json;charset=UTF-8");
        }
    }

前端参数转换的实体类

@Data
public class RsaRequestDto {

    /**
     * 时间戳
     */
    private String timestamp;

    /**
     * 随机字符串
     */
    private String nonce;

    /**
     * 业务数据加密后的密文
     */
    private String encrypt;

    /**
     * 被RSA公钥加密过得AES秘钥字符串
     */
    private String echokey;

    /**
     * 签名
     */
    private String signature; 
}
public void checkAnddecrypt() throws Exception {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        InputStream in = null;
        String body = null;
        try {
            in = ctx.getRequest().getInputStream();
            body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
        } catch (IOException e) {
            logger.error("解密接口数据获取入参异常,异常类{},异常信息{}", e.getClass(), e.getMessage());
            throw new DecryptException(DecryptException.IllegalInputStream);
        } 
        RsaRequestDto rsaRequestDto = null;
        if (!StringUtils.isEmpty(request.getContentType()) && request.getContentType()
                .contains("application/json")) {
            try {
                rsaRequestDto = JSONObject.parseObject(body, RsaRequestDto.class);
            } catch (Exception e) {
                logger.error("接口解密,解析json对象失败,将直接调用业务接口");
            }
        }

        if (!this.checkRequireDecrypt()) {
            //校验是否输入了加密参数
            if (rsaRequestDto != null && !StringUtils.isEmpty(rsaRequestDto.getEncrypt())) {
                throw new DecryptException(DecryptException.IllegalInputStream);
            }
            return;
        }

        //验证签名并解密业务数据
        String newBody = commonEncryption.checkAnddecrypt(rsaRequestDto);

        final byte[] reqBodyBytes = newBody.getBytes();
        ctx.setRequest(new HttpServletRequestWrapper(request) {
            @Override
            public ServletInputStream getInputStream() throws IOException {
                return new ServletInputStreamWrapper(reqBodyBytes);
            }

            @Override
            public int getContentLength() {
                return reqBodyBytes.length;
            }

            @Override
            public long getContentLengthLong() {
                return reqBodyBytes.length;
            }
        });
    }
  public String checkAnddecrypt(RsaRequestDto rsaRequestDto) throws Exception {
//判断是否为空,为空抛异常
        if (rsaRequestDto == null || StringUtils.isBlank(rsaRequestDto.getEncrypt()) || StringUtils
                .isBlank(rsaRequestDto.getEchokey()) || StringUtils.isBlank(rsaRequestDto.getNonce())
                || StringUtils.isBlank(rsaRequestDto.getSignature()) || StringUtils
                .isBlank(rsaRequestDto.getTimestamp())) {
            throw new DecryptException(DecryptException.IllegalInputStream);
        }

        //验证签名
        String signOriginStr = getStrForSign(rsaRequestDto.getTimestamp(), rsaRequestDto.getNonce(),
                rsaRequestDto.getEncrypt());
        boolean status = RSACoder
                .verify(signOriginStr.getBytes(), appConfigProperties.getPublicKeyForSign(),
                        rsaRequestDto.getSignature());
        if (!status) {
            throw new DecryptException(DecryptException.ValidateSignatureError);
        }

        //通过RSA的私钥解密获得前端 RSA公钥加密过得AES秘钥
        byte[] key = RSACoder.decryptByPrivateKey(rsaRequestDto.getEchokey(),
                appConfigProperties.getPrivateKeyForDataKey());
        String echokeyStr = new String(key, CHARSET);
//    System.out.println("aes 加密随机key    " + echokeyStr);

        //使用AES 的秘钥解密业务数据
        String data = AesUtils.decrypt(rsaRequestDto.getEncrypt(), echokeyStr, echokeyStr);
//    System.out.println(data);

        return data;
    }

返回加密数据的时候再使用个filter 做一下返回后置处理

public RsaRequestDto encrypt(String data) throws Exception {
        RsaRequestDto dto = new RsaRequestDto();

        String timestamp = String.valueOf(System.currentTimeMillis());
        String nonce = RSACoder.getRandomStr();

        //生成加密业务数据随机密匙(使用RSA 的算法生成一个AES的秘钥)
        String echokeyStr = RSACoder.getRandomStr();
        //加密业务数据随机密匙(使用RSA私钥加密AES秘钥)
        String echokey = RSACoder
                .encryptBASE64(
                        RSACoder.encryptByPublicKey(echokeyStr, appConfigProperties.getPublicKeyForDataKey()));

        //使用AES秘钥加密业务数据
        String encrypt = AesUtils.encrypt(data, echokeyStr, echokeyStr);

        //生成签名
        String signature = RSACoder
                .sign(getStrForSign(timestamp, nonce, encrypt).getBytes(CHARSET),
                        appConfigProperties.getPrivateKeyForSign());

        dto.setTimestamp(timestamp);
        dto.setNonce(nonce);
        //AES秘钥加密过得业务数据
        dto.setEncrypt(encrypt);
        //被加密过得AES秘钥
        dto.setEchokey(echokey);
        dto.setSignature(signature);

        return dto;
    }

你可能感兴趣的:(SpringCloud使用RAS+AES实现加密传输)