Java加密算法实现详解:AES/RSA/哈希算法代码示例与安全最佳实践

Java加密算法实现详解:AES/RSA/哈希算法代码示例与安全最佳实践 一

文章目录CloseOpen

你有没有遇到过这样的情况?项目里需要存用户手机号、身份证号这些敏感信息,直接明文存数据库肯定不行,老板看到了要拍桌子的。这时候加密就派上用场了,但选哪种加密算法、密钥怎么存,好多人第一次接触都会头大。我去年帮一个朋友的电商项目做数据安全改造,他们一开始把用户收货地址明文存在数据库,结果被安全审计揪出来,差点影响上线。后来用AES加密改造后,不仅通过了审计,性能也没受影响——这就是对称加密的好处:速度快、适合大量数据加密。

AES算法入门:从原理到代码实现

AES(Advanced Encryption Standard)是目前最常用的对称加密算法,说白了就是“一把钥匙开一把锁”,加密和解密用同一个密钥。你可以把它想象成家里的房门钥匙,锁门(加密)和开门(解密)用的是同一把。AES能处理128位、192位或256位的密钥,密钥越长越安全,但256位在部分国家需要特殊许可,所以国内项目常用128位密钥,足够安全了。

实现AES加密其实就四步,我用Java代码给你一步步拆解开,你跟着敲一遍就能上手:

第一步是生成密钥。Java自带的KeyGenerator类可以帮我们生成AES密钥,注意指定算法为“AES”,密钥长度128位。代码长这样:

KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");

keyGenerator.init(128); // 密钥长度128位

SecretKey secretKey = keyGenerator.generateKey();

byte[] keyBytes = secretKey.getEncoded(); // 密钥字节数组,后续存储用

这里有个坑:千万别把密钥直接写死在代码里!我见过有人把keyBytes转成字符串硬编码在Constants类里,结果代码一提交到Git,密钥等于公开了。正确的做法是把密钥存在环境变量或配置中心,比如用Spring Cloud Config,部署时通过服务器环境变量注入,这样即使代码泄露,密钥也安全。

第二步是选择加密模式和填充方式。AES有ECB、CBC、GCM等模式,ECB模式最简单但不安全(相同明文加密后结果一样,容易被破解),推荐用GCM模式——它自带数据校验功能,加密的同时能检测数据是否被篡改。填充方式选PKCS5Padding,这是Java默认支持的,兼容性好。

第三步是初始化加密器并处理数据。用Cipher类初始化加密模式,传入密钥和GCM模式需要的参数(比如12位的IV向量,相当于加密时的“随机数”,每次加密都要不一样,否则会被攻击)。代码示例:

// 生成12位IV(GCM模式推荐用12字节IV)

byte[] iv = new byte[12];

SecureRandom random = new SecureRandom();

random.nextBytes(iv);

// 初始化加密器

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); // GCM模式不需要额外填充

GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, iv); // 128位认证标签长度

cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmParameterSpec);

// 加密数据(假设data是要加密的字节数组)

byte[] encryptedData = cipher.doFinal(data);

这里要注意:IV向量虽然不需要保密,但解密时必须和加密时一致,所以通常会把IV和加密后的数据一起存储或传输,比如在加密结果的前12字节存IV,后面是密文。

密钥管理:从生成到轮换的实战技巧

密钥不是一成不变的,用久了可能会泄露,所以需要定期轮换。但轮换密钥时,老数据怎么办?总不能让用户重新提交吧?我之前处理过一个医疗项目,需要每季度轮换AES密钥,当时的方案是“双密钥共存”:新密钥用于加密新数据,老密钥只用于解密历史数据,等历史数据过期后再删除老密钥。

不同AES模式的安全性和性能也不一样,我整理了一张对比表,你可以根据项目需求选:

加密模式 安全性 性能 是否需要IV 适用场景
ECB 低(不推荐) 不需要 仅测试环境
CBC 需要(16字节) 文件加密
GCM 高(推荐) 中高 需要(12字节) 网络传输、数据库存储

Oracle官方文档里也提到,GCM模式是目前最推荐的AES应用模式,它同时提供机密性和完整性保障,适合大多数生产环境(Oracle Java Cryptography Architecture,nofollow)。

非对称加密与哈希:RSA签名验证及数据完整性保障

如果说AES是“门锁钥匙”,那RSA就是“快递柜”——你可以把公钥(快递柜地址)给任何人,他们用公钥加密数据(放进柜子),但只有你手里的私钥(柜门钥匙)能解密。这种“公钥加密,私钥解密”的特性特别适合密钥交换和数字签名。我之前做支付接口对接时,第三方平台就是用RSA签名来验证请求是否来自我们系统,避免别人伪造请求。

RSA签名与验签:接口安全的“防伪标签”

RSA最常用的场景不是加密大文件(速度慢),而是数字签名。比如你调用微信支付API,需要用商户私钥对请求参数签名,微信服务器用你的公钥验签,确认参数没被篡改且确实是你发的。签名的原理其实是“私钥加密哈希值”:先对数据做哈希(比如SHA-256),得到固定长度的哈希值,再用私钥加密这个哈希值,就是签名结果;验签时用公钥解密得到哈希值,和对原始数据做哈希的结果对比,如果一致,说明数据没被改。

实现RSA签名分三步:生成密钥对、用私钥签名、用公钥验签。先看密钥对生成,Java的KeyPairGenerator可以搞定:

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");

keyPairGenerator.initialize(2048); // 密钥长度至少2048位,1024位已不安全

KeyPair keyPair = keyPairGenerator.generateKeyPair();

PrivateKey privateKey = keyPair.getPrivate(); // 私钥自己保管,别泄露

PublicKey publicKey = keyPair.getPublic(); // 公钥可以公开给对接方

公钥通常会转成字符串格式(比如Base64编码的X.509格式),方便对方保存。我见过有人把公钥存成.cer证书文件,对接时发给第三方,这是规范的做法。

签名代码示例(用SHA-256withRSA算法,即先SHA-256哈希再RSA签名):

// 要签名的数据(比如API请求参数拼接的字符串)

String data = "appid=wx123&amount=100&timestamp=1620000000";

byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);

// 初始化签名对象

Signature signature = Signature.getInstance("SHA256withRSA");

signature.initSign(privateKey);

signature.update(dataBytes); // 传入原始数据

byte[] signBytes = signature.sign(); // 签名结果,通常转Base64给对方

验签时用公钥,代码类似:

Signature verifier = Signature.getInstance("SHA256withRSA");

verifier.initVerify(publicKey);

verifier.update(dataBytes); // 原始数据

boolean verifyResult = verifier.verify(signBytes); // true表示验签通过

这里有个细节:签名前一定要对参数排序!比如请求参数有amount=100appid=wx123,你按字母顺序排appid,amount拼接,对方验签时也必须按同样顺序,否则哈希值不一样,验签失败。我第一次对接支付接口时就因为没排序,卡了一下午,后来看文档才发现要按ASCII码排序——这种“坑”只有踩过才记得牢。

哈希算法:数据完整性的“指纹”

哈希算法(比如SHA-256)能把任意长度的数据“压缩”成固定长度的哈希值,就像给数据生成“指纹”——只要原始数据改一个字节,哈希值就会完全不同。它常用来做数据完整性校验,或者存储密码(存哈希值而不是明文)。

常见的哈希算法有MD5、SHA-1、SHA-256、SHA-512。但MD5和SHA-1已经不安全了(能找到不同数据但哈希值相同的“碰撞”),千万别用来存密码!OWASP(开放式Web应用安全项目)明确 用SHA-256及以上,或者专门的密码哈希算法如BCrypt(OWASP Password Storage Cheat Sheet,nofollow)。

我之前帮一个项目做密码安全改造,他们原来用MD5存密码,被安全扫描出高危漏洞。后来改用BCrypt,它自带加盐(salt)和自适应哈希(迭代次数可调,计算越慢越难暴力破解),Java的BCryptPasswordEncoder可以直接用:

String rawPassword = "user123";

BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

String encodedPassword = encoder.encode(rawPassword); // 存到数据库的哈希值

boolean matches = encoder.matches(rawPassword, encodedPassword); // 登录时验证

BCrypt的哈希结果已经包含了盐值,所以不用单独存盐,很方便。

不同哈希算法的对比表如下,选的时候优先SHA-256或BCrypt:

算法 输出长度 安全性 适用场景
MD5 128位 低(不推荐) 仅用于非安全场景(如文件校验和)
SHA-256 256位 数据完整性校验、RSA签名
BCrypt 60字符 极高(推荐) 密码存储

实际项目中,哈希和RSA经常结合使用。比如用户注册时,密码先用BCrypt哈希存数据库;登录时,前端用HTTPS传输密码,后端用BCrypt校验;API接口用RSA签名+SHA-256哈希,确保请求安全。

你在项目里用加密算法时,记得先想清楚场景:加密大量数据用AES,签名或密钥交换用RSA,存密码用BCrypt,数据校验用SHA-256。如果拿不准,就参考OWASP或Oracle的官方指南,别自己发明“加密算法”——安全这东西,越简单越标准,反而越可靠。你最近做的项目有用到加密吗?遇到过什么坑,欢迎在评论区聊聊!


选加密算法这事儿,其实就跟挑工具似的,得看你手里的活儿是啥。你要是项目里有一堆数据要加密,比如数据库里存着成百上千条用户手机号、身份证号,或者需要加密传输大文件,那肯定得选AES这种对称加密算法。我之前帮朋友的电商项目调过,他们当时要加密用户收货地址,数据量不小,用AES加密后,查库、展示的时候解密,响应速度几乎没受影响——对称加密就是这点好,密钥就一把,加解密速度快,处理大量数据完全扛得住。不过记住啊,AES的密钥得保管好,就像家里的钥匙,丢了可就麻烦了,所以密钥千万别直接写代码里,放配置中心或者环境变量才靠谱。

要是你碰到的是小数据加密,或者需要“安全交换密钥”这种场景,那就得换RSA这种非对称加密了。比如对接第三方支付接口,对方给你个公钥,你用公钥加密请求参数的签名,对方用私钥解密验签,这就不用担心密钥在传输过程中泄露。但RSA有个缺点,加解密速度比AES慢不少,要是拿它加密几百兆的文件,用户怕是得等到花儿都谢了。所以实际开发里,经常是AES和RSA搭配着用:AES加密大文件,RSA加密AES的密钥,这样既快又安全,算是个两全其美的办法。

除了加解密,你肯定还遇见过要验证“数据有没有被改过”的情况吧?比如用户传个文件给你,你得确认这文件路上没被人动过手脚,这时候哈希算法就派上用场了。像SHA-256这种,不管文件多大,都能生成一串固定长度的哈希值,就像给数据盖了个“指纹章”,接收方拿到文件后也生成一遍哈希值,两边一对,一样就说明数据没被动过。不过哈希算法是单向的,只能生成指纹,不能从指纹反推数据,所以千万别拿它当加密用——加密是要能解密还原的,这俩可不是一回事儿。

还有个特殊场景得单独说,就是存用户密码。你可别直接用SHA-256哈希存密码,我之前见过有人这么干,结果安全扫描直接标高危。为啥?因为相同的密码哈希出来的结果一样,黑客弄个彩虹表(就是存了一堆常见密码和对应哈希值的表)一比对,密码就暴露了。这种时候就得用BCrypt这种专门的密码哈希算法,它会自动给每个密码加个“盐”(随机字符串),就算俩用户密码一样,哈希结果也不一样;而且它还能调迭代次数,让哈希计算慢点,黑客想暴力破解都费劲,这才是存密码的正确姿势。


如何选择适合项目的Java加密算法

选择加密算法需根据具体场景:对称加密(AES)适合大量数据加密(如数据库敏感字段、文件加密),因速度快;非对称加密(RSA)适合密钥交换、数字签名(如API接口验签、支付请求加密),因安全性高但速度较慢;哈希算法(SHA-256)适合数据完整性校验(如文件校验和),密码存储推荐专用算法(如BCrypt),自带加盐和自适应哈希功能。

AES密钥可以直接写在代码里吗?

不可以。直接将AES密钥硬编码在代码中存在严重安全风险,一旦代码泄露,密钥会被直接获取。正确做法是将密钥存储在环境变量、配置中心(如Spring Cloud Config)或加密的配置文件中,部署时通过服务器环境变量注入,确保密钥与代码分离,即使代码泄露,密钥仍能保持安全。

RSA和AES可以结合使用吗?

可以。实际开发中常结合使用:用AES加密大量数据(速度快),再用RSA加密AES的密钥(安全性高),最后将“RSA加密的AES密钥+AES加密的数据”一起传输。接收方先用RSA私钥解密得到AES密钥,再用AES密钥解密数据。这种方式兼顾了AES的高效和RSA的安全,适合网络传输场景(如文件上传下载、敏感信息传输)。

哈希算法(如SHA-256)能用于加密数据吗?

不能。哈希算法(如SHA-256)是单向不可逆的,只能将任意数据生成固定长度哈希值(“数据指纹”),无法从哈希值反推原始数据, 不适合加密(加密需可逆)。哈希算法主要用于数据完整性校验(如校验文件是否被篡改)、密码存储(存储哈希值而非明文),而加密需使用AES、RSA等可逆算法。

BCrypt和SHA-256在密码存储中有什么区别?

BCrypt是专门的密码哈希算法,自带随机加盐(salt)和自适应哈希(可调整迭代次数,计算速度可控,防暴力破解),安全性高于普通哈希算法;SHA-256是通用哈希算法,无自带加盐机制,若直接用于密码存储,相同密码会生成相同哈希值,易被彩虹表攻击。 密码存储优先选择BCrypt,而非单纯使用SHA-256。

0
显示验证码
没有账号?注册  忘记密码?