这是《百图解码支付系统设计与实现》专栏系列文章中的第(5)篇。也是支付安全系列的第(2)篇。
本文主要讲清楚加解密技术在支付系统中的重要地位,核心应用场景,哪些是安全的算法,哪些是不安全的算法,以及对应的核心代码实现。
专栏地址:百图解码支付系统设计与实现
通过这篇文章,你可以了解到:
在数字经济的舞台上,在线支付系统扮演着至关重要的角色。究竟是什么技术让金钱能够在公开的互联网上安全无忧地穿梭?加密和解密是这舞台背后隐秘的艺术,同时确保资金的安全流转和个人信息安全。
在数字通信中,加密是将明文通过一定的算法和密钥转换成无法识别的密文的过程。这样即使数据被截获,未经授权的第三方也无法理解其内容。
解密则是加密的逆向过程,通过一定的算法和密钥将密文转换成明文的过程。
支付系统做为一个安全系数非常高的系统,加解密技术在里面起到了极其重要的作用。通常以下几个核心应用场景都会用到加解密技术:1)传输加密;2)存储加密。
具体的实现通常有两种:
1)通道加密:比如使用HTTPS,或者VPN、专线等,实现数据传输端到端的加密。HTTPS和VPN可以参考网络上公开的文档。
2)部分字段单独加密:比如把卡号等关键信息进行加密后再发出去。
3)整体报文单独加密:先生成业务报文,然后对整个报文加密再发出去。
具体的实现通常也会分两种:
1)直接加密:原始信息直接加密。通常用于信用卡、身体证等常规数据的加密。
2)加盐值(SALT)后再加密:原始信息先加上盐值,然后再进行加密。通常用于密码管理。
密码的存储比较特殊,值得单独说一说。
前面有说过,登录或支付密码需要加上盐值后,再进行加密存储。那为什么密码管理需要使用盐值?为了提高密码安全性。
在实现时,需要留意加盐策略:
推荐的算法如下:
AES:当前最广泛使用的对称加密算法,速度快,适用于高速加密大量数据。密钥长度推荐256或以上。
RSA:广泛使用的非对称加密算法,安全性比AES更高,但是加密速度慢,适用于小量数据或做为数字签名使用。密钥长度推荐2048或以上。
在https里面,数据加密使用AES,AES密钥通过RSA加密后传输,这样既解决了安全性,又解决了加密速度的问题。
当前公认不够安全的算法,不推荐使用,主要有:
DES:密钥长度较短,不够安全。
特别强调一点:千万千万不要自己去发明一种【私有的】,【自己认为很安全】的算法,并应用到生产环境。因为业界推荐的这些算法的安全性是经过大量数字家和计算机科学家论证过的,也经过工业界持续地验证,每天都有无数的攻击或破解在进行,一旦有被破解的风险就会很快知道。
明文数据被加密存储,安全了,那加密明文数据的密钥怎么办?
加密密钥有多重要呢?有一个公式是这样的:密钥的价值 = 密文的价值。比如你加密存储的密文价值10亿,那对应的密钥价值也有10亿。
密钥的管理涉及4个方面:密钥存储、更新、备份和恢复、废止和销毁。
密钥存储:
安全存储环境:密钥保存在特殊的安全环境中,包括服务器、网络环境、硬件加密机等。
最小权限原则:管理密钥的人越少越好。
密钥分为主密钥和工作密钥,其中工作密钥用来加解密普通的业务数据,而主密钥用来加解密工作密钥。
一般来说主密钥应该存储在专门的硬件安全模块(HSM)中,俗称:硬件加密机,安全性极高。但是相对来说性能有限,且价格昂贵,管理复杂。
工作密钥一般由主密钥加密后保存在DB中,在需要的时候调用主密钥解密后,缓存在内存中,然后再去加解密普通的业务数据。
密钥更新机制:
密钥备份和恢复,废止和销毁等机制,以及如何设计主密钥和工作密钥等细节,后面在介绍密钥中心设计与实现的章节再详细说明。
以经常使用的AES加解密为例:
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.spec.KeySpec;
import java.util.Base64;
import java.security.SecureRandom;
import javax.crypto.SecretKey;
public class AESWithPasswordExample {
private static final String PASSWORD = "123456";
private static final int ITERATION_COUNT = 65536;
private static final int KEY_LENGTH = 256; // AES密钥长度可以是128、192或256比特
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
// 使用PBKDF2从密码派生AES密钥
private static SecretKey getKeyFromPassword(String password) throws Exception {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt); // 创建安全随机盐
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] secretKey = factory.generateSecret(spec).getEncoded();
return new SecretKeySpec(secretKey, "AES");
}
// 加密
public static String encrypt(String data, SecretKey key, IvParameterSpec iv) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] encrypted = cipher.doFinal(data.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(encrypted);
}
// 解密
public static String decrypt(String encryptedData, SecretKey key, IvParameterSpec iv) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, iv);
byte[] original = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
return new String(original, "UTF-8");
}
public static void main(String[] args) throws Exception {
String originalData = "Confidential data that needs to be encrypted and decrypted";
// 生成密钥和初始化向量 (IV)
SecretKey key = getKeyFromPassword(PASSWORD);
byte[] ivBytes = new byte[16]; // AES使用16字节的IV
SecureRandom random = new SecureRandom();
random.nextBytes(ivBytes);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
// 加密数据
String encryptedData = encrypt(originalData, key, iv);
System.out.println("Encrypted data: " + encryptedData);
// 解密数据
String decryptedData = decrypt(encryptedData, key, iv);
System.out.println("Decrypted data: " + decryptedData);
}
}
请注意,这是一个简化的版本,实际应用中需要采取更多的安全措施,比如加密密钥的存储,盐值需要一起保存等。
曾经碰到的常见问题有:
密钥管理不规范:把密钥加密后保存在数据库,但是加密密钥用的密钥是123456。
算法选择不合适:大批量数据选择使用速度极慢的非对称的RSA算法。
兼容性算法不对:尤其是模式、填充方式是直接影响加解密结果的。比如AES下面仍然细分为:ECB,CBC,CFB,OFB,CTR,GCM等模式,以及PKCS7/PKCS5填充,零填充等填充方式。具体的可以找密码学相关资料参考。
异想天开地使用自己创造的私有算法:以为很安全,其实太傻太天真。
管理机制不完善:没有制定严格的规范,或有规范执行不严重,导致密钥能被轻易访问。
在数字支付世界里,加解密是支付系统安全的基石之一,和众多安全措施一起保护用户和平台的资产。安全的加解密算法,严谨的管理密钥,是支付系统安全的两大支柱。
加解密涉及的密码学是一个很大的领域,支付系统的安全则是一个更大的领域,因篇幅关系,这里只介绍了一些入门知识,不过对于支付系统日常研发已经足够。