加密与安全

目录

一、URL编码:

二、Base64编码:

三、哈希算法:

四、Hmac算法:

五、对称加密算法:


一、URL编码:

URL编码是浏览器发送数据给服务器时使用的编码,它通常附加在URL的参数部分。之所以需要URL编码,是因为出于兼容性考虑,很多服务器只识别ASCII字符。但如果URL中包含中文日文这些非ASCII字符怎么办?不要紧,URL编码有一套规则:

  • 如果字符是A~Za~z0~9以及-_.*,则保持不变;
  • 如果是其他字符,先转换为UTF-8编码,然后对每个字节以%XX表示。

例如:字符"中"UTF-8编码是0xe4b8ad,因此,它的URL编码是%E4%B8%ADURL编码总是大写。

Java标准库提供了一个URLEncoder类来对任意字符串进行URL编码:

import java.net.URLEncoder;
public class Main {public static void main(String[] args) {String encoded = URLEncoder.encode("中文!", "utf-8");System.out.println(encoded);}
}

URL编码是编码算法,不是加密算法。URL编码的目的是把任意文本数据编码为%前缀表示的文本,编码后的文本仅包含A~Za~z0~9-_.*%,便于浏览器和服务器处理。

二、Base64编码:

Base64编码是对二进制数据进行编码,表示成文本格式。Base64编码可以把任意长度的二进制数据变为纯文本,并且纯文本内容中且只包含指定字符内容:A~Za~z0~9+/=。它的原理是把3字节的二进制数据按6bit一组,用4个整数表示,然后查表,把整数用索引对应到字符,得到编码后的字符串。6位整数的范围总是0~63,所以,能用64个字符表示:字符A~Z对应索引0~25,字符a~z对应索引26~51,字符0~9对应索引52~61,最后两个索引6263分别用字符+/表示。

base64码表:

码值字符码值字符码值字符码值字符
0A16Q32g48w
1B17R33h49x
2C18S34i50y
3D19T35j51z
4E20U36k520
5F21V37l531
6G22W38m542
7H23X39n553
8I24Y40o564
9J25Z41p575
10K26a42q586
11L27b43r597
12M28c44s608
13N29d45t619
14O30e46u62+
15P31f47v63/

Java中,二进制数据就是byte[]数组。Java标准库提供了Base64来对byte[]数组进行编解码:

public class Main {public static void main(String[] args) {byte[] input = new byte[] { (byte) 0xe4, (byte) 0xb8, (byte) 0xad };String b64encoded = Base64.getEncoder().encodeToString(input);System.out.println(b64encoded);}
}

编码后得到字符串结果:5Lit。要对这个字符使用Base64解码,仍然用Base64这个类:

public class Main {public static void main(String[] args) {byte[] output = Base64.getDecoder().decode("5Lit");System.out.println(Arrays.toString(output)); // [-28, -72, -83]}
}

三、哈希算法:

哈希算法(Hash)又称摘要算法(Digest),它的作用是:对任意一组输入数据进行计算,得到一个固定长度的输出摘要。哈希算法最重要的特点就是:

  • 相同的输入一定得到相同的输出;
  • 不同的输入大概率得到不同的输出。

所以,哈希算法的目的:为了验证原始数据是否被篡改。Java字符串的hashCode()就是一个哈希算法,它的输入是任意字符串,输出是固定的4字节int整数:

"hello".hashCode(); // 0x5e918d2
"hello, java".hashCode(); // 0x7a9d88e8
"hello, bob".hashCode(); // 0xa0dbae2f

 1、哈希碰撞:两个不同的输入得到了相同的输出。(不能避免,安全性低)

"AaAaAa".hashCode(); // 0x7460e8c0
"BBAaBB".hashCode(); // 0x7460e8c0

"通话".hashCode(); // 0x11ff03
"重地".hashCode(); // 0x11ff03

2、常用的哈希算法:

算法

输出长度(位)

输出长度(字节)

MD5

128 bits

16 bytes

SHA-1

160 bits

20 bytes

RipeMD-160

160 bits

20 bytes

SHA-256

256 bits

32 bytes

SHA-512

512 bits

64 bytes

1、MD5算法:

使用MessageDigest时,我们首先根据哈希算法获取一个MessageDigest实例,然后,反复调用update(byte[])输入数据。当输入结束后,调用digest()方法获得byte[]数组表示的摘要,最后,把它转换为十六进制的字符串。

import java.security.MessageDigest;public class main {public static void main(String[] args)  {// 创建一个MessageDigest实例:MessageDigest md = MessageDigest.getInstance("MD5");// 反复调用update输入数据:md.update("Hello".getBytes("UTF-8"));md.update("World".getBytes("UTF-8"));// 16 bytes: 68e109f0f40ca72a15e05cc22786f8e6byte[] results = md.digest(); StringBuilder sb = new StringBuilder();for(byte bite : results) {sb.append(String.format("%02x", bite));}System.out.println(sb.toString());}
}

2、哈希算法的用途:

(1)校验下载文件

(2)存储用户密码:(要避免彩虹表攻击,对每个口令额外添加随机数,这个方法称之为加盐saltdigest = md5(salt + inputPassword)

3、SHA-1算法:

SHA-1也是一种哈希算法,它的输出是160 bits,即20字节。SHA-1是由美国国家安全局开发的,SHA算法实际上是一个系列,包括SHA-0(已废弃)、SHA-1SHA-256SHA-512等。在Java中使用SHA-1,和MD5完全一样,只需要把算法名称改为"SHA-1":

import java.security.MessageDigest;public class main {public static void main(String[] args)  {// 创建一个MessageDigest实例:MessageDigest md = MessageDigest.getInstance("SHA-1");// 反复调用update输入数据:md.update("Hello".getBytes("UTF-8"));md.update("World".getBytes("UTF-8"));// 20 bytes: db8ac1c259eb89d4a131b253bacfca5f319d54f2byte[] results = md.digest(); StringBuilder sb = new StringBuilder();for(byte bite : results) {sb.append(String.format("%02x", bite));}System.out.println(sb.toString());}
}

4、RipeMD160:

Java标准库的java.security包提供了一种标准机制,允许第三方提供商无缝接入。我们要使用BouncyCastle提供的RipeMD160算法,需要先把BouncyCastle注册一下

public class Main {public static void main(String[] args) throws Exception {// 注册BouncyCastle提供的通知类对象BouncyCastleProviderSecurity.addProvider(new BouncyCastleProvider());// 获取RipeMD160算法的"消息摘要对象"(加密对象)MessageDigest md = MessageDigest.getInstance("RipeMD160");// 更新原始数据md.update("HelloWorld".getBytes());// 获取消息摘要(加密)byte[] result = md.digest();// 消息摘要的字节长度和内容System.out.println(result.length); // 160位=20字节System.out.println(Arrays.toString(result));// 16进制内容字符串String hex = new BigInteger(1,result).toString(16);System.out.println(hex.length()); // 20字节=40个字符System.out.println(hex);}
}

四、Hmac算法:

mac算法就是一种基于密钥的消息认证码算法,它的全称是Hash-based Message Authentication Code,是一种更安全的消息摘要算法。Hmac算法总是和某种哈希算法配合起来用的。例如我们使用MD5算法,对应的就是Hmac MD5算法,它相当于“加盐”的MD5HmacMD5 ≈md5(secure_random_key, input)

HmacMD5加密:

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;public class main {public static void main(String[] args) throws NoSuchAlgorithmException, IllegalStateException, UnsupportedEncodingException, InvalidKeyException {// 获取HmacMD5秘钥生成器KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5");// 产生秘钥SecretKey secreKey = keyGen.generateKey();// 打印随机生成的秘钥:byte[] keyArray = secreKey.getEncoded();StringBuilder key = new StringBuilder();for(byte bite:keyArray) {key.append(String.format("%02x", bite));}System.out.println(key);// 使用HmacMD5加密Mac mac = Mac.getInstance("HmacMD5");mac.init(secreKey); // 初始化秘钥mac.update("HelloWorld".getBytes("UTF-8"));byte[] resultArray = mac.doFinal();StringBuilder result = new StringBuilder();for(byte bite:resultArray) {result.append(String.format("%02x", bite));}System.out.println(result);}
}

解密:

// 原始密码
String password = "nhmyzgq";// 通过"秘钥的字节数组",恢复秘钥
byte[] keyByteArray = {126, 49, 110, 126, -79, -5, 66, 34, -122, 123, 107, -63, 106, 100, -28, 67, 19, 23, 1, 23, 47, 63, 47, 109, 123, -111, -27, -121, 103, -11, 106, -26, 110, -27, 107, 40, 19, -8, 57, 20, -46, -98, -82, 102, -104, 96, 87, -16, 93, -107, 25, -56, -113, 12, -49, 96, 6, -78, -31, -17, 100, 19, -61, -58};// 恢复秘钥
SecretKey key = new SecretKeySpec(keyByteArray,"HmacMD5");// 加密
Mac mac = Mac.getInstance("HmacMD5");
mac.init(key);
mac.update(password.getBytes());
byte[] resultByteArray = mac.doFinal();StringBuilder resultStr = new StringBuilder();
for(byte b : resultByteArray) {resultStr.append(String.format("%02x", b));
}
System.out.println("加密结果:" + resultStr);

五、对称加密算法:

对称加密算法就是传统的用一个秘钥进行加密和解密。例如,我们常用的WinZIPWinRAR对压缩包的加密和解密,就是使用对称加密算法:

从程序的角度看,所谓加密,就是这样一个函数,它接收密码和明文,然后输出密文:

secret = encrypt(key, message);

而解密则相反,它接收密码和密文,然后输出明文:

plain = decrypt(key, secret);

在软件开发中,常用的对称加密算法有:

算法

密钥长度

工作模式

填充模式

DES

56/64

ECB/CBC/PCBC/CTR/...

NoPadding/PKCS5Padding/...

AES

128/192/256

ECB/CBC/PCBC/CTR/...

NoPadding/PKCS5Padding/PKCS7Padding/...

IDEA

128

ECB

PKCS5Padding/PKCS7Padding/...

 

密钥长度直接决定加密强度,而工作模式和填充模式可以看成是对称加密算法的参数和格式选择。Java标准库提供的算法实现并不包括所有的工作模式和所有填充模式。

1、使用AES加密:

(1)ECB模式:ECB模式是最简单的AES加密模式,它需要一个固定长度的密钥,固定的明文会生成固定的密文。

import java.security.*;
import java.util.Base64;import javax.crypto.*;
import javax.crypto.spec.*;public class Main {public static void main(String[] args) throws Exception {// 原文:String message = "Hello, world!";System.out.println("Message(原始信息): " + message);// 128位密钥 = 16 bytes Key:byte[] key = "1234567890abcdef".getBytes();// 加密:byte[] data = message.getBytes();byte[] encrypted = encrypt(key, data);System.out.println("Encrypted(加密内容): " + Base64.getEncoder().encodeToString(encrypted));// 解密:byte[] decrypted = decrypt(key, encrypted);System.out.println("Decrypted(解密内容): " + new String(decrypted));}// 加密:public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {// 创建密码对象,需要传入算法/工作模式/填充模式Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");// 根据key的字节内容,"恢复"秘钥对象SecretKey keySpec = new SecretKeySpec(key, "AES");// 初始化秘钥:设置加密模式ENCRYPT_MODEcipher.init(Cipher.ENCRYPT_MODE, keySpec);// 根据原始内容(字节),进行加密return cipher.doFinal(input);}// 解密:public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {// 创建密码对象,需要传入算法/工作模式/填充模式Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");// 根据key的字节内容,"恢复"秘钥对象SecretKey keySpec = new SecretKeySpec(key, "AES");// 初始化秘钥:设置解密模式DECRYPT_MODEcipher.init(Cipher.DECRYPT_MODE, keySpec);// 根据原始内容(字节),进行解密return cipher.doFinal(input);}
}

Java标准库提供的对称加密接口非常简单,使用时按以下步骤编写代码:

(1)根据算法名称/工作模式/填充模式获取Cipher实例;

(2)根据算法名称初始化一个SecretKey实例,密钥必须是指定长度;

(3)使用SerectKey初始化Cipher实例,并设置加密或解密模式;

(4)传入明文或密文,获得密文或明文。

2、CBC模式:ECB模式是最简单的AES加密模式,这种一对一的加密方式会导致安全性降低。所以,更好的方式是通过CBC模式,它需要一个随机数作为IV参数,这样对于同一份明文,每次生成的密文都不同:

import java.security.*;
import java.util.Base64;import javax.crypto.*;
import javax.crypto.spec.*;public class Main {public static void main(String[] args) throws Exception {// 原文:String message = "Hello, world!";System.out.println("Message(原始信息): " + message);// 256位密钥 = 32 bytes Key:byte[] key = "1234567890abcdef1234567890abcdef".getBytes();// 加密:byte[] data = message.getBytes();byte[] encrypted = encrypt(key, data);System.out.println("Encrypted(加密内容): " + Base64.getEncoder().encodeToString(encrypted));// 解密:byte[] decrypted = decrypt(key, encrypted);System.out.println("Decrypted(解密内容): " + new String(decrypted));}// 加密:public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {// 设置算法/工作模式CBC/填充Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");// 恢复秘钥对象SecretKeySpec keySpec = new SecretKeySpec(key, "AES");// CBC模式需要生成一个16 bytes的initialization vector:SecureRandom sr = SecureRandom.getInstanceStrong();byte[] iv = sr.generateSeed(16); // 生成16个字节的随机数System.out.println(Arrays.toString(iv));IvParameterSpec ivps = new IvParameterSpec(iv); // 随机数封装成IvParameterSpec参数对象// 初始化秘钥:操作模式、秘钥、IV参数cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps);// 加密byte[] data = cipher.doFinal(input);// IV不需要保密,把IV和密文一起返回:return join(iv, data);}// 解密:public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {// 把input分割成IV和密文:byte[] iv = new byte[16];byte[] data = new byte[input.length - 16];System.arraycopy(input, 0, iv, 0, 16); // IVSystem.arraycopy(input, 16, data, 0, data.length); //密文System.out.println(Arrays.toString(iv));// 解密:Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // 密码对象SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); // 恢复秘钥IvParameterSpec ivps = new IvParameterSpec(iv); // 恢复IV// 初始化秘钥:操作模式、秘钥、IV参数cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);// 解密操作return cipher.doFinal(data);}// 合并数组public static byte[] join(byte[] bs1, byte[] bs2) {byte[] r = new byte[bs1.length + bs2.length];System.arraycopy(bs1, 0, r, 0, bs1.length);System.arraycopy(bs2, 0, r, bs1.length, bs2.length);return r;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/917228.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/917228.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EasyExcel 公式计算大全

EasyExcel 是基于 Apache POI 的封装,主要专注于简化 Excel 的读写操作,对于公式计算的支持相对有限。以下是 EasyExcel 中处理公式计算的全面指南:1. 基本公式写入1.1 写入简单公式Data public class FormulaData {ExcelProperty("数值…

2025年AI+数模竞赛培训意见征集-最后一轮

在过去几天的“AI时代下2025年数模竞赛培训课程需求调研紧急征集”我们收到了大量老师、学生的反馈。我们通过大家的实际需求,编写了下述2025年AI时代下最新的数学建模竞赛教学课程课程表,具体授课内容以及相关课件、支撑材料都将会免费发布,…

Qwen2 RotaryEmbedding 位置编码仅仅是第一层有吗

Qwen2 RotaryEmbedding 位置编码仅仅是第一层有吗,还是全部层都有 Qwen2 模型中的 Rotary Embedding(旋转位置编码)是应用于所有 Transformer 层 的,而非仅第一层。 1. Transformer 架构的核心逻辑 Qwen2 基于 Decoder-only Transformer 架构,而位置编码(如 Rotary Emb…

CNN卷积神经网络之LeNet和AlexNet经典网络模型(三)

CNN卷积神经网络之LeNet和AlexNet经典网络模型(三) 文章目录CNN卷积神经网络之LeNet和AlexNet经典网络模型(三)深度学习两大经典 CNN 模型速览1. LeNet-5:CNN 的开山之作(1998)2. AlexNet&#…

江协科技STM32 12-2 BKP备份寄存器RTC实时时钟

这一节我们要讲的主要内容是RTC实时时钟,实时时钟本质上是一个定时器,但是这个定时器是专门用来产生年月日时分秒,这种日期和时间信息的。所以学会了STM32的RTC就可以在STM32内部拥有一个独立运行的钟表。想要记录或读取日期和时间&#xff0…

【10】大恒相机SDK C++开发 ——对相机采集的原图像数据IFrameData裁剪ROI 实时显示在pictureBox中,3种方法实现(效率不同)

文章目录1 在回调函数中实现2 独立封装调用2.1 获取图像宽、高、pBuffer、channel2.2 内存图像数据截取ROI并显示2.3 回调函数调用3 for循环嵌套 方法24 for循环嵌套 方法35 按行复制数据提高效率,但很耗内存6 unsafe代码 解释及注意事项 看我另一篇文章7 ConvertTo…

ubuntu22.04系统入门 linux入门(二) 简单命令 多实践以及相关文件管理命令

以下有免费的4090云主机提供ubuntu22.04系统的其他入门实践操作 地址:星宇科技 | GPU服务器 高性能云主机 云服务器-登录 相关兑换码星宇社区---4090算力卡免费体验、共享开发社区-CSDN博客 之所以推荐给大家使用,是因为上面的云主机目前是免费使用的…

分布式ID方案(标记)

一、参考文章-标记 分布式ID方案有哪些?雪花算法如何搞定时钟回拨和动态机器ID? 二、应用 1.百度 uid-generator github项目地址 原理参考 2.百度 uid-generator 扩展应用 灯官网 灯 项目代码 lamp-util 单元模块 lamp-util 单元模块子模块 lamp-…

std::map 加锁

在并发环境下使用std::map,必须采取同步措施。 在并发环境下对 std::map 进行不加锁的读写操作会导致严重的线程安全问题,主要会产生以下几种问题: ⚠️ 主要风险与后果数据竞争(Data Race) 当多个线程同时修改同一个键…

学习笔记090——Ubuntu 中 UFW 防火墙的使用

文章目录1、允许特定的端口访问2、允许特定 IP 访问某个端口3、允许某个范围的端口4、查看 UFW 状态5、重新加载 UFW6、启用 UFW7、关闭 UFW1、允许特定的端口访问 # 允许 TCP 端口(例如 80): sudo ufw allow 80/tcp# 允许 UDP 端口&#xf…

移动端 WebView 内存泄漏与性能退化问题如何排查 实战调试方法汇总

在混合 App 应用中,WebView 页面常承载复杂业务逻辑与交互。随着用户使用时间增长,特别在切换多个页面或反复打开界面后,常常会出现性能下降、页面卡顿、甚至白屏崩溃等现象。这通常是因为页面存在内存泄漏、事件监听未解绑或垃圾回收阻塞导致…

JSON 对象在浏览器中顺序与后端接口返回不一致的问题

一、问题描述 后端接口返回一个字典表的JSON对象,页面展示排序与预期排序不一致。 在浏览器调试面板Response中看到接口原始响应字符串,是期望顺序:在Preview中看到, key “22” 被提到最前,顺序发生变化:页…

Spring MVC数据传递全攻略

Spring MVC数据传递一、前端到后端的数据传递1. 使用 RequestParam 传递简单参数2. 使用 PathVariable传递路径参数3. 使用RequestBody传递 JSON 数据二、后端到前端的数据传递1. 使用Model或 ModelAndView传递数据到前端2. 使用HttpServletResponse直接写回数据3.使用Response…

仓库管理系统-12-前端之头部区域Header基于嵌套路由访问个人中心

文章目录 1 个人中心 1.1 DateUtils.vue(子组件) 1.2 Home.vue(父组件) 1.3 router/index.js(嵌套路由) 1.4 index.vue(路由占位符) 2 Header.vue 2.1 页面布局 2.2 toUser方法 2.3 初始加载 2.4 Header.vue 头部区域Header中有一个个人中心下拉菜单,点击个人中心选项,通过嵌…

【智能协同云图库】第七期:基于AI调用阿里云百炼大模型,实现AI图片编辑功能

摘要:AI 高速发展赋能传统业务,图库网站亦有诸多 AI 应用空间。以 AI 扩图功؜能为例,让我们来学习如何在项目⁠中快速接入 AI 绘图大模型。‏用户可以选择一张已上传的图片,‌通过 AI 扩图得到新的图片,希望可以帮到大…

Notepad++插件安装

方式一:自动安装(有些notepad并不好用,推荐方式二)工具栏-》插件-》插件管理如下点击安装后会提示,后端安装,安装成功后自动启动,本人使用的v8.6.4的版本,插件基本都无法自动安装&am…

git pull和git fetch的区别

git pull和git fetch是git版本控制系统中的两个基本命令,它们都用于从远程仓库更新本地仓库的信息,但执行的具体操作不同。git fetch:git fetch下载远程仓库最新的内容到你的本地仓库,但它并不自动合并或修改你当前的工作。它取回了远程仓库的…

Item35:考虑virtual函数以外的其他选择

在C++中,虚函数是实现多态的传统方式,但并非唯一选择。过度依赖虚函数可能导致派生类与基类的强耦合,或难以在运行时灵活切换行为。《Effective C++》Item35指出:应根据场景选择更合适的替代方案,包括NVI模式、函数指针、策略模式等。本文解析这些方案的原理、适用场景及实…

Vue3 状态管理新选择:Pinia 从入门到实战

一、什么是pinia? 在 Vue3 生态中,状态管理一直是开发者关注的核心话题。随着 Vuex 的逐步淡出,Pinia 作为官方推荐的状态管理库,凭借其简洁的 API、强大的功能和对 Vue3 特性的完美适配,成为了新时代的不二之选。今天我们就来深…

Unity相机控制

相机的控制无非移动和旋转,每种操作各3个轴6个方向,一共12种方式。在某些需要快速验证的项目或Demo里常常需要丝滑的控制相机调试效果。相机控制虽然不是什么高深的技术,但是要写的好用还是很磨人的。 锁定Z轴的旋转 一个自由的相机可以绕 …