Step by Step之后端对称加解密的实现
lipiwang 2024-11-04 14:33 13 浏览 0 评论
接着上一篇视频播放实现的方案,虽然实现了播放端的token认证,但并未进行加扰加密,安全性较差,所以本篇增加了前后端加扰环节,来提升整体解决方案的安全性。
一、视频播放认证安全
1、Web应用安全体系
Web应用安全体系的基础是HTTPS,它的核心就是在密钥交换时采用非对称加密方式,在数据传送时采用对称加密方式。
现在Web应用普遍采用HTTPS方式进行线上部署,通过运维工程手段即可实现,研发人员都不需要了解其中的技术细节,他们需要关注更多的是跨域认证、Session保持等内容。
视频播放属于Web应用中的一个环节,总体上沿用认证登录和Session保持机制,只需将认证登录后产生的token进行携带,并在OpenResty端进行安全校验即可。
2、Web应用常用加解密方法
(1)主流的对称加密算法是AES(高级加密标准),分组长度只能是128位,即每个分组16个字节,密钥的长度可以使用128位、192位或256位,密钥长度不同,推荐的加密轮数也不同,分别对应10轮、12轮和14轮。
AES使用时的参数配置主要包括加密模式和填充方式。
(2)AES的主要加密模式
最简单的是ECB电话本模式,一般也是默认模式,它将整个明文分成若干段相同的小段,然后对每一小段进行加密,具有简单、可并行、不传送误差等优点,但掩盖不了明文结构信息,难以抵抗统计分析攻击。
复杂模式最常用的CBC密码分组链模式,先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。优点是能掩盖明文结构信息,保证相同密文可得不同明文,所以不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL和IPSec的标准。缺点:不利于并行计算;会传递误差——前一个出错则后续全错;第一个明文块需要与一个初始化向量IV进行抑或,初始化向量IV的选取比较复杂。
其他模式还有OFB输出反馈模式、计数器模式CRT、密码反馈模式CFB等。
(3)AES的填充模式
常用有NoPadding、ZerosPadding、PKCS#5 padding、PKCS#7 padding等。
NoPadding是一种不使用填充算法的加密模式,即输入数据将被直接编码为字节序列,而不会受到填充的影响。
ZerosPadding是一种简单的填充算法,将输入数据的最后一个字节替换为0。这种填充方式适用于所有AES加密模式,包括ECB、CBC和CTR模式。
PKCS#5 padding是一种常见的填充算法,最初设计用于加密块大小为8字节(64位)的算法,这种填充方式使用一个固定长度的填充字节序列,以确保输入数据被完全填充。
PKCS#7 padding是是PKCS#5的通用版,适用于更大的块大小,是PKCS#5 padding的超集,可以互相替代使用,尤其是在处理AES等块大小为16字节的算法时。
3、加解扰Base64
Base64是二进制到字符编码的一种方案,将二进制数据使用ASCII字符串格式表示,即翻译为基数为64的一种表示。每个Base64数字表示一个6比特的数据。三个字节(共24个比特)因此可以被表示为4个Base64数字。
当未编码的输入的字节数不是3的倍数时,编码输出必须加上缀词来使得输出的长度是4的倍数。这个缀词便是=。
Base64在加解密中经常使用,主要场景包括:(1)加密通信的辅助,在某些加密协议中,Base64用于编码加密后的密文,使其能作为文本安全地在网络中传输,而不必担心特殊字符引起的问题;(2)可以将敏感信息(如密码或密钥)编码为不可直接阅读的形式,增加一定的安全性,这在URL参数中传输敏感数据时尤其有用,一般还需配合HTTPS等加密协议。
4、技术选型
在Java后端,选择Hutool库的AES模块,因为Hutool本身集成了其他许多常用组件,项目中本就已经包含,加密时,采用其默认加密方式:ECB+PKCS5,或者说与之等效的PKCS7填充方式。
在视频认证的场景下,后端根据一定的规则生成Token,认证通过后反馈给客户端(前端或小程序端),客户端对Token进行保存,并在每次发送信息时放在请求头部(Header)或请求主体(如Path变量或Body中),因此客户端不需要进行加解密或加解扰操作。
在我们的视频简化解决方案中,OpenResty提供了视频流播放服务,所以需要在OpenResty端对视频播放请求携带的Token进行解扰解密,并与Redis进行交互以验证Token的有效性。在lua中,选择使用其默认包含的resty.aes库进行加解密。
二、核心实现代码
1、Java后端实现
因为Token的加解密都在服务器端,不需要与客户端进行密钥交换,所以在Java端和OpenResty端保持一致即可。
import cn.hutool.crypto.symmetric.AES;
import java.nio.charset.StandardCharsets;
public class TokenBase {
public String tokenKey = "0123456789012345"; // 16位密钥
public Integer expiredTime = 3600; // 有效时间为一小时
// 加密函数
private String encryptToken(String token) {
AES aes = new AES(tokenKey.getBytes(StandardCharsets.UTF_8));
// 加密Token中包括了生成时的时间,以便进行有效期控制
return aes.encryptBase64(token + System.currentTimeMillis());
}
// 解密函数
private String decryptToken(String data) {
AES aes = new AES(tokenKey.getBytes(StandardCharsets.UTF_8));
String decryptData;
String decryptToken;
try {
decryptData = aes.decryptStr(data);
decryptToken = decryptStr.substring(0,32);
// 创建token的时间
long tokenCreateTime = Long.parseLong(decryptData.substring(32,45));
// 判断token是否过期
if (System.currentTimeMillis() - tokenCreateTime > expiredTime * 1000L) {
// 进行token过期的异常处理
}
}
catch (Exception e) {
// 进行其他异常处理
}
return decryptToken;
}
}
2、OpenResty端实现
核心代码如下:
// nginx的配置文件
server {
listen 443 ssl;
server_name abc.com www.abc.com;
// 其他配置信息
location /media {
auth_request /media_auth;
root /usr/local/openresty/nginx/html;
error_page 401 = @error401;
mp4;
mp4_buffer_size 1m;
mp4_max_buffer_size 10m;
}
location /media_auth {
content_by_lua_block {
-- 从URL的Path变量中获取token
local _, _, token = string.find(ngx.var.request_uri, "%?token=(.*)&?")
if token == nil or token == "" then
ngx.log(ngx.ERR, "token not found!")
return ngx.exit(401)
end
-- 解扰和解密token
local resty_aes = require "resty.aes"
local resty_string = require "resty.string"
local key = "0123456789012345" -- 与java端保持一致即可
local aes_obj = resty_aes:new(key, nil, resty_aes.cipher(128, "ecb"),{iv="0000000000000000"})
local des_token = ngx.decode_base64(token)
local decrypted, err = aes_obj:decrypt(des_token)
if not decrypted then
ngx.log(ngx.ERR, "Failed to decrypt the token: ", err)
ngx.exit(401)
end
local decryptToken = string.sub(decrypted, 1, 32)
-- 检查redis是否存在对应的token键值
local redis = require "redis.redis_resty"
local opts = { ip = "redis-ip", port = "6379", password = "redis-pass", db_index = 0 }
local conn = redis:new(opts)
local ok, err = conn:get("redis-key-prefix" .. decryptToken)
if not ok then
ngx.log(ngx.ERR, "token not found in redis! error message is :", err)
return ngx.exit(401)
end
return ngx.exit(200)
}
}
Lua是踩坑较多的地方,这可能也体现了一种不够流行语言的通病,即Bug或问题较多,因为用的人不够,没有踩遍各种坑,这些坑也就没人填。
(1)base64解扰,一开始百度和找大模型,先告诉直接使用自带的string库,结果直接提示找不到该函数,因为项目使用了最新的openresty镜像,找了源码确实没有这个函数,于是先网上找了一个源码,后来在用python进行验证时发觉不对,这么简单的代码怎么可能没有,于是在镜像的lualib目录下进行grep,结果发现在/resty/core目录下有base64.lua文件,定义了ngx.decode_base64和ngx.encode_base64函数,于是就直接拿来用了。
(2)团队调试花时间最多的在aes库的new函数上,百度上最多和大模型最初的反馈都是很简单的调用,new(key, "ecb"),结果直接报错,提示没有size字段,继续百度,发现第二个参数为resty_aes.cipher(128, "ecb"),继续报错,于是翻看源码aes.lua,发现需要第二个参数,于是增加了一个nil,总算new函数不再报错。但马上就是解密报错,提示EVP_DecryptFinal_ex failed,因为解密函数调用参数非常简单,还是怀疑为new的参数问题,百度发现有人提到即使用ecb模式,也需要填写cbc才需要的初始化向量iv,增加了参数后终于ok了。
而按照理论上ecb是不需要iv的,尝试过将iv设置为其他的16位字符串,解密同样没有问题,所以估计是代码的bug吧,因为最终代码是c的库,所以就没再深究下去了。
3、Python实现
在查找问题时为了对照,用Python也写了同样的代码,发现它无愧于排名第一的编程语言,组件库很多,三两下就弄出来了。
// 使用pip安装库pycryptodome,base64自身就有
from Crypto.Cipher import AES
import base64
key='0123456789012345'
token = '需解扰解密的token串'
cipher = AES.new(key.encode('utf-8'), AES.MODE_ECB)
str = base64.b64decode(token)
out = cipher.decrypt(str)
相关推荐
- 微软Office Open XML中的数字签名漏洞
-
MicrosoftOffice是最广泛使用的办公文档应用程序之一。对于重要文件,如合同和发票,可以对其内容进行签名,以确保其真实性和完整性。自2019年以来,安全研究人员发现了针对PDF和ODF等其...
- Javaweb知识 day12 XML(javaweb中xml作用)
-
一、XML:1.1概念:ExtensibleMarkupLanguage可扩展标记语言*可扩展:标签都是自定义的。<user><student>1.2功能:...
- 易筋洗髓功——内外同修方可致远(易筋洗髓功口诀)
-
达摩祖师所传易筋、洗髓两经,一分为二,二实为一,无非以方便法门接引众生,而归于慈悲清净之心地。修炼《易筋经》是为强身健体,修炼《洗髓经》是为修心养性,此二者相辅相成,内外兼修,缺一不可。这是一套传统中...
- 道家洗髓功修炼要义,洗髓功如何做到丹田聚气?
-
不管是道家洗髓功,还是洗髓经,其修炼的关键点就在于得气、行气、聚气...那么,作为洗髓功修炼者,具体该怎么做呢?在实际修炼中,就洗髓功的修炼方法来讲,我们可以简单的归纳为修炼三部曲,其具体表现如下:一...
- 「清风聊练功」师门传我易筋经:聊聊我的学习经历和正身图感受
-
一个人的眼界认识,是随着是自身的知识积累和水平不断成长的。开篇为什么要说这么一句呢?是从我的学习经历上感受明显的这句话:一处不到一处迷。我们学传统武术,内功功法,也是从小白到明白一步步走的,走的越远,...
- 内功外练功介绍(练内功 外功)
-
这里介绍我练习的两套动功心得体会。是老道长的八部金刚功、长寿功和增演易筋洗髓经。八部金刚功外练奇经八脉,练出健康强壮的好身体还是可以的,长寿功也是内练功法。这部功法很好的预防效果。这个大家都认同的。说...
- 《增演易筋洗髓内功图说》17卷(1930年(清)周述官撰 1
-
少林空悟老师珍藏
- 国术典籍:《增演易筋洗髓内功图说》【2024年8月编校】
-
《增演易筋洗髓内功图说》系养生气功著作,全书共十八卷。清周述官编撰于光绪二十一年(1895年)。清光绪十九年(1893年),僧人静一空悟将少林功法传授于周述官,并将《增益易筋洗髓内功图说》十二卷(按,...
- 小说:自媒体小白的修道之路-洗髓(自媒体小白运营技巧)
-
谁应了谁的劫,谁又变成了谁的执念。当沧海遗忘了桑田,这世间又多了一个不回家的人!异域空间中,知生缓缓起身,目光扫了一下小帝后,又转身看向画板上的那朵白色蒲公英,自言道:“白瑛,这一世我们莫要再辜负了!...
- 这才是少林洗髓经真相:它是静功和导引术与八段锦暗合
-
不少朋友误解易筋经和洗髓经,将其简单归为强力呼吸的吐纳功以及为了提升房中的关窍功。事实上易筋经和洗髓经是两部功法:易筋经主要为炼体,包含以膜论为核心的十二月怕打筋膜法,以及辅助的呼吸、导引功法;洗髓经...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- maven镜像 (69)
- undefined reference to (60)
- zip格式 (63)
- oracle over (62)
- date_format函数用法 (67)
- 在线代理服务器 (60)
- shell 字符串比较 (74)
- x509证书 (61)
- localhost (65)
- java.awt.headless (66)
- syn_sent (64)
- settings.xml (59)
- 弹出窗口 (56)
- applicationcontextaware (72)
- my.cnf (73)
- httpsession (62)
- pkcs7 (62)
- session cookie (63)
- java 生成uuid (58)
- could not initialize class (58)
- beanpropertyrowmapper (58)
- word空格下划线不显示 (73)
- jar文件 (60)
- jsp内置对象 (58)
- makefile编写规则 (58)