加密签名使用指导

← 返回加密签名组件参考 · 全部使用指导

加密签名使用指导

本文 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

下载 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

下载 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 源码与操作指导

下载 .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个错误,真实后台导入、安装包编译通过;真机/伴侣/硬件联调尚未实测。全部属性读写、方法与事件的操作入口和预期结果见组件指导。

文档反馈