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解密与加密
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 + "]"; } }