密码安全
摘要加密
摘要是哈希值,我们通过散列算法比如MD5算法就可以得到这个哈希值。摘要只是用于验证数据完整性和唯一性的哈希值,不管原始数据是什么样的,得到的哈希值都是固定长度的。 不管原始数据是什么样的,得到的哈希值都是固定长度的,也就是说摘要并不是原始数据加密后的密文,只是一个验证身份的令牌。所以我们无法通过摘要解密得到原始数据。
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函 数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由 美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套 算法的程序在 RFC 1321 标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于 需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞 (collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。 MD5存在一个缺陷,只要明文相同,那么生成的MD5码就相同,于是攻击者就可以通过撞库的方式 来破解出明文。加盐就是向明文中加入指定字符,主要用于混淆用户、并且增加MD5撞库破解难度,这样一来即使撞库破解,知道了明文,但明文也是混淆了的,真正需要用到的数据也需要从明文中摘取, 摘取范围、长度、摘取方式都是个谜,如此一来就大大增加了暴力破解的难度,使其几乎不可能破解。
摘要加密算法
- MD5算法法(MD2 、MD4、MD5)
- SHA算法(SHA1、SHA256、SHA384、 SHA512)
- HMAC算法
特点
- 任何数据加密,得到的密文长度固定。
- 密文是无法解密的(不可逆)。
验签
验签其实就是签名验证,MD5加密算法经常用于签名安全验证。疑问:要是密文也一起改了再传过去呢?
代码示例
import org.apache.commons.codec.digest.DigestUtils;
public class MD5Util {
/**
* MD5方法
*
* @param text 明文
* @return 密文
* @throws Exception
*/
public static String md5(String text) throws Exception {
//加密后的字符串
String encode = DigestUtils.md5Hex(text);
return encode;
}
/**
* MD5方法
*
* @param text 明文
* @param key 盐
* @return 密文
* @throws Exception
*/
public static String md5(String text, String key) throws Exception {
//加密后的字符串
String encode = DigestUtils.md5Hex(text + key);
return encode;
}
/**
* MD5验证方法
*
* @param text 明文
* @param key 密钥/盐
* @param md5 密文
* @return true/false
* @throws Exception
*/
public static boolean verify(String text, String key, String md5) throws Exception {
//根据传入的密钥进行验证
String md5Text = md5(text, key);
return md5Text.equalsIgnoreCase(md5);
}
public static void main(String[] args) throws Exception {
String test = "admin";
String key = "tiger";
String md5 = "bcf918bd662986b3bcab0f9a32a21e1d";
String md5Str = md5(test, key);
System.out.println(md5Str);
boolean passOrNot = verify(test, key, md5);
System.out.println(passOrNot);
}
}
Base64
Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印 字符来表示二进制数据的方法。
解决编码问题
代码示例
import java.util.Base64;
public class Base64Util {
/***
* 普通解密操作
* @param encodedText:密文
* @return
*/
public static byte[] decode(String encodedText) {
final Base64.Decoder decoder = Base64.getDecoder();
return decoder.decode(encodedText);
}
/***
* 普通加密操作
* @param data
* @return
*/
public static String encode(byte[] data) {
final Base64.Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(data);
}
/***
* 解密操作
* @param encodedText
* @return
*/
public static byte[] decodeURL(String encodedText) {
final Base64.Decoder decoder = Base64.getUrlDecoder();
return decoder.decode(encodedText);
}
/***
* 加密操作
* @param data
* @return
*/
public static String encodeURL(byte[] data) {
final Base64.Encoder encoder = Base64.getUrlEncoder();
return encoder.encodeToString(data);
}
public static void main(String[] args) throws Exception {
String str = "今天的饭真好吃啊!明天还来!";
// 加密
String encode = encode(str.getBytes("UTF-8"));
// 解密
byte[] decode = decode(encode);
// 字节数组 -》字符串
String text = new String(decode, "UTF-8");
System.out.println("密文:" + encode);
System.out.println("明文:" + text);
// URL
// 加密
String encodeURL = encodeURL(str.getBytes("UTF-8"));
// 解密
byte[] decodeURL = decodeURL(encodeURL);
// 字节数组 -》字符串
String textURL = new String(decodeURL, "UTF-8");
System.out.println("密文:" + encodeURL);
System.out.println("明文:" + textURL);
}
}
对称加密AES
典型的对称加密算法有AES、DES、3DES,但AES加密算法的安全性要高于DES和3DES,所以AES 已经成为了主要的对称加密算法。
AES加密算法就是众多对称加密算法中的一种,它的英文全称是Advanced Encryption Standard, 翻译过来是高级加密标准,它是用来替代之前的DES加密算法的。 要理解AES的加密流程,会涉及到AES加密的五个关键词,分别是:分组密码体制、Padding、密 钥、初始向量IV和四种加密模式
代码示例
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
public class AESUtil {
/**
* AES加密/解密
*
* @param buffer:密文/明文
* @param secretKey:秘钥
* @param mode:加密/解密模式 1 加密 2 解密
* @return
*/
public static byte[] encryptAndDecrypt(byte[] buffer, String secretKey, Integer mode) throws Exception {
// 1、加载加密解密算法处理对象(包含算法、秘钥管理)
Security.addProvider(new BouncyCastleProvider());
// 2、根据不同算法创建秘钥 1)秘钥的字节数组 2)加密算法
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES");
// 3、设置加密模式(无论是加密还是解析,模式一致)
// 1)、AES/ECB/KPS7Padding 设置算法
// 2)、指定算法库对象
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
// 4、初始化加密配置
cipher.init(mode, secretKeySpec);
// 5、执行加密/解密
return cipher.doFinal(buffer);
}
/***
* 加密解密测试
* 密钥:128(16)、192(24)、256(32)
*/
public static void main(String[] args) throws Exception {
// 明文
String plainText = "SpringCloud Alibaba";
// 128bit 16位密钥
String secretKey = "1234567890123456";
// 将128bit 16位密钥 -》 256bit 32位密钥
secretKey = MD5Util.md5(secretKey);
System.out.println("明文:" + plainText);
System.out.println("密钥:" + secretKey);
// 加密
byte[] bytes = encryptAndDecrypt(plainText.getBytes("UTF-8"), secretKey, 1);
// 编码
String encodeStr = Base64Util.encode(bytes);
System.out.println("密文:" + encodeStr);
//解密 ->解码Base64->解密
byte[] decodeStr = encryptAndDecrypt(Base64Util.decode(encodeStr), secretKey, 2);
System.out.println("解密后数据:" + new String(decodeStr, "UTF-8"));
}
}
微信支付-Native
接口文档
https://pay.weixin.qq.com/wiki/doc/api/index.html
流程说明
- 商户后台系统根据用户选购的商品生成订单。
- 用户确认支付后调用微信支付【统一下单API】生成预支付交易;
- 微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。
- 商户后台系统根据返回的code_url生成二维码。
- 用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
- 微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
- 用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
- 微信支付系统根据用户授权完成支付交易。
- 微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
- 微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
- 未收到支付通知的情况,商户后台系统调用【查询订单API】。
- 商户确认订单已支付后给用户发货。
开发步骤
-
过修改官方wxpay-sdk源码,将抽象方法的修饰符从默认修改为public, 然后重新打包到本地或者私服
import com.github.wxpay.sdk.IWXPayDomain; import com.github.wxpay.sdk.WXPayConfig; import com.github.wxpay.sdk.WXPayConstants; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; @Component public class WeixinPayConfig extends WXPayConfig { //微信支付信息 @Value("${payconfig.weixin.appId}") private String appId; //应用ID @Value("${payconfig.weixin.mchID}") private String mchID; //商户号 @Value("${payconfig.weixin.key}") private String key; //秘钥 @Value("${payconfig.weixin.notifyUrl}") private String notifyUrl; //回调地址 @Value("${payconfig.weixin.certPath}") private String certPath; //证书路径 //证书字节数组 private byte[] certData; @Override public String getAppID() { return this.appId; } @Override public String getMchID() { return this.mchID; } @Override public String getKey() { return this.key; } /*** * 获取商户证书内容 * @return */ @Override public InputStream getCertStream() { /**** * 加载证书 */ if (certData == null) { synchronized (WeixinPayConfig.class) { try { if (certData == null) { File file = new File(certPath); InputStream certStream = new FileInputStream(file); this.certData = new byte[(int) file.length()]; certStream.read(this.certData); certStream.close(); } } catch (Exception e) { e.printStackTrace(); } } } ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData); return certBis; } /*** * 获取WXPayDomain, 用于多域名容灾自动切换 * @return */ @Override public IWXPayDomain getWXPayDomain() { // 这个方法需要这样实现, 否则无法正常初始化WXPay IWXPayDomain iwxPayDomain = new IWXPayDomain() { @Override public void report(String domain, long elapsedTimeMillis, Exception ex) { } @Override public DomainInfo getDomain(WXPayConfig config) { return new DomainInfo(WXPayConstants.DOMAIN_API, true); } }; return iwxPayDomain; } }
-
微信支付\证书和秘钥中的证书以及秘钥配置导入到 mall-pay-service 工程中,如果是php开发,采用pem证书,如果是java开发采用p12证书
-
创建 WXPay 的实例并交给Spring容器管理
import com.github.wxpay.sdk.WXPay; import com.gupaoedu.vip.mall.pay.config.WeixinPayConfig; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication @MapperScan(basePackages = {"com.tiger.mall.pay.mapper"}) public class MallPayApplication { public static void main(String[] args) { SpringApplication.run(MallPayApplication.class, args); } /**** * 微信支付SDK对象 * @param weixinPayConfig * @return * @throws Exception */ @Bean public WXPay wxPay(WeixinPayConfig weixinPayConfig) throws Exception { return new WXPay(weixinPayConfig); } }