加密签名使用指导
本文 PHP 后端已配套 Node.js 与 Python 版本,见页面中的对应实现;全部后端源码与运行说明。
加密签名组件提供 App 开发中最常用的几种算法:摘要(MD5、SHA1、SHA256)、HMAC-SHA256 签名、 AES 加解密、Base64 编解码和随机字符串。所有方法直接返回结果文本,不需要事件。
先分清三件事
| 需求 | 用什么 | 能不能还原 |
|---|---|---|
| 判断两段内容是否一样(校验、给密码做”指纹”) | 摘要:MD5摘要(文本)(MD5)、SHA256摘要(文本)(SHA256) |
不能还原 |
| 证明请求是自己发的、没被改过(接口签名) | 签名:HmacSHA256签名(文本,键)(HmacSHA256)(要一个密钥) |
不能还原 |
| 内容要保密,之后还要读回来 | 加密:AES加密(文本,键,iv)(AesEncrypt) / AES解密(密文,键,iv)(AesDecrypt) |
用同一个密钥能还原 |
| 把文字变成只含字母数字的形式,方便放进网址或请求头 | 编码:Base64编码(文本)(Base64Encode) |
谁都能还原,不是加密 |
要点:
- 所有文本都按 UTF-8 转成字节再计算,中文也和服务器、电脑上的工具算出的一样;
- 摘要和签名默认输出小写十六进制,接口要求大写时套一层文本的「转大写」,要求 Base64 时把摘要格式(
DigestFormat)改为 base64; - 出错(如 AES 密钥长度不对、密文损坏)时返回空文本,并触发屏幕的「出现错误时」,原因是中文说明。
快速上手
拖一个加密签名、一个文本输入框、一个按钮和一个标签:
when 按钮1.Click() {
标签1.Text = join("MD5:", 加密签名1.MD5(文本输入框1.Text))
}
输入 abc,标签显示 MD5:900150983cd24fb0d6963f7d28e17f72。
案例一:登录前给密码做摘要
不要把明文密码直接发给服务器或存在手机上。先加一段固定的”盐”再做 SHA256,服务器端用同样的方法比对:
when 按钮_登录.Click() {
Web1.Url = "https://example.com/api/login"
Web1.PostText(join("user=", 文本输入框_账号.Text, "&pwd=", 加密签名1.SHA256(join(密码输入框1.Text, "fun123-salt"))))
}
盐可以是任意一段固定文字,App 和服务器保持一致即可。MD5 已不适合单独用来保存密码,优先用 SHA256。
案例二:调用接口的参数签名
很多接口要求每次请求带上时间戳、随机串和签名,服务器用同一个密钥重新计算签名来验证请求:
global 参数 = ""
when 按钮_查询.Click() {
参数 = join("appid=10086&nonce=", 加密签名1.RandomString(16), "&ts=", 计时器1.SystemTime())
Web1.Url = join("https://example.com/api/query?", 参数, "&sign=", upcase(加密签名1.HmacSHA256(参数, "my-secret")))
Web1.Get()
}
要点:
- 参数的拼接顺序、大小写、分隔符以接口文档为准,差一个字符签名就对不上;
- 密钥写在 App 里会被反编译看到,重要的接口应由自己的服务器转发签名;
- 部分云服务要求签名是 Base64:把摘要格式(
DigestFormat)设为 base64,就不用转大写了。
案例三:AES 加密保存敏感数据
把日记、身份证号这类内容加密后再存进微数据库;读取时解密。密钥和 IV 各 16 个字符(AES-128-CBC):
when 按钮_保存.Click() {
微数据库1.StoreValue("日记", 加密签名1.AesEncrypt(文本输入框_日记.Text, "1234567890123456", "abcdefghijklmnop"))
标签_状态.Text = "已加密保存"
}
when 按钮_读取.Click() {
文本输入框_日记.Text = 加密签名1.AesDecrypt(微数据库1.GetValue("日记", ""), "1234567890123456", "abcdefghijklmnop")
}
要点:
- 密钥 16、24、32 个字符分别对应 AES-128、192、256,越长越安全;
- IV 填 16 个字符是 CBC 模式(推荐),留空是 ECB 模式;
- 同一段明文、同样的密钥和 IV,加密结果总是一样。需要每次不同时,可以每次随机生成 IV(随机字符串(长度)(
RandomString)), 把 IV 和密文一起保存; - 密钥或 IV 不对时解密返回空文本,并提示「解密失败:密钥或 IV 不对,或密文已损坏」。
案例四:Base64 与 Basic 认证请求头
有些接口用 Basic 认证:请求头 Authorization 的值是 Basic 加上「账号:密码」的 Base64:
when 按钮_请求.Click() {
Web1.Url = "https://example.com/api/data"
Web1.RequestHeaders = list(list("Authorization", join("Basic ", 加密签名1.Base64Encode(join(文本输入框_账号.Text, ":", 密码输入框1.Text)))))
Web1.Get()
}
Base64 只是编码,谁都能还原,务必配合 https 使用。图片与 Base64 互转请用图片处理。
与服务器互通
本组件的结果与常用工具一致,服务器端按下面的方式就能对上。以明文 hello 世界、密钥 1234567890123456、
IV abcdefghijklmnop 为例,AES 加密结果都是 YL28tnUQb4uWoJRf3jGP5g==。
Python 对应实现
保存为 crypto.py,在文件所在目录安装依赖并启动:
python3 -m pip install pycryptodome
python3 crypto.py
# python3 -m pip install pycryptodome
import base64
import hashlib
import hmac
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
key, iv = b'1234567890123456', b'abcdefghijklmnop'
cipher = base64.b64encode(AES.new(key, AES.MODE_CBC, iv).encrypt(pad('hello 世界'.encode('utf-8'), 16))).decode('ascii')
print(cipher) # YL28tnUQb4uWoJRf3jGP5g==
plain = AES.new(key, AES.MODE_CBC, iv).decrypt(base64.b64decode(cipher))
print(unpad(plain, 16).decode('utf-8'))
print(hmac.new(b'my-secret', b'appid=10086', hashlib.sha256).hexdigest())
PHP:
$key = '1234567890123456'; $iv = 'abcdefghijklmnop';
echo openssl_encrypt('hello 世界', 'AES-128-CBC', $key, 0, $iv); // YL28tnUQb4uWoJRf3jGP5g==
echo openssl_decrypt('YL28tnUQb4uWoJRf3jGP5g==', 'AES-128-CBC', $key, 0, $iv);
echo hash_hmac('sha256', 'appid=10086', 'my-secret');
Node.js 对应实现
保存为 crypto.cjs,在文件所在目录安装依赖并启动:
node crypto.cjs
// Node.js 内置 crypto,无需 npm 依赖;node crypto.cjs
const {createCipheriv, createDecipheriv, createHmac} = require('node:crypto');
const key = Buffer.from('1234567890123456', 'utf8');
const iv = Buffer.from('abcdefghijklmnop', 'utf8');
const encrypt = createCipheriv('aes-128-cbc', key, iv); // 默认 PKCS7 填充
const cipher = Buffer.concat([encrypt.update('hello 世界', 'utf8'), encrypt.final()]).toString('base64');
console.log(cipher); // YL28tnUQb4uWoJRf3jGP5g==
const decrypt = createDecipheriv('aes-128-cbc', key, iv);
console.log(Buffer.concat([decrypt.update(Buffer.from(cipher, 'base64')), decrypt.final()]).toString('utf8'));
console.log(createHmac('sha256', 'my-secret').update('appid=10086', 'utf8').digest('hex'));
对不上时逐项核对:密钥和 IV 的长度与内容、模式(CBC/ECB)、填充(本组件是 PKCS5/PKCS7)、文本编码(UTF-8)、输出格式(hex/Base64)。
常见问题
| 现象 | 原因与处理 |
|---|---|
| 和服务器算的摘要不一样 | 大小写(本组件输出小写);是否多了空格或换行;服务器是否用的 UTF-8 |
| AES 加密返回空文本,提示密钥长度 | 密钥必须正好 16、24 或 32 个字符;一个汉字按 UTF-8 算 3 个字节,密钥建议只用字母数字 |
| AES 解密返回空文本 | 密钥、IV 与加密时不同;密文被截断或多了字符 |
| Base64解码返回空文本 | 内容不是有效的 Base64(常见:网址里的 + 变成了空格) |
| 想要 RSA 公钥加密 | 本组件不提供,可使用 Encrypt.Security 拓展 |
平台说明
安卓与鸿蒙都支持,结果一致;鸿蒙使用系统加解密框架。
完整 .aia 源码与操作指导
无需账号、网络或外部库。文本按UTF-8处理,AES使用PKCS5/PKCS7兼容填充,密文为Base64;默认CBC。预置密钥仅供本地互通测试,实际接口请替换为协议约定参数。Base64是编码;签名和AES功能独立。
导入 CryptoAllFeatures.aia,连接最新伴侣,或编译独立安装包。真机/伴侣运行尚未实测。
| API / 功能 | 操作入口 | 预期结果 |
|---|---|---|
| DigestFormat 读取与设置、MD5 / SHA1 / SHA256 / HmacSHA256 | 切换 hex/base64 后点摘要 | 显示四种真实计算结果;HMAC 使用密钥输入 |
| AesEncrypt / AesDecrypt | 默认参数点加密 | 显示 Base64 密文、中文还原、一致为真;自动填入解密输入 |
| AesDecrypt | 点解密,或输入外部兼容密文 | 使用同一密钥与向量,显示实际结果或错误 |
| AES ECB/CBC | 清空向量后重新加密;恢复16字符向量 | 空为 ECB;非空为 CBC;两种均支持往返 |
| Base64Encode / Base64Decode | 点编码 | 中文文本往返一致;空文本也可往返 |
| RandomString | 点随机,再重新加密 | 密钥与向量各16个随机字符;不再用新密钥解旧密文 |
| 错误反馈 | 点错误或填错误长度向量 | 返回空值并显示组件错误,可修正后继续 |
所有返回值均用于界面反馈,错误由底部错误区展示。设计器采用明确非默认配置,初始化与界面同步。API 覆盖检查按最终 AIA 检查方法、事件、属性读取与设置及设计器配置,不能替代真实设备验证。
加密签名
导入方法:项目 → 导入项目(.aia)→ 选择下载的 .aia 源码文件。 无需账号、网络或外部库。文本按UTF-8处理,AES使用PKCS5/PKCS7兼容填充,密文为Base64;默认CBC。预置密钥仅供本地互通测试,实际接口请替换为协议约定参数。Base64是编码;签名和AES功能独立。 覆盖检查 12/12。首次载入及保存重开均0个错误,真实后台导入、安装包编译通过;真机/伴侣/硬件联调尚未实测。全部属性读写、方法与事件的操作入口和预期结果见组件指导。
扫码添加客服咨询