公司内部的P2P平台由于监管,进行了一系列不合规项的整改。其中有一条就是要对数据库中一些比较敏感的信息进行加密,比如:手机,邮箱,身份证,银行卡之类的。解决方案是,在整个过程,查询的时候在java服务端进行解密,插入和更新的时候在java服务端进行加密。数据库端则存放加密后的信息。本来只需要考虑java服务端加解密后能保持一致即可,但是为了方便某些在数据库端进行运维的人能使用sql对已加密的信息进行处理。这里要求了java服务端加密的内容能够在数据库端进行解密。所以两边需要使用一样的算法。
首先,第一步就是在postgresql数据库端安装pgcrypto模块
postgres=# \c superp2b_test
You are now connected to database "superp2b_test" as user "enterprisedb".
superp2b_test=# create extension pgcrypto;
CREATE EXTENSION
superp2b_test=#
安装完后就可以使用postgresql数据库中的decrypt加密函数和encrypt解密函数了。
在网上找了java的AES加解密的工具类,进行加密,然后数据库端进行解密,报错。发现两边的加解密不一致。网上查了AES算法,才发现AES算法有多种加密模式,填充模式,如果两边的模式不一致,则加密出来的内容也会不一致。且加密其实是对byte字节的加密,我对加密前字符串转字节的过程和加密后字节转字符串的过程是否一致也产生了怀疑。
由于java服务端的加解密对我们来说相对透明可见,而数据库端的加解密的过程则完全不清楚,只暴露了一个函数名和几个函数参数。这里查询了postgresql官网。由于当前数据库使用的版本是9.3。故查询了
https://www.postgresql.org/docs/9.3/static/pgcrypto.html
可知默认用cbc模式,数据可为任何长度。
把数据库的模式改成aes-ecb后再次尝试分别在java和数据库端进行加解密,两边结果依然不一致。很是困惑。经过很久的排查,最后才发现网上搜到的java端aes算法大部分都额外会对密钥进行处理,从而导致了两边加解密不一致。
这里贴一下java端的AES加解密工具类代码:
package com.xxx.xxx.framework.ext.sqlfilter; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; public class AESUtil { private static Logger logger = LoggerFactory.getLogger(AESUtil.class); private static String MODEL = "AES/ECB/PKCS5Padding"; private static String useKey ="0000000000888888"; public static String encrypt(String content) { if(StringUtils.isEmpty(content)){ return content; } String result = content; try { byte[] contentBytes = content.getBytes("UTF-8"); SecretKeySpec skeySpec = new SecretKeySpec(useKey.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance(MODEL); cipher.init(Cipher.ENCRYPT_MODE, skeySpec); byte[] encryptResult = cipher.doFinal(contentBytes); result = Base64.encodeBase64String(encryptResult); //替换\r \n result = result.replace("\n", "").replace("\r", ""); } catch (Exception ex) { logger.error("进行自动加密时出错,加密内容为"+content+",异常信息"+ex.getMessage(), ex); throw new RuntimeException(ex); } return result; } public static String decrypt(String content){ if(StringUtils.isEmpty(content)){ return content; } String result = content; byte[] contentBytes =null; try{ if(content.length()%4==0){ contentBytes =Base64.decodeBase64(content); }else{ logger.error("进行自动解密时出错,字符串{}不是base64编码过的字符串!",content); throw new RuntimeException("字符串"+content+"不是base64编码过的字符串!"); } } catch (Exception ex) { logger.error("进行自动解密时出错,字符串"+content+"不是base64编码过的字符串,进行base64解码出错!出错信息:"+ex.getMessage(),ex); throw new RuntimeException(ex); } if(contentBytes!=null){ try{ SecretKeySpec skeySpec = new SecretKeySpec(useKey.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance(MODEL); cipher.init(Cipher.DECRYPT_MODE, skeySpec); byte[] decryptResult = cipher.doFinal(contentBytes); if (decryptResult != null) { result = new String(decryptResult, "UTF-8"); } } catch (Exception ex) { logger.error("进行自动解密时出错,加密内容为"+content+",异常信息"+ex.getMessage(), ex); throw new RuntimeException(ex); } } return result; } /* public static void main(String args[]) { String content ="test123456"; System.out.println(encrypt(content)); System.out.println(decrypt(encrypt(content))); System.out.println(decrypt("954545test")); }*/ }
而数据库端,加密则select encode(encrypt('test123456'::bytea,'0000000000888888','aes-ecb'),'base64');解密则为select convert_from(decrypt(decode('cbbuXr3h9EWL0QOSDreFsw==','base64'),'0000000000888888','aes-ecb'),'SQL_ASCII');
可在数据库端建立函数mydec(varchar,varchar):
BEGIN IF $1 IS NULL OR $1='' THEN RETURN $1; ELSE IF $2 IS NULL OR $2='' THEN RETURN $1; ELSE RETURN encode(encrypt($1::bytea,$2::bytea,'aes-ecb'),'base64'); END IF; END IF; EXCEPTION WHEN OTHERS THEN RAISE EXCEPTION '(%)',$1; return -1; END
进行加密则可简化为select myenc('test123456','0000000000888888');
解密同理。以上的0000000000888888就是密钥。
再次过程,需要保证数据库和java两端的加解密模式都是一样的。上面的例子里,数据库和java两端的加密
模式都是ECB。当java端使用ECB,而数据库模式是使用CBC模式,则在加密16个字节以上的内容时,会出现乱码。两边加解密不一致。这个应该跟AES内部把数据按照16个字节进行分块有关吧。没有去仔细研究。
这里是简单的描述下自己在java后端和数据库端两边进行加解密的过程中遇到的坑,希望对其他人有所帮助。
相关推荐
Java编程语言以其跨平台性和广泛的应用领域而闻名,而在处理数据存储方面,它通常与关系型数据库管理系统(RDBMS)如MySQL、Oracle或PostgreSQL等进行集成。然而,对于那些需要与Microsoft Access数据库交互的Java...
ETL 数据基本清洗包括以下分类: ...7.加解密(md5、sha、base64、aes、rsa); 8.文件; 9.http服务; 10.正则表达式; 11.个人信息:身份证号、手机号、姓名清洗和扩展; 后期会不断更新,望大家指正。
1. 数据库管理:使用关系型数据库(如MySQL、PostgreSQL)或NoSQL数据库(如MongoDB)来存储卡号和卡密,设计合理的表结构和索引来优化查询性能。 2. 加密算法:如MD5、SHA系列、AES等,用于对卡密进行加密和解密...
数据库管理也是此项目的关键部分,可能使用MySQL、PostgreSQL或者NoSQL数据库如MongoDB来存储用户信息、聊天记录等。数据库设计需要考虑数据的一致性、安全性和可扩展性。 最后,版本控制工具如Git可能用于协同开发...
- **流管理**:密钥的使用和更新可通过NiFi的数据流进行控制,实现动态加密和解密操作。 3. **Java技术栈**: nifi-encrypt模块基于Java开发,充分利用了Java的跨平台性和丰富的库资源。Java的加密库如Java ...
确保服务器端的编程语言(如PHP、Python、Java等)和数据库(如MySQL、PostgreSQL)配置正确,能够识别并存储Unicode字符。例如,对于MySQL,确保数据库字符集设置为`utf8mb4`,因为`utf8`不完全支持所有Unicode字符...
5. 数据库管理:为了存储用户信息,passmakr 可能使用了MySQL、PostgreSQL或MongoDB等数据库管理系统。 6. 版本控制:源代码很可能在Git等版本控制系统下管理,因此可能包含.git文件夹,揭示项目的版本历史和开发...
6. **数据库管理**:为了存储用户信息和认证相关数据,项目可能会使用关系型数据库(如MySQL或PostgreSQL)或NoSQL数据库(如MongoDB)。数据库设计需要考虑数据的完整性、安全性以及高效查询。 7. **客户端应用**...
项目可能采用了关系型数据库(如MySQL、Oracle或PostgreSQL)来存储用户信息和课程数据。数据库操作可能通过JDBC或ORM框架(如Hibernate)进行,以实现高效的数据存取和事务处理。 8. **安全性**: 除了加密之外...
数据库管理是数据管理系统的核心组件,dms-lk可能使用了关系型数据库(如MySQL或PostgreSQL)或者NoSQL数据库(如MongoDB)来存储用户信息、权限设置和密钥数据。数据库的设计应当遵循ACID(原子性、一致性、隔离性...
项目可能使用Java的ExecutorService来创建和管理线程池,提高多线程处理效率,同时避免频繁创建和销毁线程带来的开销。 7. **数据加密解密(AES)**: 使用AES(高级加密标准)进行数据加密可以保护敏感信息的安全...
1. 数据加密:SecureIM采用了高级加密标准AES(Advanced Encryption Standard)对传输的数据进行加密,保证信息在传输过程中的隐私不被窃取。 2. 数字签名与消息认证:利用RSA等非对称加密算法进行数字签名,确保...