开发中经常要用到加解密熟悉它很有好处。文章目录哈希算法(hash)(消息摘要算法)单向散列哈希算法哈希算法可逆吗?哈希算法的时间复杂度哈希算法是加密算法吗?md5DigestUtils(spring)实现md5DigestUtils验证密码(登录、修改密码)DigestUtils新增用户时加密对称加密算法base64BASE64jdk自带base64工具base64有什么好处base64如何不打印出来base64报错 Illegal base64 character 3aaes加密算法代码AES加密时报错 Cannot find any provider supporting AES/CBC/PKCS7Paddingaes加密时报错 Illegal key size or default parameters非对称加密算法又称公开密钥加密算法rsa每次加密生成的密码不一样是错了吗?数字信封签名校验签名校验时间戳的判断代码bcrypt(spring推荐加密算法)bcrypt引入依赖bcrypt SecurityConfig安全配置类bcrypt在业务代码中使用bcrypt 登录、修改密码时的写法bcrypt 新增用户时的写法哈希算法(hash)(消息摘要算法)单向散列哈希算法摘要算法的特点1、无论输入的长度是多少输出总是固定的长度。2、如果输入有变化摘要一定不同。基于这个原理可以对数据的完整性和一致性进行校验。由于是提取的不是全部内容所以无法还原为原始值。MD5、SHA128、256系列描述单向不可逆场景用户密码的加密哈希算法可逆吗?不可逆。哈希算法的时间复杂度是O(1)。# 也就是一次就可以计算出结果(在不考虑碰撞的情况下)哈希算法是加密算法吗?当然是。哈希算法又称消息摘要算法消息摘要算法本身就是加密算法如md5等。md5摘要算法DigestUtils(spring)实现md5//md5 单向加密Stringpassword芝麻开门;StringdigestDigestUtils.md5DigestAsHex(password.getBytes());// digest是摘要算法,这里只生成的字符串System.out.println(digest);DigestUtils验证密码(登录、修改密码)登录时StringloginPasswordDigestUtils.md5DigestAsHex(loginDTO.getPassword().getBytes());if(!StringUtils.equals(loginPassword,sysUser.getPassword())){thrownewIllegalArgumentException(旧密码输入错误);}修改密码时 newPasswordDigestUtils.md5DigestAsHex(newPassword.getBytes());if(!StringUtils.equals(newPassword,oldPassword)){thrownewIllegalArgumentException(旧密码输入错误);}DigestUtils新增用户时加密StringpasswordDigestUtils.md5DigestAsHex(rawPassword.getBytes());sysUser.setPassword(password);对称加密算法DES、3DES、AES、Blowfish、IDEA、RC5、RC6base64首先强调一点base64属于编码算法不属于加密算法概念一定要搞清楚。描述双向可逆加密场景邮件中的信息BASE64base64工具类有很多。jdk自带base64工具jdk自带的base64工具就不错。 类路径为java.util.Base64// jdk自带的BASE64 加解密工具Stringpassword芝麻开门;BASE64Encoderbase64EncodernewBASE64Encoder();Stringencodebase64Encoder.encode(password.getBytes());// 加密System.out.println(加密为字符串: encode);BASE64DecoderdecodernewBASE64Decoder();try{StringdecodenewString(decoder.decodeBuffer(encode));// 解密System.out.println(解密为: decode);}catch(IOExceptione){e.printStackTrace();}再来一个例子// 文件流到base64字符串FilerefereeFilenewFile(D://我的.jpg);byte[]refereeFileOriginalBytesFileUtils.readFileToByteArray(refereeFile);Stringbase64StringBase64.getEncoder().encodeToString(refereeFileOriginalBytes);logger.info(base64字符串{},base64String);//base64字符串到文件byte[]bytesBase64.getDecoder().decode(base64String);FilefilenewFile(D://base64转换为我的.jpg);FileUtils.writeByteArrayToFile(file,bytes);// 执行完之后去文件系统查看是否有这个文件字符串直接复制到java因为太大可能报错所以最好把字符串写在文件里面再读取如下例子// 文件流到base64字符串FilerefereeFilenewFile(D://base64String.txt);try{Stringbase64StringFileUtils.readFileToString(refereeFile);//base64字符串到文件byte[]bytesBase64.getDecoder().decode(base64String);FilefilenewFile(D://base64转换为我的.jpg);FileUtils.writeByteArrayToFile(file,bytes);// 执行完之后去文件系统查看是否有这个文件}catch(IOExceptione){e.printStackTrace();}base64有什么好处1是不直接显示文件流稍微安全些。另外如果不考虑性能数据之间的转移也比较方便就当做是个大点的字符串。对于需要反复读写的不是特别大的文件可以把base64放在内存中。省去了io和网络rest的时间。base64如何不打印出来方法间传递一般是一个对象通常会有多个参数base64String只是其中一项想要打印其他参数但是不打印base64String。其实不应该放在这里这属于json的序列化问题。以阿里的fastjson为例字段加忽略注解即可。JSONField(serializefalse)//fastjson 忽略字段打印privateStringbase64String;还有一种方案是toString的时候去掉。如果是继承的类调用下父类的toString()方法即可。还有一种说法是使用lombok时添加如下注解实测无效。ToString(callSupertrue)publicclassA{}base64报错 Illegal base64 character 3a一般有两种情况。1、base64确实不对。2、前端传的带文件类型头如data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD。对于这种可以替换下头。Stringbase64request.getFile().replaceAll( ,).replace(data:image/png;base64,,);aes加密算法五种加密算法ECBCBCCTROFBCFB不用纠结具体区别了就是起个匹配的作用和前后端匹配时一致即可。主要参数key偏移量(懒一点的话可以设置为和key一样)算法 例如AES/CBC/PKCS7Padding代码aes属于通用工具网上代码很多随便来一段吧。Slf4jComponentpublicclassAESUtils{// AES密钥算法privatestaticfinalStringKEY_ALGORITHMAES;// 加密/解密算法/工作模式/填充方式privatestaticfinalStringCIPHER_ALGORITHMAES/ECB/PKCS5Padding;Value(${aesKey:kkkkkkkkkkkkkkkk})privateStringtempAesKey;publicstaticStringAES_KEY;PostConstructpublicvoidinit(){log.info(AES工具类初始化开始,tempAesKey);AES_KEYtempAesKey;log.info(AES工具类初始化完成aesKey{},AES_KEY);}// 生成密钥publicstaticStringgenerateAESKey()throwsException{KeyGeneratorkeyGeneratorKeyGenerator.getInstance(KEY_ALGORITHM);keyGenerator.init(128,newSecureRandom());// 192 and 256 bits may not be availableSecretKeysecretKeykeyGenerator.generateKey();returnBase64.getEncoder().encodeToString(secretKey.getEncoded());}// 加密publicstaticStringencrypt(Stringdata,Stringkey)throwsException{CiphercipherCipher.getInstance(CIPHER_ALGORITHM);SecretKeySpecsecretKeySpecnewSecretKeySpec(Base64.getDecoder().decode(key),KEY_ALGORITHM);cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec);byte[]encryptedcipher.doFinal(data.getBytes());returnBase64.getEncoder().encodeToString(encrypted);}// 解密publicstaticStringdecrypt(Stringdata,Stringkey)throwsException{CiphercipherCipher.getInstance(CIPHER_ALGORITHM);SecretKeySpecsecretKeySpecnewSecretKeySpec(Base64.getDecoder().decode(key),KEY_ALGORITHM);cipher.init(Cipher.DECRYPT_MODE,secretKeySpec);byte[]originalcipher.doFinal(Base64.getDecoder().decode(data));returnnewString(original);}publicstaticvoidmain(String[]args)throwsException{// 这个方法只有第一次用生成后以后固定用一个值即可 自己编写的key不一定符合位数等要求所以第一次要自己生成// String key generateAESKey();Stringkeykkkkkkkkkkkkkkkk;StringoriginalTexthello world;StringencryptedTextencrypt(originalText,key);StringdecryptedTextdecrypt(encryptedText,key);log.info(key{},key);log.info(originalText{},originalText);log.info(encryptedText{},encryptedText);log.info(decryptedText{},decryptedText);}}注一直以为aes很简单眼高手低实际使用的时候还是有些要注意。例如用默认的方法每次key都是重新生成导致解密失败。实际生成key后应该固定用这个key就可以解密成功了。AES加密时报错 Cannot find any provider supporting AES/CBC/PKCS7Padding是因为jdk1.8不支持这个算法需要下载补丁包放到jdk jar包目录下。(不推荐因为成本太多从开发到服务器都要操作)aes加密时报错 Illegal key size or default parameters报错解释这个错误通常发生在使用Java加密时尤其是AES加密算法。在JDK 8 Update 161及以后的版本中引入了新的加密策略限制称为JEP 261: JDK Cryptography Extension Policy这意味着默认情况下Java加密库中的一些加密算法可能被限制使用。Illegal key size or default parameters错误表明你正在使用的加密密钥大小超出了Java加密库的默认限制。注这堆内容啰哩吧嗦知道是版本不对就行了。解决方法1、更新JDK到8u161或更高版本这样可以使用未受限制的加密策略。2、下载补丁包。jdk7的略。https://files-cdn.cnblogs.com/files/zgngg/jce_policy-8.zip # 这个是jdk8的官网太慢直接从这里下吧下载完成后解压缩下载的文件。你将会得到两个JAR文件local_policy.jar和US_export_policy.jar。下载包的readme.txt 有安装说明即替换${jdk_home}/jre/lib/security${jre_home}/lib/security目录下的local_policy.jar和US_export_policy.jar文件。自己下载的步骤步骤 1下载Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files打开Oracle官方网站搜索Java Cryptography Extension Unlimited Strength Jurisdiction Policy Files。进入下载页面找到适用于你的Java版本的文件并下载。非对称加密算法又称公开密钥加密算法如rsa加密。rsa每次加密生成的密码不一样是错了吗?当然不是非对称加密每次生成的密码本来就不不一样。数字信封数字信封是一种方案原理类似于https非对称密钥保护对称秘钥对称密钥加密数据。签名校验为什么要签名校验呢?1、只有已注册系统的请求才可以成功。2、只有一定时间内的请求才可以成功。3、只有采用相同加密规则的请求才可以成功。签名校验方式多种多样至少会一两种套路。sourceSystem # 来源系统 header中传salt # 盐值(密钥) 约定好私下单发timestamp # 时间戳 header中传signed # 签名 header中传校验规则sourceSystem必须在自己系统中注册timestamp误差必须在一分钟内避免一个签名可以永久使用signed约定的加密算法(salttimestamp*N) # 这里规则自己定义可以多来几次外人根本不知道啥规则这样可以说是相当安全了。签名校验时间戳的判断代码挺简单的也是网上通用的代码了。这里误差时间设置的是1分钟。longmyTimestampSystem.currentTimeMillis();longtimestampDiffAbsMath.abs(myTimestamp-Long.parseLong(timestamp));// 时间误差绝对值booleantimestampFlagtimestampDiffAbssignVerifyTimestampOffset*1000*60;log.info(methodName操作_时间误差部分完成,timestamp{},myTimestamp{},timestampDiffAbs{},误差范围(秒){},timestampFlag{},timestamp,myTimestamp,timestampDiffAbs,signVerifyTimestampOffset,timestampFlag);CustomAssertUtils.isSuccess(timestampFlag,timestamp误差必须在范围内);bcrypt(spring推荐加密算法)md5已经不再安全推荐bcrypt算法。生成后的密码如下示例$2a$10$X2KuhxgG2eif/GVWx5qGxeDpyodHrlsZxfd.EP3Oox3T4bTmrxK46# 这谁能破解的了。。。bcrypt引入依赖组合依赖dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependency也可以引入单个依赖dependencygroupIdorg.springframework.security/groupIdartifactIdspring-security-crypto/artifactId/dependencybcrypt SecurityConfig安全配置类importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;ConfigurationpublicclassSecurityConfig{BeanpublicPasswordEncoderpasswordEncoder(){// 默认强度为10建议生产环境设为 12~14 以平衡安全性与性能returnnewBCryptPasswordEncoder(12);}}bcrypt在业务代码中使用importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;ServicepublicclassUserService{AutowiredprivatePasswordEncoderpasswordEncoder;publicStringencryptPassword(StringrawPassword){// 对原始密码进行加密BCrypt 会自动生成随机盐值StringencodedPasswordpasswordEncoder.encode(rawPassword);log.info(加密后的密码{},encodedPassword);returnencodedPassword;}// 密码验证例如用户登录publicbooleanvalidatePassword(StringrawPassword,StringencodedPassword){// 将用户输入的明文密码与数据库中存储的加密密码进行比对booleanmatchespasswordEncoder.matches(rawPassword,encodedPassword);log.info(密码验证结果{},matches);returnmatches;}}注1、永远使用matches()方法BCrypt每次加密都会生成不同的盐值导致同样的明文密码每次加密后的字符串都不一样所以验证时必须使用matches(明文,数据库密文)。2、强度因子CostFactor默认值是10。生产环境建议设为12到14。数值每增加1计算耗时就会翻倍。建议根据你服务器的性能将单次加密耗时控制在50~100毫秒左右。bcrypt 登录、修改密码时的写法booleanmatchFlagthis.validatePassword(loginDTO.getPassword(),sysUser.getPassword());if(matchFlag){thrownewIllegalArgumentException(旧密码输入错误);}bcrypt 新增用户时的写法StringpasswordpasswordEncoder.encode(123456);sysUser.setPassword(password);