用户密码安全存储
关键字:AES加密,CBC,Java
浏览器或者各种客户端实现注册功能时,对用户密码的存储,该如何保证密码的安全?
关于方案
- 客户端使用AES(本文使用AES加密)算法对密码进行加密,然后将加密后的密码通过BASE64编码发送给服务器。
- 服务端收到后,BASE64解码,然后使用与客户端相同的密钥和初始向量进行ASE解密。解密后我们就得到原始密码了,然后我们可以对原始密码在进行各种操作,我说说之前我们公司的操作。他将原始密码进行md5编码,将编码后的字符串每16字节进行一次AES加密,最终将所有加密结果拼接合并,存放到数据库中再做一次SHA256.(我并不懂为什么要做这种操作,为什么不直接使用加密后的密文,而是要将编码后的每16字节进行一次加密后做拼接)
对于密钥存储
我不清楚客户端如何获取密钥,直接写在客户端代码中? 如果这样那就需要代码混淆,或者代码加密。
还是用接口访问服务器获得密钥?那么服务器也要验证接口调用者身份,况且对于每个app客户端如何判断是否有权限获得密钥。如果可以抓包,那么也可以获得请求中密钥,所以要对密钥再次进行加密,进行套娃?
好了,我们说说服务器端吧。我还是先说说之前公司的操作。他将密钥存储到配置文件,在生产环境有一个统一的配置中心,密钥就存储在那里。如果配置中心的配置泄露,那么密钥就会泄露。
如果密钥泄露,那么攻击者是否可以拦截客户端请求体,而解密出密码呢?
那么可以使用密钥管理系统来提高密钥的安全性。如KMS云服务,各个云服务厂商搜索KMS即可。或者自己部署一个开源软件密钥管理系统,如HashiCorp、Keycloak 等项目。
关于Java加密解密代码
@Test
void encrypt() {
String value="baeldung";
final String key = "aesEncryptionKey";
final String initVector = "encryptionIntVec";
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
String s = Base64.encodeBase64String(encrypted);
System.out.println(s);
} catch (Exception ex) {
ex.printStackTrace();
}
}
@Test
void decrypt() {
String encrypted = "f3abc1/iSw92hEfjeZeZxA==";
final String key = "aesEncryptionKey";
final String initVector = "encryptionIntVec";
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
System.out.println(new String(original));
} catch (Exception ex) {
ex.printStackTrace();
}
}
关于AES
AES padding,AES是块加密算法,因此加密的数据可能并不是16字节整数倍,因此填充数据使之成为16整数倍。
CBC需要初始向量,ECB不需要初始向量。