跳转至

rsa: RSA 加密/解密 & 签名/验证 for nginx-module-luaJIT

安装

如果您尚未设置 RPM 仓库订阅,请 注册。然后您可以继续以下步骤。

CentOS/RHEL 7 或 Amazon Linux 2

yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-rsa

CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-rsa

要在 NGINX 中使用此 Lua 库,请确保已安装 nginx-module-lua

本文档描述了 lua-resty-rsa v1.1.1,于 2024 年 11 月 09 日发布。


该库需要一个带有 OpenSSL 的 nginx 构建, ngx_lua 模块LuaJIT

概述

    # nginx.conf:

    server {
        location = /test {
            content_by_lua_file conf/test.lua;
        }
    }

    -- conf/test.lua:

    local resty_rsa = require "resty.rsa"
    local rsa_public_key, rsa_priv_key, err = resty_rsa:generate_rsa_keys(2048)
    if not rsa_public_key then
        ngx.say('generate rsa keys err: ', err)
    end

    ngx.say(rsa_public_key)
    --[[
    -----BEGIN RSA PUBLIC KEY-----
    MIIBCgKCAQEAuw4T755fepEyXTM66pzf6nv8NtnukQTMGnhmBFIFHp/P2vEpxjXU
    BBDUpzKkVFR3wuK9O1FNmRDAGNGYC0N/9cZNdhykA1NixJfKQzncN31VJTmNqJNZ
    W0x7H9ZGoh2aE0zCCZpRlC1Rf5rL0SVlBoQkn/n9LnYFwyLLIK5/d/y/NZVL6Z6L
    cyvga0zRajamLIjY0Dy/8YIwVV6kaSsHeRv2cOB03eam6gbhLGIz/l8wuJhIn1rO
    yJLQ36IOJymbbNmcC7+2hEQJP40qLvH7hZ1LaAkgQUHjfi8RvH2T1Jmce7XGPxCo
    Ed0yfeFz+pL1KeSWNey6cL3N5hJZE8EntQIDAQAB
    -----END RSA PUBLIC KEY-----
    ]]--

    ngx.say(rsa_priv_key)
    --[[
    -----BEGIN RSA PRIVATE KEY-----
    MIIEpAIBAAKCAQEAuw4T755fepEyXTM66pzf6nv8NtnukQTMGnhmBFIFHp/P2vEp
    xjXUBBDUpzKkVFR3wuK9O1FNmRDAGNGYC0N/9cZNdhykA1NixJfKQzncN31VJTmN
    qJNZW0x7H9ZGoh2aE0zCCZpRlC1Rf5rL0SVlBoQkn/n9LnYFwyLLIK5/d/y/NZVL
    6Z6Lcyvga0zRajamLIjY0Dy/8YIwVV6kaSsHeRv2cOB03eam6gbhLGIz/l8wuJhI
    n1rOyJLQ36IOJymbbNmcC7+2hEQJP40qLvH7hZ1LaAkgQUHjfi8RvH2T1Jmce7XG
    PxCoEd0yfeFz+pL1KeSWNey6cL3N5hJZE8EntQIDAQABAoIBAGim1ayIFK8EMQNH
    uDyui/Aqcc9WWky0PGTK23irUsXxb1708gQ89WNY70Cj6qBrqZ1VMb3QHPP4FSFN
    kh0rJJoi2g+ssm5R5r5KlhTKeFRrQInVC1Y3KhUUUwZa4aWtnhgSJ7Urq1yVhjU4
    K7PVkhH1OHBwcp/d1Bd6jd65AgPkY63P+WpcARJkClmQ1RhgoRwThyJdpKrV4/gO
    ha0AUGlJNRNvRwiZxP0zaI5C8RdrG96SnVpeYOcD0z/M1HVlkoYMXsXLKttwLfpK
    88Igtm6ZJwRpfuMF5VA+9hHaYGCBdGz0B/rMp2fc+EtrOavYQGrWIWi2RL1Qk6Rt
    BUyeTgECgYEA9anj4n/cak1MT+hbNFsL31mJXryl1eVNjEZj/iPMztpdS15CmFgj
    Kjr9UuintjSiK7Is43nZUWWyP1XQjRhVi2uP7PRIv92QNl/YteWD6tYCInJHKe2J
    QqYyZrElezsdayXb5DK6bi1UIYYji90g79N7x6pOR0UnQNQUXTv+Y8ECgYEAwuzl
    6Ez4BSXIIL9NK41jfNMa73Utfl5oO1f6mHM2KbILqaFE76PSgEeXDbOKdcjCbbqC
    KCGjwyPd+Clehg4vkYXTq1y2SQGHwfz7DilPSOxhPY9ND7lGbeNzDUK4x8xe52hd
    MWKdgqeqCK83e5D0ihzRiMah8dbxmlfLAOZ3sPUCgYEA0dT9Czg/YqUHq7FCReQG
    rg3iYgMsexjTNh/hxO97PqwRyBCJPWr7DlU4j5qdteobIsubv+kSEI6Ww7Ze3kWM
    u/tyAeleQlPTnD4d8rBKD0ogpJ+L3WpBNaaToldpNmr149GAktgpmXYqSEA1GIAW
    ZAL11UPIfOO6dYswobpevYECgYEApSosSODnCx2PbMgL8IpWMU+DNEF6sef2s8oB
    aam9zCi0HyCqE9AhLlb61D48ZT8eF/IAFVcjttauX3dWQ4rDna/iwgHF5yhnyuS8
    KayxJJ4+avYAmwEnfzdJpoPRpGI0TCovRQhFZI8C0Wb+QTJ7Mofmt9lvIUc64sff
    GD0wT/0CgYASMf708dmc5Bpzcis++EgMJVb0q+ORmWzSai1NB4bf3LsNS6suWNNU
    zj/JGtMaGvQo5vzGU4exNkhpQo8yUU5YbHlA8RCj7SYkmP78kCewEqxlx7dbcuj2
    LAPWpiDca8StTfEphoKEVfCPHaUk0MlBHR4lCrnAkEtz23vhZKWhFw==
    -----END RSA PRIVATE KEY-----
    ]]--

    local pub, err = resty_rsa:new({ public_key = rsa_public_key })
    if not pub then
        ngx.say("new rsa err: ", err)
        return
    end
    local encrypted, err = pub:encrypt("hello")
    if not encrypted then
        ngx.say("failed to encrypt: ", err)
        return
    end
    ngx.say("encrypted length: ", #encrypted)

    local priv, err = resty_rsa:new({ private_key = rsa_priv_key })
    if not priv then
        ngx.say("new rsa err: ", err)
        return
    end
    local decrypted = priv:decrypt(encrypted)
    ngx.say(decrypted == "hello")

    local algorithm = "SHA256"
    local priv, err = resty_rsa:new({ private_key = rsa_priv_key, algorithm = algorithm })
    if not priv then
        ngx.say("new rsa err: ", err)
        return
    end

    local str = "hello"
    local sig, err = priv:sign(str)
    if not sig then
        ngx.say("failed to sign:", err)
        return
    end
    ngx.say("sig length: ", #sig)

    local pub, err = resty_rsa:new({ public_key = rsa_public_key, algorithm = algorithm })
    if not pub then
        ngx.say("new rsa err: ", err)
        return
    end
    local verify, err = pub:verify(str, sig)
    if not verify then
        ngx.say("verify err: ", err)
        return
    end
    ngx.say(verify)

方法

要加载此库,

  1. 您需要在 ngx_lua 的 lua_package_path 指令中指定此库的路径。例如,lua_package_path "/path/to/lua-resty-rsa/lib/?.lua;;";
  2. 使用 require 将库加载到一个本地 Lua 变量中:
    local rsa = require "resty.rsa"

generate_rsa_keys

语法: public_key, private_key, err = rsa:generate_rsa_keys(bits, in_pkcs8_fmt)

通过指定 bits 的数量生成 rsa 公钥和私钥。 in_pkcs8_fmt 是可选的。如果 in_pkcs8_fmt 为 true,生成的私钥为 PKCS#8 格式,公钥为 PKIX 格式,以 -----BEGIN PUBLIC-----BEGIN PRIVATE 开头。 否则,生成的密钥为 PKCS#1 格式,以 -----BEGIN RSA 开头。

new

语法: obj, err = rsa:new(opts)

通过指定选项表 opts 创建一个新的 rsa 对象实例。

选项表接受以下选项:

  • public_key 指定公 rsa 密钥。
  • private_key 指定私 rsa 密钥。
  • password 指定读取 rsa 密钥的密码。
  • key_type 指定给定密钥的类型。 默认情况下,类型将根据密钥的值进行检测。
key_type 含义
rsa.KEY_TYPE.PKCS1 输入密钥为 PKCS#1 格式(通常以 -----BEGIN RSA PUBLIC 开头)。
rsa.KEY_TYPE.PKIX 输入密钥为 PKIX 格式(通常以 -----BEGIN PUBLIC 开头)。
-- 创建一个 PKIX 格式的公钥 rsa 对象
local resty_rsa = require "resty.rsa"
local pub, err = resty_rsa:new({
    public_key = RSA_PKCS8_PUB_KEY,
    key_type = resty_rsa.KEY_TYPE.PKIX,
})

-- 创建一个 pkcs#8 格式的私钥 rsa 对象
local priv, err = resty_rsa:new({
    private_key = RSA_PKCS8_PASS_PRIV_KEY,
    key_type = resty_rsa.KEY_TYPE.PKCS8,
    -- 如果私钥被加密,您需要指定密码
    -- password = "foobar",
})
  • padding 指定加密/解密时的填充模式。
  • algorithm 指定签名/验证时的摘要算法。
algorithm 含义
md4/MD4/RSA-MD4/md4WithRSAEncryption 使用 md4 的摘要
md5/MD5/RSA-MD5/md5WithRSAEncryption/ssl3-md5 使用 md5 的摘要
ripemd160/RIPEMD160/RSA-RIPEM160/ripemd160WithRSA/rmd160 使用 ripemd160 的摘要
sha1/SHA1/RSA-SHA1/sha1WithRSAEncryption/ssl3-sha1 使用 sha1 的摘要
sha224/SHA224/RSA-SHA224/sha224WithRSAEncryption 使用 sha224 的摘要
sha256/SHA256/RSA-SHA256/sha256WithRSAEncryption 使用 sha256 的摘要
sha384/SHA384/RSA-SHA384/sha384WithRSAEncryption 使用 sha384 的摘要
sha512/SHA512/RSA-SHA512/sha512WithRSAEncryption 使用 sha512 的摘要

encrypt

语法: encrypted, err = obj:encrypt(str)

decrypt

语法: decrypted, err = obj:decrypt(encrypted)

sign

语法: signature, err = obj:sign(str)

verify

语法: ok, err = obj:verify(str, signature)

性能

我得到了以下结果:

encrypt for 50000 times cost : 2.4110000133514s
decrypt for 50000 times cost : 57.196000099182s
sign for 50000 times cost : 59.169999837875s
verify for 50000 times cost : 1.8230001926422s

当我运行此脚本时。

local resty_rsa = require "resty.rsa"
local algorithm = "SHA256"

local rsa_public_key, rsa_priv_key, err = resty_rsa:generate_rsa_keys(2048)
if not rsa_public_key then
    ngx.say("generate rsa keys err: ", err)
    return
end

local pub, err = resty_rsa:new({
    public_key = rsa_public_key,
    padding = resty_rsa.PADDING.RSA_PKCS1_PADDING,
    algorithm = algorithm,
})
if not pub then
    ngx.say("new rsa err: ", err)
    return
end

local priv, err = resty_rsa:new({
    private_key = rsa_priv_key,
    padding = resty_rsa.PADDING.RSA_PKCS1_PADDING,
    algorithm = algorithm,
})
if not priv then
    ngx.say("new rsa err: ", err)
    return
end

local num = 5 * 10000

local str = "hello test"

local encrypted, decrypted, err, sig, verify

ngx.update_time()
local now = ngx.now()

local function timer(operation)
    ngx.update_time()
    local t = ngx.now()

    ngx.say(operation, " for ", num, " times cost : ", t - now, "s")
    now = t
end

for _ = 1, num do
    encrypted, err = pub:encrypt(str)
    if not encrypted then
        ngx.say("failed to encrypt: ", err)
        return
    end
end

timer("encrypt")

for _ = 1, num do
    decrypted = priv:decrypt(encrypted)
    if decrypted ~= str then
        ngx.say("decrypted not match")
        return
    end
end

timer("decrypt")

for _ = 1, num do
    sig, err = priv:sign(str)
    if not sig then
        ngx.say("failed to sign:", err)
        return
    end
end

timer("sign")

for _ = 1, num do
    verify, err = pub:verify(str, sig)
    if not verify then
        ngx.say("verify err: ", err)
        return
    end
end

timer("verify")

发布步骤

  1. 更新 lib/resty/rsa.lua 中的 _VERSION
  2. 更新 dist.ini 中的 version
  3. 将当前 rockspec 重命名为新版本并更新其中的引用。
  4. 标记新版本
  5. opm 上传

参见

GitHub

您可以在 nginx-module-rsa 的 GitHub 仓库 中找到此模块的其他配置提示和文档。