├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .luacheckrc ├── LICENSE ├── Makefile ├── README.md ├── dist.ini ├── lib └── resty │ └── rsa.lua ├── lua-resty-rsa-1.0.2-1.rockspec ├── lua-resty-rsa-1.1.0-1.src.rock ├── lua-resty-rsa-1.1.0-2.src.rock ├── lua-resty-rsa-1.1.0-3.rockspec ├── lua-resty-rsa-1.1.0-3.src.rock ├── lua-resty-rsa-1.1.1-1.rockspec ├── t └── rsa.t ├── test_with_valgrind.sh └── valgrind.suppress /.gitattributes: -------------------------------------------------------------------------------- 1 | *.t linguist-language=Text 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | jobs: 8 | build: 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | platform: 13 | - ubuntu-latest 14 | runs-on: ${{ matrix.platform }} 15 | steps: 16 | - name: Check out 17 | uses: actions/checkout@v2 18 | with: 19 | submodules: recursive 20 | - name: Linux Get dependencies 21 | run: sudo apt install -y cpanminus luarocks valgrind 22 | 23 | - name: Linux Before install 24 | run: | 25 | sudo luarocks install luacheck 26 | luacheck -q . 27 | sudo cpanm --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1) 28 | 29 | - name: Linux Install 30 | run: | 31 | wget -qO - https://openresty.org/package/pubkey.gpg | sudo apt-key add - 32 | sudo apt-get -y install software-properties-common 33 | sudo add-apt-repository -y "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main" 34 | sudo apt-get update 35 | sudo apt-get install -y --no-install-recommends openresty 36 | 37 | - name: Linux Script 38 | run: | 39 | export PATH=$PATH:/usr/local/openresty/nginx/sbin 40 | export TEST_NGINX_BINARY=openresty 41 | openresty -V 42 | prove -r t 43 | bash test_with_valgrind.sh 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | t/servroot 2 | -------------------------------------------------------------------------------- /.luacheckrc: -------------------------------------------------------------------------------- 1 | std = 'ngx_lua' 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (C) 2014-2018, by Dejiang Zhu (doujiang24) 4 | Copyright (C) 2018-, by Zexuan Luo (spacewander) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OPENRESTY_PREFIX=/usr/local/openresty-debug 2 | 3 | PREFIX ?= /usr/local 4 | LUA_INCLUDE_DIR ?= $(PREFIX)/include 5 | LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION) 6 | INSTALL ?= install 7 | 8 | .PHONY: all test install 9 | 10 | all: ; 11 | 12 | install: all 13 | $(INSTALL) -d $(DESTDIR)/$(LUA_LIB_DIR)/resty 14 | $(INSTALL) lib/resty/*.lua $(DESTDIR)/$(LUA_LIB_DIR)/resty 15 | 16 | test: all 17 | PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r t 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Name 2 | ============= 3 | 4 | lua-resty-rsa - RSA functions for OpenResty 5 | 6 | Status 7 | ====== 8 | 9 | This library is considered production ready. 10 | 11 | Build status: ![https://github.com/spacewander/lua-resty-rsa/workflows/build/badge.svg?branch=master](https://github.com/spacewander/lua-resty-rsa/actions/workflows/ci.yml/badge.svg?branch=master) 12 | 13 | 14 | Description 15 | =========== 16 | 17 | This library requires an nginx build with OpenSSL, 18 | the [ngx_lua module](https://github.com/openresty/lua-nginx-module), and [LuaJIT](http://luajit.org/luajit.html). 19 | 20 | 21 | Synopsis 22 | ======== 23 | 24 | ```lua 25 | # nginx.conf: 26 | 27 | lua_package_path "/path/to/lua-resty-rsa/lib/?.lua;;"; 28 | 29 | server { 30 | location = /test { 31 | content_by_lua_file conf/test.lua; 32 | } 33 | } 34 | 35 | -- conf/test.lua: 36 | 37 | local resty_rsa = require "resty.rsa" 38 | local rsa_public_key, rsa_priv_key, err = resty_rsa:generate_rsa_keys(2048) 39 | if not rsa_public_key then 40 | ngx.say('generate rsa keys err: ', err) 41 | end 42 | 43 | ngx.say(rsa_public_key) 44 | --[[ 45 | -----BEGIN RSA PUBLIC KEY----- 46 | MIIBCgKCAQEAuw4T755fepEyXTM66pzf6nv8NtnukQTMGnhmBFIFHp/P2vEpxjXU 47 | BBDUpzKkVFR3wuK9O1FNmRDAGNGYC0N/9cZNdhykA1NixJfKQzncN31VJTmNqJNZ 48 | W0x7H9ZGoh2aE0zCCZpRlC1Rf5rL0SVlBoQkn/n9LnYFwyLLIK5/d/y/NZVL6Z6L 49 | cyvga0zRajamLIjY0Dy/8YIwVV6kaSsHeRv2cOB03eam6gbhLGIz/l8wuJhIn1rO 50 | yJLQ36IOJymbbNmcC7+2hEQJP40qLvH7hZ1LaAkgQUHjfi8RvH2T1Jmce7XGPxCo 51 | Ed0yfeFz+pL1KeSWNey6cL3N5hJZE8EntQIDAQAB 52 | -----END RSA PUBLIC KEY----- 53 | ]]-- 54 | 55 | ngx.say(rsa_priv_key) 56 | --[[ 57 | -----BEGIN RSA PRIVATE KEY----- 58 | MIIEpAIBAAKCAQEAuw4T755fepEyXTM66pzf6nv8NtnukQTMGnhmBFIFHp/P2vEp 59 | xjXUBBDUpzKkVFR3wuK9O1FNmRDAGNGYC0N/9cZNdhykA1NixJfKQzncN31VJTmN 60 | qJNZW0x7H9ZGoh2aE0zCCZpRlC1Rf5rL0SVlBoQkn/n9LnYFwyLLIK5/d/y/NZVL 61 | 6Z6Lcyvga0zRajamLIjY0Dy/8YIwVV6kaSsHeRv2cOB03eam6gbhLGIz/l8wuJhI 62 | n1rOyJLQ36IOJymbbNmcC7+2hEQJP40qLvH7hZ1LaAkgQUHjfi8RvH2T1Jmce7XG 63 | PxCoEd0yfeFz+pL1KeSWNey6cL3N5hJZE8EntQIDAQABAoIBAGim1ayIFK8EMQNH 64 | uDyui/Aqcc9WWky0PGTK23irUsXxb1708gQ89WNY70Cj6qBrqZ1VMb3QHPP4FSFN 65 | kh0rJJoi2g+ssm5R5r5KlhTKeFRrQInVC1Y3KhUUUwZa4aWtnhgSJ7Urq1yVhjU4 66 | K7PVkhH1OHBwcp/d1Bd6jd65AgPkY63P+WpcARJkClmQ1RhgoRwThyJdpKrV4/gO 67 | ha0AUGlJNRNvRwiZxP0zaI5C8RdrG96SnVpeYOcD0z/M1HVlkoYMXsXLKttwLfpK 68 | 88Igtm6ZJwRpfuMF5VA+9hHaYGCBdGz0B/rMp2fc+EtrOavYQGrWIWi2RL1Qk6Rt 69 | BUyeTgECgYEA9anj4n/cak1MT+hbNFsL31mJXryl1eVNjEZj/iPMztpdS15CmFgj 70 | Kjr9UuintjSiK7Is43nZUWWyP1XQjRhVi2uP7PRIv92QNl/YteWD6tYCInJHKe2J 71 | QqYyZrElezsdayXb5DK6bi1UIYYji90g79N7x6pOR0UnQNQUXTv+Y8ECgYEAwuzl 72 | 6Ez4BSXIIL9NK41jfNMa73Utfl5oO1f6mHM2KbILqaFE76PSgEeXDbOKdcjCbbqC 73 | KCGjwyPd+Clehg4vkYXTq1y2SQGHwfz7DilPSOxhPY9ND7lGbeNzDUK4x8xe52hd 74 | MWKdgqeqCK83e5D0ihzRiMah8dbxmlfLAOZ3sPUCgYEA0dT9Czg/YqUHq7FCReQG 75 | rg3iYgMsexjTNh/hxO97PqwRyBCJPWr7DlU4j5qdteobIsubv+kSEI6Ww7Ze3kWM 76 | u/tyAeleQlPTnD4d8rBKD0ogpJ+L3WpBNaaToldpNmr149GAktgpmXYqSEA1GIAW 77 | ZAL11UPIfOO6dYswobpevYECgYEApSosSODnCx2PbMgL8IpWMU+DNEF6sef2s8oB 78 | aam9zCi0HyCqE9AhLlb61D48ZT8eF/IAFVcjttauX3dWQ4rDna/iwgHF5yhnyuS8 79 | KayxJJ4+avYAmwEnfzdJpoPRpGI0TCovRQhFZI8C0Wb+QTJ7Mofmt9lvIUc64sff 80 | GD0wT/0CgYASMf708dmc5Bpzcis++EgMJVb0q+ORmWzSai1NB4bf3LsNS6suWNNU 81 | zj/JGtMaGvQo5vzGU4exNkhpQo8yUU5YbHlA8RCj7SYkmP78kCewEqxlx7dbcuj2 82 | LAPWpiDca8StTfEphoKEVfCPHaUk0MlBHR4lCrnAkEtz23vhZKWhFw== 83 | -----END RSA PRIVATE KEY----- 84 | ]]-- 85 | 86 | local pub, err = resty_rsa:new({ public_key = rsa_public_key }) 87 | if not pub then 88 | ngx.say("new rsa err: ", err) 89 | return 90 | end 91 | local encrypted, err = pub:encrypt("hello") 92 | if not encrypted then 93 | ngx.say("failed to encrypt: ", err) 94 | return 95 | end 96 | ngx.say("encrypted length: ", #encrypted) 97 | 98 | local priv, err = resty_rsa:new({ private_key = rsa_priv_key }) 99 | if not priv then 100 | ngx.say("new rsa err: ", err) 101 | return 102 | end 103 | local decrypted = priv:decrypt(encrypted) 104 | ngx.say(decrypted == "hello") 105 | 106 | local algorithm = "SHA256" 107 | local priv, err = resty_rsa:new({ private_key = rsa_priv_key, algorithm = algorithm }) 108 | if not priv then 109 | ngx.say("new rsa err: ", err) 110 | return 111 | end 112 | 113 | local str = "hello" 114 | local sig, err = priv:sign(str) 115 | if not sig then 116 | ngx.say("failed to sign:", err) 117 | return 118 | end 119 | ngx.say("sig length: ", #sig) 120 | 121 | local pub, err = resty_rsa:new({ public_key = rsa_public_key, algorithm = algorithm }) 122 | if not pub then 123 | ngx.say("new rsa err: ", err) 124 | return 125 | end 126 | local verify, err = pub:verify(str, sig) 127 | if not verify then 128 | ngx.say("verify err: ", err) 129 | return 130 | end 131 | ngx.say(verify) 132 | ``` 133 | 134 | 135 | Methods 136 | ======= 137 | 138 | To load this library, 139 | 140 | 1. you need to specify this library's path in ngx_lua's [lua_package_path](https://github.com/openresty/lua-nginx-module#lua_package_path) directive. For example, `lua_package_path "/path/to/lua-resty-rsa/lib/?.lua;;";`. 141 | 2. you use `require` to load the library into a local Lua variable: 142 | 143 | ```lua 144 | local rsa = require "resty.rsa" 145 | ``` 146 | 147 | generate_rsa_keys 148 | --- 149 | `syntax: public_key, private_key, err = rsa:generate_rsa_keys(bits, in_pkcs8_fmt)` 150 | 151 | Generate rsa public key and private key by specifying the number of `bits`. 152 | The `in_pkcs8_fmt` is optional. If `in_pkcs8_fmt` is true, the generated priviate key is in PKCS#8 format and 153 | the public key is in PKIX format, which start with `-----BEGIN PUBLIC` or `-----BEGIN PRIVATE`. 154 | Otherwise the generated keys are in PKCS#1 format, which start with `-----BEGIN RSA`. 155 | 156 | new 157 | --- 158 | `syntax: obj, err = rsa:new(opts)` 159 | 160 | Creates a new rsa object instance by specifying an options table `opts`. 161 | 162 | The options table accepts the following options: 163 | 164 | * `public_key` 165 | Specifies the public rsa key. 166 | * `private_key` 167 | Specifies the private rsa key. 168 | * `password` 169 | Specifies the password to read rsa key. 170 | * `key_type` 171 | Specifies the type of given key. 172 | By default the type will be detected from the value of the key. 173 | 174 | | `key_type` value | meaning | 175 | | ------------------- | ------ | 176 | | rsa.KEY_TYPE.PKCS1 | The input key is in PKCS#1 format(usually starts with `-----BEGIN RSA PUBLIC`). | 177 | | rsa.KEY_TYPE.PKIX | The input key is in PKIX format(usually starts with `-----BEGIN PUBLIC`). | 178 | 179 | ```lua 180 | -- creates a rsa object with PKIX format of public key 181 | local resty_rsa = require "resty.rsa" 182 | local pub, err = resty_rsa:new({ 183 | public_key = RSA_PKCS8_PUB_KEY, 184 | key_type = resty_rsa.KEY_TYPE.PKIX, 185 | }) 186 | 187 | -- creates a rsa object with pkcs#8 format of private key 188 | local priv, err = resty_rsa:new({ 189 | private_key = RSA_PKCS8_PASS_PRIV_KEY, 190 | key_type = resty_rsa.KEY_TYPE.PKCS8, 191 | -- you need to specify the password if the pkey is encrypted 192 | -- password = "foobar", 193 | }) 194 | ``` 195 | 196 | * `padding` 197 | Specifies the padding mode when you want to encrypt/decrypt. 198 | * `algorithm` 199 | Specifies the digest algorithm when you want to sign/verify. 200 | 201 | | `algorithm` value | meaning | 202 | | ------------------- | ------ | 203 | | md4/MD4/RSA-MD4/md4WithRSAEncryption | digest with `md4` | 204 | | md5/MD5/RSA-MD5/md5WithRSAEncryption/ssl3-md5 | digest with `md5` | 205 | | ripemd160/RIPEMD160/RSA-RIPEM160/ripemd160WithRSA/rmd160 | digest with `ripemd160` | 206 | | sha1/SHA1/RSA-SHA1/sha1WithRSAEncryption/ssl3-sha1 | digest with `sha1` | 207 | | sha224/SHA224/RSA-SHA224/sha224WithRSAEncryption | digest with `sha224` | 208 | | sha256/SHA256/RSA-SHA256/sha256WithRSAEncryption | digest with `sha256` | 209 | | sha384/SHA384/RSA-SHA384/sha384WithRSAEncryption | digest with `sha384` | 210 | | sha512/SHA512/RSA-SHA512/sha512WithRSAEncryption | digest with `sha512` | 211 | 212 | encrypt 213 | ---- 214 | `syntax: encrypted, err = obj:encrypt(str)` 215 | 216 | decrypt 217 | ------ 218 | `syntax: decrypted, err = obj:decrypt(encrypted)` 219 | 220 | 221 | sign 222 | ---- 223 | `syntax: signature, err = obj:sign(str)` 224 | 225 | verify 226 | ------ 227 | `syntax: ok, err = obj:verify(str, signature)` 228 | 229 | 230 | Performance 231 | ======== 232 | 233 | I got the result: 234 | ``` 235 | encrypt for 50000 times cost : 2.4110000133514s 236 | decrypt for 50000 times cost : 57.196000099182s 237 | sign for 50000 times cost : 59.169999837875s 238 | verify for 50000 times cost : 1.8230001926422s 239 | ``` 240 | 241 | when I run this script. 242 | ``` 243 | local resty_rsa = require "resty.rsa" 244 | local algorithm = "SHA256" 245 | 246 | local rsa_public_key, rsa_priv_key, err = resty_rsa:generate_rsa_keys(2048) 247 | if not rsa_public_key then 248 | ngx.say("generate rsa keys err: ", err) 249 | return 250 | end 251 | 252 | local pub, err = resty_rsa:new({ 253 | public_key = rsa_public_key, 254 | padding = resty_rsa.PADDING.RSA_PKCS1_PADDING, 255 | algorithm = algorithm, 256 | }) 257 | if not pub then 258 | ngx.say("new rsa err: ", err) 259 | return 260 | end 261 | 262 | local priv, err = resty_rsa:new({ 263 | private_key = rsa_priv_key, 264 | padding = resty_rsa.PADDING.RSA_PKCS1_PADDING, 265 | algorithm = algorithm, 266 | }) 267 | if not priv then 268 | ngx.say("new rsa err: ", err) 269 | return 270 | end 271 | 272 | 273 | local num = 5 * 10000 274 | 275 | local str = "hello test" 276 | 277 | local encrypted, decrypted, err, sig, verify 278 | 279 | ngx.update_time() 280 | local now = ngx.now() 281 | 282 | local function timer(operation) 283 | ngx.update_time() 284 | local t = ngx.now() 285 | 286 | ngx.say(operation, " for ", num, " times cost : ", t - now, "s") 287 | now = t 288 | end 289 | 290 | for _ = 1, num do 291 | encrypted, err = pub:encrypt(str) 292 | if not encrypted then 293 | ngx.say("failed to encrypt: ", err) 294 | return 295 | end 296 | end 297 | 298 | timer("encrypt") 299 | 300 | for _ = 1, num do 301 | decrypted = priv:decrypt(encrypted) 302 | if decrypted ~= str then 303 | ngx.say("decrypted not match") 304 | return 305 | end 306 | end 307 | 308 | timer("decrypt") 309 | 310 | for _ = 1, num do 311 | sig, err = priv:sign(str) 312 | if not sig then 313 | ngx.say("failed to sign:", err) 314 | return 315 | end 316 | end 317 | 318 | timer("sign") 319 | 320 | for _ = 1, num do 321 | verify, err = pub:verify(str, sig) 322 | if not verify then 323 | ngx.say("verify err: ", err) 324 | return 325 | end 326 | end 327 | 328 | timer("verify") 329 | ``` 330 | 331 | 332 | Author 333 | ====== 334 | 335 | Dejiang Zhu (doujiang24) 336 | 337 | Zexuan Luo (spacewander) 338 | 339 | Release Steps 340 | ============= 341 | 342 | 1. update the `_VERSION` in `lib/resty/rsa.lua` 343 | 2. update the `version` in `dist.ini` 344 | 3. rename current rockspec to the new version and update the reference in it. 345 | 4. tag the new version 346 | 5. opm upload 347 | 348 | Copyright and License 349 | ===================== 350 | 351 | This module is licensed under the MIT license. 352 | 353 | Copyright (C) 2014-2018, by Dejiang Zhu (doujiang24) 354 | Copyright (C) 2018-, by Zexuan Luo (spacewander) 355 | 356 | All rights reserved. 357 | 358 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 359 | 360 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 361 | 362 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 363 | 364 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 365 | 366 | See Also 367 | ======== 368 | * the ngx_lua module: http://wiki.nginx.org/HttpLuaModule 369 | * the lua-resty-string: https://github.com/openresty/lua-resty-string 370 | -------------------------------------------------------------------------------- /dist.ini: -------------------------------------------------------------------------------- 1 | name=lua-resty-rsa 2 | abstract=RSA encrypt/decrypt & sign/verify for OpenResty/LuaJIT 3 | author=spacewander 4 | is_original=yes 5 | license=mit 6 | lib_dir=lib 7 | repo_link=https://github.com/spacewander/lua-resty-rsa 8 | version=1.1.0 9 | -------------------------------------------------------------------------------- /lib/resty/rsa.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) by Zhu Dejiang (doujiang24) 2 | -- Copyright (C) by Zexuan Luo (spacewander) 3 | 4 | 5 | local bit = require "bit" 6 | local band = bit.band 7 | local ffi = require "ffi" 8 | local ffi_new = ffi.new 9 | local ffi_gc = ffi.gc 10 | local ffi_copy = ffi.copy 11 | local ffi_str = ffi.string 12 | local C = ffi.C 13 | local tab_concat = table.concat 14 | local setmetatable = setmetatable 15 | 16 | local _M = { _VERSION = '1.1.0' } 17 | 18 | local mt = { __index = _M } 19 | 20 | 21 | local PADDING = { 22 | RSA_PKCS1_PADDING = 1, 23 | RSA_NO_PADDING = 3, 24 | RSA_PKCS1_OAEP_PADDING = 4, 25 | } 26 | _M.PADDING = PADDING 27 | 28 | local KEY_TYPE = { 29 | PKCS1 = "PKCS#1", 30 | PKCS8 = "PKCS#8", 31 | PKIX = "PKIX", 32 | } 33 | local PKCS1_PREFIX = "-----BEGIN RSA " 34 | _M.KEY_TYPE = KEY_TYPE 35 | 36 | 37 | ffi.cdef[[ 38 | int memcmp(const void *s1, const void *s2, size_t n); 39 | 40 | typedef struct bio_st BIO; 41 | typedef struct bio_method_st BIO_METHOD; 42 | BIO_METHOD *BIO_s_mem(void); 43 | BIO * BIO_new(BIO_METHOD *type); 44 | int BIO_puts(BIO *bp, const char *buf); 45 | void BIO_vfree(BIO *a); 46 | 47 | typedef struct rsa_st RSA; 48 | RSA *RSA_new(void); 49 | void RSA_free(RSA *rsa); 50 | typedef int pem_password_cb(char *buf, int size, int rwflag, void *userdata); 51 | RSA * PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **rsa, pem_password_cb *cb, 52 | void *u); 53 | RSA * PEM_read_bio_RSAPublicKey(BIO *bp, RSA **rsa, pem_password_cb *cb, 54 | void *u); 55 | RSA * PEM_read_bio_RSA_PUBKEY(BIO *bp, RSA **rsa, pem_password_cb *cb, 56 | void *u); 57 | 58 | unsigned long ERR_get_error_line_data(const char **file, int *line, 59 | const char **data, int *flags); 60 | const char * ERR_reason_error_string(unsigned long e); 61 | 62 | typedef struct bignum_st BIGNUM; 63 | BIGNUM *BN_new(void); 64 | void BN_free(BIGNUM *a); 65 | typedef unsigned long BN_ULONG; 66 | int BN_set_word(BIGNUM *a, BN_ULONG w); 67 | typedef struct bn_gencb_st BN_GENCB; 68 | int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb); 69 | 70 | typedef struct evp_cipher_st EVP_CIPHER; 71 | int PEM_write_bio_RSAPrivateKey(BIO *bp, RSA *x, const EVP_CIPHER *enc, 72 | unsigned char *kstr, int klen, 73 | pem_password_cb *cb, void *u); 74 | int PEM_write_bio_RSAPublicKey(BIO *bp, RSA *x); 75 | int PEM_write_bio_RSA_PUBKEY(BIO *bp, RSA *x); 76 | 77 | long BIO_ctrl(BIO *bp, int cmd, long larg, void *parg); 78 | int BIO_read(BIO *b, void *data, int len); 79 | 80 | typedef struct evp_pkey_st EVP_PKEY; 81 | typedef struct engine_st ENGINE; 82 | typedef struct evp_pkey_ctx_st EVP_PKEY_CTX; 83 | 84 | EVP_PKEY *EVP_PKEY_new(void); 85 | void EVP_PKEY_free(EVP_PKEY *key); 86 | 87 | EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e); 88 | void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx); 89 | 90 | int EVP_PKEY_CTX_ctrl(EVP_PKEY_CTX *ctx, int keytype, int optype, 91 | int cmd, int p1, void *p2); 92 | 93 | int EVP_PKEY_size(EVP_PKEY *pkey); 94 | int EVP_PKEY_get_size(EVP_PKEY *pkey); 95 | 96 | int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *ctx); 97 | int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, 98 | unsigned char *out, size_t *outlen, 99 | const unsigned char *in, size_t inlen); 100 | 101 | int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *ctx); 102 | int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, 103 | unsigned char *out, size_t *outlen, 104 | const unsigned char *in, size_t inlen); 105 | 106 | int EVP_PKEY_set1_RSA(EVP_PKEY *pkey, RSA *key); 107 | int PEM_write_bio_PKCS8PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc, 108 | char *kstr, int klen, pem_password_cb *cb, 109 | void *u); 110 | 111 | void OpenSSL_add_all_digests(void); 112 | typedef struct env_md_st EVP_MD; 113 | typedef struct env_md_ctx_st EVP_MD_CTX; 114 | const EVP_MD *EVP_get_digestbyname(const char *name); 115 | 116 | /* EVP_MD_CTX methods for OpenSSL < 1.1.0 */ 117 | EVP_MD_CTX *EVP_MD_CTX_create(void); 118 | void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx); 119 | 120 | /* EVP_MD_CTX methods for OpenSSL >= 1.1.0 */ 121 | EVP_MD_CTX *EVP_MD_CTX_new(void); 122 | void EVP_MD_CTX_free(EVP_MD_CTX *ctx); 123 | 124 | int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type); 125 | int EVP_DigestUpdate(EVP_MD_CTX *ctx, const unsigned char *in, int inl); 126 | int EVP_SignFinal(EVP_MD_CTX *ctx,unsigned char *sig,unsigned int *s, 127 | EVP_PKEY *pkey); 128 | int EVP_VerifyFinal(EVP_MD_CTX *ctx,unsigned char *sigbuf, unsigned int siglen, 129 | EVP_PKEY *pkey); 130 | int EVP_PKEY_set1_RSA(EVP_PKEY *e, RSA *r); 131 | 132 | void ERR_set_error_data(char *data, int flags); 133 | ]] 134 | --[[ 135 | # define EVP_PKEY_CTX_set_rsa_padding(ctx, pad) \ 136 | EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, -1, EVP_PKEY_CTRL_RSA_PADDING, \ 137 | pad, NULL) 138 | # define EVP_SignInit(a,b) EVP_DigestInit(a,b) 139 | # define EVP_SignUpdate(a,b,c) EVP_DigestUpdate(a,b,c) 140 | --]] 141 | 142 | 143 | local EVP_PKEY_ALG_CTRL = 0x1000 144 | local EVP_PKEY_CTRL_RSA_PADDING = EVP_PKEY_ALG_CTRL + 1 145 | local NID_rsaEncryption = 6 146 | local EVP_PKEY_RSA = NID_rsaEncryption 147 | local ERR_TXT_STRING = 0x02 148 | 149 | local evp_md_ctx_new 150 | local evp_md_ctx_free 151 | if not pcall(function () return C.EVP_MD_CTX_create end) then 152 | evp_md_ctx_new = C.EVP_MD_CTX_new 153 | evp_md_ctx_free = C.EVP_MD_CTX_free 154 | else 155 | evp_md_ctx_new = C.EVP_MD_CTX_create 156 | evp_md_ctx_free = C.EVP_MD_CTX_destroy 157 | end 158 | 159 | local evp_pkey_size 160 | if not pcall(function () return C.EVP_PKEY_size end) then 161 | evp_pkey_size = C.EVP_PKEY_get_size 162 | else 163 | evp_pkey_size = C.EVP_PKEY_size 164 | end 165 | 166 | local function ssl_err() 167 | local err_queue = {} 168 | local i = 1 169 | local data = ffi_new("const char*[1]") 170 | local flags = ffi_new("int[1]") 171 | 172 | while true do 173 | local code = C.ERR_get_error_line_data(nil, nil, data, flags) 174 | if code == 0 then 175 | break 176 | end 177 | 178 | local err = C.ERR_reason_error_string(code) 179 | if err == nil then 180 | err_queue[i] = "unknown openssl error code: " .. code 181 | i = i + 1 182 | 183 | else 184 | err_queue[i] = ffi_str(err) 185 | i = i + 1 186 | 187 | if data[0] ~= nil and band(flags[0], ERR_TXT_STRING) > 0 then 188 | err_queue[i] = ffi_str(data[0]) 189 | i = i + 1 190 | end 191 | end 192 | end 193 | 194 | if #err_queue == 0 then 195 | return nil, "internal error" 196 | end 197 | return nil, tab_concat(err_queue, ": ", 1, i - 1) 198 | end 199 | 200 | local function has_prefix(s, prefix) 201 | if type(s) ~= "string" or type(prefix) ~= "string" then 202 | return false 203 | end 204 | if #s < #prefix then 205 | return false 206 | end 207 | local rc = C.memcmp(s, prefix, #prefix) 208 | return rc == 0 209 | end 210 | 211 | local function read_bio(bio) 212 | local BIO_CTRL_PENDING = 10 213 | local keylen = C.BIO_ctrl(bio, BIO_CTRL_PENDING, 0, nil); 214 | local key = ffi.new("char[?]", keylen) 215 | if C.BIO_read(bio, key, keylen) < 0 then 216 | return ssl_err() 217 | end 218 | return ffi_str(key, keylen) 219 | end 220 | 221 | -- Follow the calling style to avoid careless mistake. 222 | function _M.generate_rsa_keys(_, bits, pkcs8) 223 | local rsa = C.RSA_new() 224 | if rsa == nil then 225 | return nil, ssl_err() 226 | end 227 | ffi_gc(rsa, C.RSA_free) 228 | 229 | local bn = C.BN_new() 230 | if bn == nil then 231 | return nil, ssl_err() 232 | end 233 | ffi_gc(bn, C.BN_free) 234 | 235 | -- Set public exponent to 65537 236 | if C.BN_set_word(bn, 65537) ~= 1 then 237 | return nil, ssl_err() 238 | end 239 | 240 | -- Generate key 241 | if C.RSA_generate_key_ex(rsa, bits, bn, nil) ~= 1 then 242 | return nil, ssl_err() 243 | end 244 | 245 | local pub_key_bio = C.BIO_new(C.BIO_s_mem()) 246 | if pub_key_bio == nil then 247 | return nil, ssl_err() 248 | end 249 | ffi_gc(pub_key_bio, C.BIO_vfree) 250 | 251 | if pkcs8 == true then 252 | if C.PEM_write_bio_RSA_PUBKEY(pub_key_bio, rsa) ~= 1 then 253 | return nil, ssl_err() 254 | end 255 | else 256 | if C.PEM_write_bio_RSAPublicKey(pub_key_bio, rsa) ~= 1 then 257 | return nil, ssl_err() 258 | end 259 | end 260 | 261 | local public_key, err = read_bio(pub_key_bio) 262 | if not public_key then 263 | return nil, nil, err 264 | end 265 | 266 | local priv_key_bio = C.BIO_new(C.BIO_s_mem()) 267 | if priv_key_bio == nil then 268 | return nil, ssl_err() 269 | end 270 | ffi_gc(priv_key_bio, C.BIO_vfree) 271 | 272 | if pkcs8 == true then 273 | local pk = C.EVP_PKEY_new() 274 | if pk == nil then 275 | return nil, ssl_err() 276 | end 277 | ffi_gc(pk, C.EVP_PKEY_free) 278 | 279 | if C.EVP_PKEY_set1_RSA(pk,rsa) ~= 1 then 280 | return nil, ssl_err() 281 | end 282 | if C.PEM_write_bio_PKCS8PrivateKey(priv_key_bio, pk, 283 | nil, nil, 0, nil, nil) ~= 1 then 284 | return nil, ssl_err() 285 | end 286 | else 287 | if C.PEM_write_bio_RSAPrivateKey(priv_key_bio, rsa, 288 | nil, nil, 0, nil, nil) ~= 1 then 289 | return nil, ssl_err() 290 | end 291 | end 292 | 293 | local private_key 294 | private_key, err = read_bio(priv_key_bio) 295 | if not private_key then 296 | return nil, nil, err 297 | end 298 | 299 | return public_key, private_key 300 | end 301 | 302 | function _M.new(_, opts) 303 | local key, read_func, is_pub, md 304 | 305 | if opts.public_key then 306 | key = opts.public_key 307 | if opts.key_type == KEY_TYPE.PKIX or 308 | opts.key_type == KEY_TYPE.PKCS8 -- for compatible 309 | then 310 | read_func = C.PEM_read_bio_RSA_PUBKEY 311 | elseif opts.key_type == KEY_TYPE.PKCS1 then 312 | read_func = C.PEM_read_bio_RSAPublicKey 313 | else 314 | local is_pkcs1 = has_prefix(key, PKCS1_PREFIX) 315 | if is_pkcs1 then 316 | read_func = C.PEM_read_bio_RSAPublicKey 317 | else 318 | read_func = C.PEM_read_bio_RSA_PUBKEY 319 | end 320 | end 321 | is_pub = true 322 | 323 | elseif opts.private_key then 324 | key = opts.private_key 325 | read_func = C.PEM_read_bio_RSAPrivateKey 326 | 327 | else 328 | return nil, "public_key or private_key not found" 329 | end 330 | 331 | local bio_method = C.BIO_s_mem() 332 | local bio = C.BIO_new(bio_method) 333 | if bio == nil then 334 | return ssl_err() 335 | end 336 | ffi_gc(bio, C.BIO_vfree) 337 | 338 | local len = C.BIO_puts(bio, key) 339 | if len < 0 then 340 | return ssl_err() 341 | end 342 | 343 | local pass 344 | if opts.password then 345 | local plen = #opts.password 346 | pass = ffi_new("unsigned char[?]", plen + 1) 347 | ffi_copy(pass, opts.password, plen) 348 | end 349 | 350 | local rsa = read_func(bio, nil, nil, pass) 351 | if rsa == nil then 352 | return ssl_err() 353 | end 354 | ffi_gc(rsa, C.RSA_free) 355 | 356 | -- EVP_PKEY 357 | local pkey = C.EVP_PKEY_new() 358 | if pkey == nil then 359 | return ssl_err() 360 | end 361 | ffi_gc(pkey, C.EVP_PKEY_free) 362 | 363 | if C.EVP_PKEY_set1_RSA(pkey, rsa) == 0 then 364 | return ssl_err() 365 | end 366 | 367 | --EVP_PKEY_CTX 368 | local ctx = C.EVP_PKEY_CTX_new(pkey, nil) 369 | if ctx == nil then 370 | return ssl_err() 371 | end 372 | ffi_gc(ctx, C.EVP_PKEY_CTX_free) 373 | 374 | -- md_ctx init for sign or verify; if signature algorithm is seted 375 | if opts.algorithm then 376 | md = C.EVP_get_digestbyname(opts.algorithm) 377 | if md == nil then 378 | return nil, "Unknown message digest" 379 | end 380 | 381 | end 382 | 383 | -- ctx init for encrypt or decrypt 384 | -- default for encrypt/decrypt if nothing is set 385 | if opts.padding or not opts.digest then 386 | local init_func = is_pub and C.EVP_PKEY_encrypt_init 387 | or C.EVP_PKEY_decrypt_init 388 | if init_func(ctx) <= 0 then 389 | return ssl_err() 390 | end 391 | 392 | if C.EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, -1, EVP_PKEY_CTRL_RSA_PADDING, 393 | opts.padding or PADDING.RSA_PKCS1_PADDING, nil) <= 0 then 394 | return ssl_err() 395 | end 396 | end 397 | 398 | local size = evp_pkey_size(pkey) 399 | return setmetatable({ 400 | pkey = pkey, 401 | size = size, 402 | buf = ffi_new("unsigned char[?]", size), 403 | _encrypt_ctx = is_pub and ctx or nil, 404 | _decrypt_ctx = not is_pub and ctx or nil, 405 | is_pub = is_pub, 406 | md = md, 407 | }, mt) 408 | end 409 | 410 | 411 | function _M.decrypt(self, str) 412 | local ctx = self._decrypt_ctx 413 | if not ctx then 414 | return nil, "not inited for decrypt" 415 | end 416 | 417 | local len = ffi_new("size_t [1]") 418 | if C.EVP_PKEY_decrypt(ctx, nil, len, str, #str) <= 0 then 419 | return ssl_err() 420 | end 421 | 422 | local buf = self.buf 423 | if C.EVP_PKEY_decrypt(ctx, buf, len, str, #str) <= 0 then 424 | return ssl_err() 425 | end 426 | 427 | return ffi_str(buf, len[0]) 428 | end 429 | 430 | 431 | function _M.encrypt(self, str) 432 | local ctx = self._encrypt_ctx 433 | if not ctx then 434 | return nil, "not inited for encrypt" 435 | end 436 | 437 | local len = ffi_new("size_t [1]") 438 | if C.EVP_PKEY_encrypt(ctx, nil, len, str, #str) <= 0 then 439 | return ssl_err() 440 | end 441 | 442 | local buf = self.buf 443 | if C.EVP_PKEY_encrypt(ctx, buf, len, str, #str) <= 0 then 444 | return ssl_err() 445 | end 446 | 447 | return ffi_str(buf, len[0]) 448 | end 449 | 450 | 451 | function _M.sign(self, str) 452 | if self.is_pub then 453 | return nil, "not inited for sign" 454 | end 455 | 456 | local md_ctx = evp_md_ctx_new() 457 | if md_ctx == nil then 458 | return ssl_err() 459 | end 460 | ffi_gc(md_ctx, evp_md_ctx_free) 461 | 462 | if C.EVP_DigestInit(md_ctx, self.md) <= 0 then 463 | return ssl_err() 464 | end 465 | 466 | if C.EVP_DigestUpdate(md_ctx, str, #str) <= 0 then 467 | return ssl_err() 468 | end 469 | 470 | local buf = self.buf 471 | local len = ffi_new("unsigned int[1]") 472 | if C.EVP_SignFinal(md_ctx, self.buf, len, self.pkey) <= 0 then 473 | return ssl_err() 474 | end 475 | 476 | return ffi_str(buf, len[0]) 477 | end 478 | 479 | 480 | function _M.verify(self, str, sig) 481 | if not self.is_pub then 482 | return nil, "not inited for verify" 483 | end 484 | 485 | local md_ctx = evp_md_ctx_new() 486 | if md_ctx == nil then 487 | return ssl_err() 488 | end 489 | ffi_gc(md_ctx, evp_md_ctx_free) 490 | 491 | if C.EVP_DigestInit(md_ctx, self.md) <= 0 then 492 | return ssl_err() 493 | end 494 | 495 | if C.EVP_DigestUpdate(md_ctx, str, #str) <= 0 then 496 | return ssl_err() 497 | end 498 | 499 | local siglen = #sig 500 | local buf = siglen <= self.size and self.buf 501 | or ffi_new("unsigned char[?]", siglen) 502 | ffi_copy(buf, sig, siglen) 503 | if C.EVP_VerifyFinal(md_ctx, buf, siglen, self.pkey) <= 0 then 504 | return ssl_err() 505 | end 506 | 507 | return true 508 | end 509 | 510 | 511 | return _M 512 | -------------------------------------------------------------------------------- /lua-resty-rsa-1.0.2-1.rockspec: -------------------------------------------------------------------------------- 1 | package = 'lua-resty-rsa' 2 | version = '1.0.2-1' 3 | source = { 4 | url = 'git://github.com/spacewander/lua-resty-rsa.git', 5 | tag = 'v1.0.2' 6 | } 7 | description = { 8 | summary = 'RSA encrypt/decrypt & sign/verify for OpenResty/LuaJIT.', 9 | detailed = [[ 10 | This library requires an nginx build 11 | with OpenSSL, the ngx_lua module, 12 | the LuaJIT 2.0. 13 | ]], 14 | homepage = 'https://github.com/spacewander/lua-resty-rsa', 15 | license = 'MIT' 16 | } 17 | dependencies = { 18 | 'lua >= 5.1' 19 | } 20 | build = { 21 | type = 'builtin', 22 | modules = { 23 | ['resty.rsa'] = 'lib/resty/rsa.lua' 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lua-resty-rsa-1.1.0-1.src.rock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacewander/lua-resty-rsa/49ed1f112cbdcac7e0521b2798ef78c469f3555c/lua-resty-rsa-1.1.0-1.src.rock -------------------------------------------------------------------------------- /lua-resty-rsa-1.1.0-2.src.rock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacewander/lua-resty-rsa/49ed1f112cbdcac7e0521b2798ef78c469f3555c/lua-resty-rsa-1.1.0-2.src.rock -------------------------------------------------------------------------------- /lua-resty-rsa-1.1.0-3.rockspec: -------------------------------------------------------------------------------- 1 | package = 'lua-resty-rsa' 2 | version = '1.1.0-3' 3 | source = { 4 | url = 'git+https://github.com/spacewander/lua-resty-rsa.git', 5 | tag = 'v1.1.0' 6 | } 7 | description = { 8 | summary = 'RSA encrypt/decrypt & sign/verify for OpenResty/LuaJIT.', 9 | detailed = [[ 10 | This library requires an nginx build 11 | with OpenSSL, the ngx_lua module, 12 | the LuaJIT 2.1. 13 | ]], 14 | homepage = 'https://github.com/spacewander/lua-resty-rsa', 15 | license = 'MIT' 16 | } 17 | dependencies = { 18 | 'lua >= 5.1' 19 | } 20 | build = { 21 | type = 'builtin', 22 | modules = { 23 | ['resty.rsa'] = 'lib/resty/rsa.lua' 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lua-resty-rsa-1.1.0-3.src.rock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacewander/lua-resty-rsa/49ed1f112cbdcac7e0521b2798ef78c469f3555c/lua-resty-rsa-1.1.0-3.src.rock -------------------------------------------------------------------------------- /lua-resty-rsa-1.1.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = 'lua-resty-rsa' 2 | version = '1.1.1-1' 3 | source = { 4 | url = 'git+https://github.com/spacewander/lua-resty-rsa.git', 5 | tag = 'v1.1.1' 6 | } 7 | description = { 8 | summary = 'RSA encrypt/decrypt & sign/verify for OpenResty/LuaJIT.', 9 | detailed = [[ 10 | This library requires an nginx build 11 | with OpenSSL, the ngx_lua module, 12 | the LuaJIT 2.1. 13 | ]], 14 | homepage = 'https://github.com/spacewander/lua-resty-rsa', 15 | license = 'MIT' 16 | } 17 | dependencies = { 18 | 'lua >= 5.1' 19 | } 20 | build = { 21 | type = 'builtin', 22 | modules = { 23 | ['resty.rsa'] = 'lib/resty/rsa.lua' 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /t/rsa.t: -------------------------------------------------------------------------------- 1 | # vi:ft= 2 | 3 | use Test::Nginx::Socket::Lua; 4 | 5 | repeat_each(2); 6 | no_long_string(); 7 | 8 | plan tests => repeat_each() * (3 * blocks()); 9 | 10 | our $HttpConfig = <<'_EOC_'; 11 | lua_package_path 'lib/?.lua;;'; 12 | lua_package_cpath 'lib/?.so;;'; 13 | _EOC_ 14 | 15 | $ENV{TEST_NGINX_SHORT_PK_CONF} = ' 16 | local RSA_PUBLIC_KEY = [[ 17 | -----BEGIN RSA PUBLIC KEY----- 18 | MIGJAoGBAJ9YqFCTlhnmTYNCezMfy7yb7xwAzRinXup1Zl51517rhJq8W0wVwNt+ 19 | mcKwRzisA1SIqPGlhiyDb2RJKc1cCNrVNfj7xxOKCIihkIsTIKXzDfeAqrm0bU80 20 | BSjgjj6YUKZinUAACPoao8v+QFoRlXlsAy72mY7ipVnJqBd1AOPVAgMBAAE= 21 | -----END RSA PUBLIC KEY----- 22 | ]] 23 | local RSA_PRIV_KEY = [[ 24 | -----BEGIN RSA PRIVATE KEY----- 25 | MIICXAIBAAKBgQCfWKhQk5YZ5k2DQnszH8u8m+8cAM0Yp17qdWZedede64SavFtM 26 | FcDbfpnCsEc4rANUiKjxpYYsg29kSSnNXAja1TX4+8cTigiIoZCLEyCl8w33gKq5 27 | tG1PNAUo4I4+mFCmYp1AAAj6GqPL/kBaEZV5bAMu9pmO4qVZyagXdQDj1QIDAQAB 28 | AoGBAJega3lRFvHKPlP6vPTm+p2c3CiPcppVGXKNCD42f1XJUsNTHKUHxh6XF4U0 29 | 7HC27exQpkJbOZO99g89t3NccmcZPOCCz4aN0LcKv9oVZQz3Avz6aYreSESwLPqy 30 | AgmJEvuVe/cdwkhjAvIcbwc4rnI3OBRHXmy2h3SmO0Gkx3D5AkEAyvTrrBxDCQeW 31 | S4oI2pnalHyLi1apDI/Wn76oNKW/dQ36SPcqMLTzGmdfxViUhh19ySV5id8AddbE 32 | /b72yQLCuwJBAMj97VFPInOwm2SaWm3tw60fbJOXxuWLC6ltEfqAMFcv94ZT/Vpg 33 | nv93jkF9DLQC/CWHbjZbvtYTlzpevxYL8q8CQHiAKHkcopR2475f61fXJ1coBzYo 34 | suAZesWHzpjLnDwkm2i9D1ix5vDTVaJ3MF/cnLVTwbChLcXJSVabDi1UrUcCQAmn 35 | iNq6/mCoPw6aC3X0Uc3jEIgWZktoXmsI/jAWMDw/5ZfiOO06bui+iWrD4vRSoGH9 36 | G2IpDgWic0Uuf+dDM6kCQF2/UbL6MZKDC4rVeFF3vJh7EScfmfssQ/eVEz637N06 37 | 2pzSvvB4xq6Gt9VwoGVNsn5r/K6AbT+rmewW57Jo7pg= 38 | -----END RSA PRIVATE KEY----- 39 | ]] 40 | '; 41 | 42 | $ENV{TEST_NGINX_PK_CONF} = ' 43 | local RSA_BAD_KEY = [[ 44 | -----BEGIN RSA PUBLIC KEY----- 45 | badkey 46 | -----END RSA PUBLIC KEY----- 47 | ]] 48 | local RSA_PUBLIC_KEY = [[ 49 | -----BEGIN RSA PUBLIC KEY----- 50 | MIGJAoGBAJ9YqFCTlhnmTYNCezMfy7yb7xwAzRinXup1Zl51517rhJq8W0wVwNt+ 51 | mcKwRzisA1SIqPGlhiyDb2RJKc1cCNrVNfj7xxOKCIihkIsTIKXzDfeAqrm0bU80 52 | BSjgjj6YUKZinUAACPoao8v+QFoRlXlsAy72mY7ipVnJqBd1AOPVAgMBAAE= 53 | -----END RSA PUBLIC KEY----- 54 | ]] 55 | local RSA_PRIV_KEY = [[ 56 | -----BEGIN RSA PRIVATE KEY----- 57 | MIICXAIBAAKBgQCfWKhQk5YZ5k2DQnszH8u8m+8cAM0Yp17qdWZedede64SavFtM 58 | FcDbfpnCsEc4rANUiKjxpYYsg29kSSnNXAja1TX4+8cTigiIoZCLEyCl8w33gKq5 59 | tG1PNAUo4I4+mFCmYp1AAAj6GqPL/kBaEZV5bAMu9pmO4qVZyagXdQDj1QIDAQAB 60 | AoGBAJega3lRFvHKPlP6vPTm+p2c3CiPcppVGXKNCD42f1XJUsNTHKUHxh6XF4U0 61 | 7HC27exQpkJbOZO99g89t3NccmcZPOCCz4aN0LcKv9oVZQz3Avz6aYreSESwLPqy 62 | AgmJEvuVe/cdwkhjAvIcbwc4rnI3OBRHXmy2h3SmO0Gkx3D5AkEAyvTrrBxDCQeW 63 | S4oI2pnalHyLi1apDI/Wn76oNKW/dQ36SPcqMLTzGmdfxViUhh19ySV5id8AddbE 64 | /b72yQLCuwJBAMj97VFPInOwm2SaWm3tw60fbJOXxuWLC6ltEfqAMFcv94ZT/Vpg 65 | nv93jkF9DLQC/CWHbjZbvtYTlzpevxYL8q8CQHiAKHkcopR2475f61fXJ1coBzYo 66 | suAZesWHzpjLnDwkm2i9D1ix5vDTVaJ3MF/cnLVTwbChLcXJSVabDi1UrUcCQAmn 67 | iNq6/mCoPw6aC3X0Uc3jEIgWZktoXmsI/jAWMDw/5ZfiOO06bui+iWrD4vRSoGH9 68 | G2IpDgWic0Uuf+dDM6kCQF2/UbL6MZKDC4rVeFF3vJh7EScfmfssQ/eVEz637N06 69 | 2pzSvvB4xq6Gt9VwoGVNsn5r/K6AbT+rmewW57Jo7pg= 70 | -----END RSA PRIVATE KEY----- 71 | ]] 72 | 73 | local RSA_PASS_PUBLIC_KEY = [[ 74 | -----BEGIN RSA PUBLIC KEY----- 75 | MIGJAoGBAPnnuVFUH4N+1yB0YVc4dS8zZYvvcwlDabPgee+IZaoC19tLSIT7smM3 76 | 3uiEq9Jk5Y0mO+PljVGqCwB4MPDlQ1GUsX/77kWWzXf++MeelDY8jd43gJAvaQaN 77 | bhVJg4SluyPIeCsmijrv0iZ9FMuDtGzjGXbuzbWSzAovFgIOg/dzAgMBAAE= 78 | -----END RSA PUBLIC KEY----- 79 | ]] 80 | 81 | local RSA_PASS_PRIV_KEY= [[ 82 | -----BEGIN RSA PRIVATE KEY----- 83 | Proc-Type: 4,ENCRYPTED 84 | DEK-Info: AES-128-CBC,90BB636FD15F26B5BBE8BB197C17A007 85 | 86 | IRxh4ZhuWPCQBd1w7S1Qg73sEh/tbxOyZuiaruo6L+tbOgStfo703vZeMdhB6bqe 87 | fn2616SmzzFUNvZ9iSKRqP+pOPjwxcSAorWytvDaSrLq2EXfY1/b0rTlrSWzTbE/ 88 | 2lCXzgi8OiAjE9r/2lqhsJU4BWvxVwxjXQNe9dq+xCNkNfVEEaHPv5/VgXWX3SZs 89 | OvzCMF0i4aZ5iY0DO+6BvNx98C1VDrguFkBfxUp7YZqoeGZL2Vdcxe3M/M8SHCjW 90 | n5Wh2wPJto/uqIv+d/WUjJzrqnessa5tGBaWvF48cRsHjMZIhSEgeQmctTGKqsc6 91 | D6iEkcwbocMk2UMLwgXJpvtsFM4UzL0WW/jf21DYqfXF/wvZDEYfgKbtPEChzqWu 92 | M0mUxE8SMSVA/qeF9FamwECcGkCplx7YE1pal7grZmuHTOCHGgVIw2UQH9Q/o1AN 93 | 5aG0N5aioEZErt6ox/AcdayGGwVyhesJmQd3p80PBxLb5dyFi8SPsx0A1EN2HIFJ 94 | blYMmUbn4eQ8dsv+EPhhc+tbp33GQE34JzJLGSjq2DAQbO50YBbr+B5SA7G2Wtbu 95 | 5gi7WCfihH6nc4jAP3VNGp2nF9PZ86K8PBclePZDRaL168dEBnjKsuujw61m4x2r 96 | sAE+U734LveoerlgRDxL2NVeIU3WR46qh/DCyL6oN5HKNGLAo239DOBTbYmaO+Xs 97 | nPiSlearb/PMPJnq494/0QD5dTmXTEtiDDoM67wmF3G/8jaqbZ0zb1XtrvclMddU 98 | RCxMyDrtIHNmD2uVFQefpY+6OiTaOsWYiK9FtPQwswue1NjPtXrClKt+gzJkCAd6 99 | -----END RSA PRIVATE KEY----- 100 | ]] 101 | 102 | local RSA_PKIX_PUB_KEY = [[ 103 | -----BEGIN PUBLIC KEY----- 104 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfWKhQk5YZ5k2DQnszH8u8m+8c 105 | AM0Yp17qdWZedede64SavFtMFcDbfpnCsEc4rANUiKjxpYYsg29kSSnNXAja1TX4 106 | +8cTigiIoZCLEyCl8w33gKq5tG1PNAUo4I4+mFCmYp1AAAj6GqPL/kBaEZV5bAMu 107 | 9pmO4qVZyagXdQDj1QIDAQAB 108 | -----END PUBLIC KEY----- 109 | ]] 110 | local RSA_PKCS8_PRIV_KEY = [[ 111 | -----BEGIN PRIVATE KEY----- 112 | MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJ9YqFCTlhnmTYNC 113 | ezMfy7yb7xwAzRinXup1Zl51517rhJq8W0wVwNt+mcKwRzisA1SIqPGlhiyDb2RJ 114 | Kc1cCNrVNfj7xxOKCIihkIsTIKXzDfeAqrm0bU80BSjgjj6YUKZinUAACPoao8v+ 115 | QFoRlXlsAy72mY7ipVnJqBd1AOPVAgMBAAECgYEAl6BreVEW8co+U/q89Ob6nZzc 116 | KI9ymlUZco0IPjZ/VclSw1McpQfGHpcXhTTscLbt7FCmQls5k732Dz23c1xyZxk8 117 | 4ILPho3Qtwq/2hVlDPcC/Pppit5IRLAs+rICCYkS+5V79x3CSGMC8hxvBziucjc4 118 | FEdebLaHdKY7QaTHcPkCQQDK9OusHEMJB5ZLigjamdqUfIuLVqkMj9afvqg0pb91 119 | DfpI9yowtPMaZ1/FWJSGHX3JJXmJ3wB11sT9vvbJAsK7AkEAyP3tUU8ic7CbZJpa 120 | be3DrR9sk5fG5YsLqW0R+oAwVy/3hlP9WmCe/3eOQX0MtAL8JYduNlu+1hOXOl6/ 121 | FgvyrwJAeIAoeRyilHbjvl/rV9cnVygHNiiy4Bl6xYfOmMucPCSbaL0PWLHm8NNV 122 | oncwX9yctVPBsKEtxclJVpsOLVStRwJACaeI2rr+YKg/DpoLdfRRzeMQiBZmS2he 123 | awj+MBYwPD/ll+I47Tpu6L6JasPi9FKgYf0bYikOBaJzRS5/50MzqQJAXb9Rsvox 124 | koMLitV4UXe8mHsRJx+Z+yxD95UTPrfs3TranNK+8HjGroa31XCgZU2yfmv8roBt 125 | P6uZ7BbnsmjumA== 126 | -----END PRIVATE KEY----- 127 | ]] 128 | local RSA_PKCS8_PASS_PRIV_KEY= [[ 129 | -----BEGIN ENCRYPTED PRIVATE KEY----- 130 | MIIC3TBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIhb4GOb0D4mgCAggA 131 | MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBDjIYlxykFE4aSt/4onYKxQBIIC 132 | gB25/DcrsWZpqFs3r54aC9OnT6dy32CgLs0lNzB+SHoHT1KTbDAX2mBEVTPrDl1+ 133 | l4IGsqtcS/wsCi7fzPZ18QrIrRR2FJueEwaS8Jbg5OT/3JGfCGXCaId0p2NljrwS 134 | HPGOIdkHx38uOubzPoYOfeQw0rYNy6IfEqHXA9CCNT6TP43CXWrxidDCC7N5WUE5 135 | d/HSuqN+cGt0TS2N3siqoURkAx0iALOX9pwsOLUfuwvmzkeU5nyaihLW+dseR/oL 136 | puGLjMwA/NV2pVo8ViRQslOUY4ImNQxtYlHdZqhm7AqU89qrIrUD/jc6TK7LqKxA 137 | UgAGZFcvrI7p2j3lpmkRs7oI0JZiEZ2vqZ1C3DuzfW8h6lsXULHrnHE5n3Y6dKsw 138 | mEnlYpf63KbPVlMePkuKLxyN3NuxsKUhpcZtea9OmdaDHqiU4LkaD05AJak3p/2j 139 | VebOUqZYaukh+ucEcItaraybXS9wIZI4re6W7Ak1dUuA3zO1T1kDN9dMOUiEqfy4 140 | o6NfC2Fn7EeyTSJVnEn5xmhUdbxPhpeJOb4ZAQJLrjaoYY1RDGhMux84otrtP/DD 141 | HFlOw0iWBjWkBAp/eDua+9M/+FS9c6xZ+NO1E6j3tsvFV3GfLHdBic6zp1tR2Zn/ 142 | OiC4gSzU7j3BENNpuuxTob5tEijhTjtDuTr7XpLYvQtSItlg05+Iq2VKtiN/oMYU 143 | DBOFWJuD79AG0ZwzLChAPNZG0pHgAH2qrdfwhygehNdncW9xZzQT8oFQlQTNHJcv 144 | vA4Mo08QiuVDyBQ7h8YuzUhQcbAMF7Y3CJ5u3ctiNkUuLLlN7Gy1sAvQ8K3D7Ju3 145 | xd+7BVH7S212qiNd3OdxE8Q= 146 | -----END ENCRYPTED PRIVATE KEY----- 147 | ]] 148 | '; 149 | 150 | #log_level 'warn'; 151 | 152 | run_tests(); 153 | 154 | __DATA__ 155 | 156 | 157 | === TEST 1: RSA default hello 158 | --- http_config eval: $::HttpConfig 159 | --- config 160 | location /t { 161 | content_by_lua_block { 162 | $TEST_NGINX_PK_CONF 163 | local resty_rsa = require "resty.rsa" 164 | local pub, err = resty_rsa:new({ public_key = RSA_PUBLIC_KEY }) 165 | if not pub then 166 | ngx.say("new rsa err: ", err) 167 | return 168 | end 169 | local encrypted, err = pub:encrypt("hello") 170 | if not encrypted then 171 | ngx.say("failed to encrypt: ", err) 172 | return 173 | end 174 | ngx.say("encrypted length: ", #encrypted) 175 | 176 | local priv, err = resty_rsa:new({ private_key = RSA_PRIV_KEY }) 177 | if not priv then 178 | ngx.say("new rsa err: ", err) 179 | return 180 | end 181 | local decrypted = priv:decrypt(encrypted) 182 | ngx.say(decrypted == "hello") 183 | 184 | collectgarbage() 185 | } 186 | } 187 | --- request 188 | GET /t 189 | --- response_body 190 | encrypted length: 128 191 | true 192 | --- no_error_log 193 | [error] 194 | 195 | 196 | === TEST 2: RSA bad pkey 197 | --- http_config eval: $::HttpConfig 198 | --- config 199 | location /t { 200 | content_by_lua_block { 201 | $TEST_NGINX_PK_CONF 202 | local resty_rsa = require "resty.rsa" 203 | local pub, err = resty_rsa:new({ public_key = RSA_BAD_KEY }) 204 | if not pub then 205 | ngx.say("new rsa err: ", err) 206 | return 207 | end 208 | local encrypted, err = pub:encrypt("hello") 209 | if not encrypted then 210 | ngx.say("failed to encrypt: ", err) 211 | return 212 | end 213 | ngx.say("encrypted length: ", #encrypted) 214 | 215 | collectgarbage() 216 | } 217 | } 218 | --- request 219 | GET /t 220 | --- response_body 221 | new rsa err: bad base64 decode 222 | --- no_error_log 223 | [error] 224 | 225 | 226 | === TEST 3: RSA different padding 227 | --- http_config eval: $::HttpConfig 228 | --- config 229 | location /t { 230 | content_by_lua_block { 231 | $TEST_NGINX_PK_CONF 232 | local resty_rsa = require "resty.rsa" 233 | local pub, err = resty_rsa:new({ public_key = RSA_PUBLIC_KEY, padding = resty_rsa.PADDING.RSA_PKCS1_PADDING }) 234 | if not pub then 235 | ngx.say("new rsa err: ", err) 236 | return 237 | end 238 | local encrypted, err = pub:encrypt("hello") 239 | if not encrypted then 240 | ngx.say("failed to encrypt: ", err) 241 | return 242 | end 243 | ngx.say("encrypted length: ", #encrypted) 244 | 245 | local priv, err = resty_rsa:new({ private_key = RSA_PRIV_KEY, padding = resty_rsa.PADDING.RSA_PKCS1_OAEP_PADDING }) 246 | if not priv then 247 | ngx.say("new rsa err: ", err) 248 | return 249 | end 250 | local decrypted = priv:decrypt(encrypted) 251 | ngx.say(decrypted == "hello") 252 | 253 | collectgarbage() 254 | } 255 | } 256 | --- request 257 | GET /t 258 | --- response_body 259 | encrypted length: 128 260 | false 261 | --- no_error_log 262 | [error] 263 | 264 | 265 | === TEST 4: RSA data size 266 | --- http_config eval: $::HttpConfig 267 | --- config 268 | location /t { 269 | content_by_lua_block { 270 | $TEST_NGINX_PK_CONF 271 | local resty_rsa = require "resty.rsa" 272 | local padding_names = {} 273 | for name, _ in pairs(resty_rsa.PADDING) do 274 | padding_names[#padding_names+1] = name 275 | end 276 | table.sort(padding_names) 277 | 278 | for _, name in ipairs(padding_names) do 279 | local padding = resty_rsa.PADDING[name] 280 | local pub, err = resty_rsa:new({ public_key = RSA_PUBLIC_KEY, padding = padding }) 281 | if not pub then 282 | ngx.say("new rsa err: ", err) 283 | return 284 | end 285 | 286 | local s, max_len 287 | for i = 1, 130 do 288 | s = (s or "") .. "p" 289 | max_len = #s 290 | local encrypted, err = pub:encrypt(s) 291 | if not encrypted then 292 | break 293 | end 294 | end 295 | ngx.say(name, ":", max_len - 1) 296 | end 297 | 298 | collectgarbage() 299 | } 300 | } 301 | --- request 302 | GET /t 303 | --- response_body 304 | RSA_NO_PADDING:0 305 | RSA_PKCS1_OAEP_PADDING:86 306 | RSA_PKCS1_PADDING:117 307 | --- no_error_log 308 | [error] 309 | 310 | 311 | === TEST 5: RSA RSA_NO_PADDING 312 | --- http_config eval: $::HttpConfig 313 | --- config 314 | location /t { 315 | content_by_lua_block { 316 | $TEST_NGINX_PK_CONF 317 | local resty_rsa = require "resty.rsa" 318 | local pub, err = resty_rsa:new( { public_key = RSA_PUBLIC_KEY, padding = resty_rsa.PADDING.RSA_NO_PADDING }) 319 | if not pub then 320 | ngx.say("new rsa err: ", err) 321 | return 322 | end 323 | 324 | local s = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 325 | local encrypted, err = pub:encrypt(s) 326 | if not encrypted then 327 | ngx.say("failed to encrypt: ", err) 328 | return 329 | end 330 | ngx.say("encrypted length: ", #encrypted) 331 | 332 | local priv, err = resty_rsa:new({ private_key = RSA_PRIV_KEY, padding = resty_rsa.PADDING.RSA_NO_PADDING}) 333 | if not priv then 334 | ngx.say("new rsa err: ", err) 335 | return 336 | end 337 | local decrypted = priv:decrypt(encrypted) 338 | 339 | ngx.say(decrypted == s) 340 | 341 | collectgarbage() 342 | } 343 | } 344 | --- request 345 | GET /t 346 | --- response_body 347 | encrypted length: 128 348 | true 349 | --- no_error_log 350 | [error] 351 | 352 | 353 | === TEST 6: RSA pass phrase 354 | --- http_config eval: $::HttpConfig 355 | --- config 356 | location /t { 357 | content_by_lua_block { 358 | $TEST_NGINX_PK_CONF 359 | local resty_rsa = require "resty.rsa" 360 | local pub, err = resty_rsa:new({ public_key = RSA_PASS_PUBLIC_KEY }) 361 | if not pub then 362 | ngx.say("new rsa err: ", err) 363 | return 364 | end 365 | local encrypted, err = pub:encrypt("hello") 366 | if not encrypted then 367 | ngx.say("failed to encrypt: ", err) 368 | return 369 | end 370 | ngx.say("encrypted length: ", #encrypted) 371 | 372 | local priv, err = resty_rsa:new({ private_key = RSA_PASS_PRIV_KEY, password = "foobar"} ) 373 | if not priv then 374 | ngx.say("new rsa err: ", err) 375 | return 376 | end 377 | local decrypted = priv:decrypt(encrypted) 378 | ngx.say(decrypted == "hello") 379 | 380 | collectgarbage() 381 | } 382 | } 383 | --- request 384 | GET /t 385 | --- response_body 386 | encrypted length: 128 387 | true 388 | --- no_error_log 389 | [error] 390 | 391 | 392 | 393 | === TEST 7: RSA sign 394 | --- http_config eval: $::HttpConfig 395 | --- config 396 | location /t { 397 | content_by_lua_block { 398 | $TEST_NGINX_SHORT_PK_CONF 399 | local resty_rsa = require "resty.rsa" 400 | local algorithm = "SHA256" 401 | local priv, err = resty_rsa:new({ private_key = RSA_PRIV_KEY, algorithm = algorithm }) 402 | if not priv then 403 | ngx.say("new rsa err: ", err) 404 | return 405 | end 406 | 407 | local str = "hello" 408 | local sig, err = priv:sign(str) 409 | if not sig then 410 | ngx.say("failed to sign:", err) 411 | return 412 | end 413 | ngx.say("sig length: ", #sig) 414 | 415 | local pub, err = resty_rsa:new({ public_key = RSA_PUBLIC_KEY, algorithm = algorithm }) 416 | if not pub then 417 | ngx.say("new rsa err: ", err) 418 | return 419 | end 420 | local verify, err = pub:verify(str, sig) 421 | if not verify then 422 | ngx.say("verify err: ", err) 423 | return 424 | end 425 | ngx.say(verify) 426 | 427 | collectgarbage() 428 | } 429 | } 430 | --- request 431 | GET /t 432 | --- response_body 433 | sig length: 128 434 | true 435 | --- no_error_log 436 | [error] 437 | 438 | 439 | 440 | === TEST 8: RSA sign: wrong with different signature algorithm 441 | --- http_config eval: $::HttpConfig 442 | --- config 443 | location /t { 444 | content_by_lua_block { 445 | $TEST_NGINX_PK_CONF 446 | local resty_rsa = require "resty.rsa" 447 | local priv, err = resty_rsa:new({ private_key = RSA_PRIV_KEY, algorithm = "RSA-MD5" }) 448 | if not priv then 449 | ngx.say("new rsa err: ", err) 450 | return 451 | end 452 | 453 | local str = "hello" 454 | local sig, err = priv:sign(str) 455 | if not sig then 456 | ngx.say("failed to sign:", err) 457 | return 458 | end 459 | ngx.say("sig length: ", #sig) 460 | 461 | local pub, err = resty_rsa:new({ public_key = RSA_PUBLIC_KEY, algorithm = "RSA-SHA1" }) 462 | if not pub then 463 | ngx.say("new rsa err: ", err) 464 | return 465 | end 466 | local verify, err = pub:verify(str, sig) 467 | if not verify then 468 | ngx.say("verify err: ", err) 469 | return 470 | end 471 | ngx.say(verify) 472 | 473 | collectgarbage() 474 | } 475 | } 476 | --- request 477 | GET /t 478 | --- response_body_like 479 | sig length: 128 480 | verify err: (bad signature|algorithm mismatch|bad signature: RSA lib) 481 | --- no_error_log 482 | [error] 483 | 484 | 485 | 486 | === TEST 9: RSA sign with different algorithm 487 | --- http_config eval: $::HttpConfig 488 | --- config 489 | location /t { 490 | content_by_lua_block { 491 | $TEST_NGINX_SHORT_PK_CONF 492 | local resty_rsa = require "resty.rsa" 493 | 494 | local algorithms = { 495 | "MD5", 496 | "RIPEMD160", 497 | "SHA1", 498 | "SHA224", 499 | "SHA256", 500 | "SHA384", 501 | "SHA512", 502 | "md5", 503 | "ripemd160", 504 | "sha1", 505 | "sha224", 506 | "sha256", 507 | "sha384", 508 | "sha512", 509 | "RSA-MD5", 510 | "RSA-RIPEMD160", 511 | "RSA-SHA1", 512 | "RSA-SHA1-2", 513 | "RSA-SHA224", 514 | "RSA-SHA256", 515 | "RSA-SHA384", 516 | "RSA-SHA512", 517 | "md5WithRSAEncryption", 518 | "ripemd", 519 | "ripemd160WithRSA", 520 | "rmd160", 521 | "sha1WithRSAEncryption", 522 | "sha224WithRSAEncryption", 523 | "sha256WithRSAEncryption", 524 | "sha384WithRSAEncryption", 525 | "sha512WithRSAEncryption", 526 | "ssl3-md5", 527 | "ssl3-sha1", 528 | } 529 | local count = 0 530 | for i, algorithm in pairs(algorithms) do 531 | local str = "hello" 532 | local sig, pub, verify 533 | local priv, err = resty_rsa:new({ private_key = RSA_PRIV_KEY, algorithm = algorithm }) 534 | if not priv then 535 | ngx.say("new rsa err: ", err, "; with algorithm: ", algorithm) 536 | goto continue 537 | end 538 | 539 | sig, err = priv:sign(str) 540 | if not sig then 541 | ngx.say("failed to sign:", err, "; with algorithm: ", algorithm) 542 | goto continue 543 | end 544 | 545 | pub, err = resty_rsa:new({ public_key = RSA_PUBLIC_KEY, algorithm = algorithm }) 546 | if not pub then 547 | ngx.say("new rsa err: ", err) 548 | goto continue 549 | end 550 | 551 | verify, err = pub:verify(str, sig) 552 | if not verify then 553 | ngx.say("verify err: ", err) 554 | goto continue 555 | end 556 | 557 | count = count + 1 558 | 559 | ::continue:: 560 | 561 | collectgarbage() 562 | end 563 | 564 | ngx.say(count == #algorithms) 565 | } 566 | } 567 | --- request 568 | GET /t 569 | --- response_body 570 | true 571 | --- no_error_log 572 | [error] 573 | 574 | 575 | 576 | === TEST 10: RSA sign: need init for every call 577 | --- http_config eval: $::HttpConfig 578 | --- config 579 | location /t { 580 | content_by_lua_block { 581 | $TEST_NGINX_PK_CONF 582 | local resty_rsa = require "resty.rsa" 583 | local priv, err = resty_rsa:new({ private_key = RSA_PRIV_KEY, algorithm = "RSA-SHA1" }) 584 | if not priv then 585 | ngx.say("new rsa err: ", err) 586 | return 587 | end 588 | 589 | local str = "hello" 590 | local sig, err = priv:sign(str) 591 | if not sig then 592 | ngx.say("failed to sign:", err) 593 | return 594 | end 595 | 596 | local sig, err = priv:sign(str) 597 | if not sig then 598 | ngx.say("failed to sign:", err) 599 | return 600 | end 601 | ngx.say("sig length: ", #sig) 602 | 603 | local pub, err = resty_rsa:new({ public_key = RSA_PUBLIC_KEY, algorithm = "RSA-SHA1" }) 604 | if not pub then 605 | ngx.say("new rsa err: ", err) 606 | return 607 | end 608 | local verify, err = pub:verify(str, sig) 609 | if not verify then 610 | ngx.say("verify err: ", err) 611 | return 612 | end 613 | ngx.say(verify) 614 | 615 | local verify, err = pub:verify(str, sig) 616 | if not verify then 617 | ngx.say("verify err: ", err) 618 | return 619 | end 620 | ngx.say(verify) 621 | 622 | collectgarbage() 623 | } 624 | } 625 | --- request 626 | GET /t 627 | --- response_body 628 | sig length: 128 629 | true 630 | true 631 | --- no_error_log 632 | [error] 633 | 634 | 635 | 636 | === TEST 11: generate RSA public key and private key 637 | --- http_config eval: $::HttpConfig 638 | --- config 639 | location /t { 640 | content_by_lua_block { 641 | $TEST_NGINX_PK_CONF 642 | local resty_rsa = require "resty.rsa" 643 | local rsa_public_key, rsa_private_key, err = resty_rsa:generate_rsa_keys(2048) 644 | 645 | local pub, err = resty_rsa:new({ public_key = rsa_public_key }) 646 | if not pub then 647 | ngx.say("new rsa err: ", err) 648 | return 649 | end 650 | local encrypted, err = pub:encrypt("hello") 651 | if not encrypted then 652 | ngx.say("failed to encrypt: ", err) 653 | return 654 | end 655 | ngx.say("encrypted length: ", #encrypted) 656 | 657 | local priv, err = resty_rsa:new({ private_key = rsa_private_key }) 658 | if not priv then 659 | ngx.say("new rsa err: ", err) 660 | return 661 | end 662 | local decrypted = priv:decrypt(encrypted) 663 | ngx.say(decrypted == "hello") 664 | 665 | collectgarbage() 666 | } 667 | } 668 | --- request 669 | GET /t 670 | --- response_body 671 | encrypted length: 256 672 | true 673 | --- no_error_log 674 | [error] 675 | 676 | 677 | 678 | === TEST 12: generate PKCS#8 format public key and private key 679 | --- http_config eval: $::HttpConfig 680 | --- config 681 | location /t { 682 | content_by_lua_block { 683 | $TEST_NGINX_PK_CONF 684 | local resty_rsa = require "resty.rsa" 685 | local rsa_public_key, rsa_private_key, err = resty_rsa:generate_rsa_keys(2048, true) 686 | 687 | local pub, err = resty_rsa:new({ 688 | public_key = rsa_public_key, 689 | key_type = resty_rsa.KEY_TYPE.PKCS8, 690 | }) 691 | if not pub then 692 | ngx.say("new rsa err: ", err) 693 | return 694 | end 695 | local encrypted, err = pub:encrypt("hello") 696 | if not encrypted then 697 | ngx.say("failed to encrypt: ", err) 698 | return 699 | end 700 | 701 | local priv, err = resty_rsa:new({ private_key = rsa_private_key }) 702 | if not priv then 703 | ngx.say("new rsa err: ", err) 704 | return 705 | end 706 | local decrypted = priv:decrypt(encrypted) 707 | ngx.say(decrypted == "hello") 708 | 709 | collectgarbage() 710 | } 711 | } 712 | --- request 713 | GET /t 714 | --- response_body 715 | true 716 | --- no_error_log 717 | [error] 718 | 719 | 720 | 721 | === TEST 13: report generate RSA keypair error 722 | --- http_config eval: $::HttpConfig 723 | --- config 724 | location /t { 725 | content_by_lua_block { 726 | $TEST_NGINX_PK_CONF 727 | local resty_rsa = require "resty.rsa" 728 | local rsa_public_key, rsa_private_key, err = resty_rsa:generate_rsa_keys(0) 729 | if not (rsa_public_key and rsa_private_key) then 730 | ngx.say("generated rsa keys err: ", err) 731 | end 732 | 733 | collectgarbage() 734 | } 735 | } 736 | --- request 737 | GET /t 738 | --- response_body_like 739 | generated rsa keys err: .+$ 740 | --- no_error_log 741 | [error] 742 | 743 | 744 | 745 | === TEST 14: support PKIX format public key(encrypt) 746 | --- http_config eval: $::HttpConfig 747 | --- config 748 | location /t { 749 | content_by_lua_block { 750 | $TEST_NGINX_PK_CONF 751 | local resty_rsa = require "resty.rsa" 752 | local pub, err = resty_rsa:new({ 753 | public_key = RSA_PKIX_PUB_KEY, 754 | key_type = resty_rsa.KEY_TYPE.PKCS8, 755 | }) 756 | if not pub then 757 | ngx.say("new rsa err: ", err) 758 | return 759 | end 760 | local encrypted, err = pub:encrypt("hello") 761 | if not encrypted then 762 | ngx.say("failed to encrypt: ", err) 763 | return 764 | end 765 | 766 | local priv, err = resty_rsa:new({ private_key = RSA_PRIV_KEY }) 767 | if not priv then 768 | ngx.say("new rsa err: ", err) 769 | return 770 | end 771 | local decrypted = priv:decrypt(encrypted) 772 | ngx.say(decrypted == "hello") 773 | 774 | collectgarbage() 775 | } 776 | } 777 | --- request 778 | GET /t 779 | --- response_body 780 | true 781 | --- no_error_log 782 | [error] 783 | 784 | 785 | 786 | === TEST 15: support PKIX format public key(verify) 787 | --- http_config eval: $::HttpConfig 788 | --- config 789 | location /t { 790 | content_by_lua_block { 791 | $TEST_NGINX_PK_CONF 792 | local resty_rsa = require "resty.rsa" 793 | local algorithm = "SHA256" 794 | local priv, err = resty_rsa:new({ private_key = RSA_PRIV_KEY, algorithm = algorithm }) 795 | if not priv then 796 | ngx.say("new rsa err: ", err) 797 | return 798 | end 799 | 800 | local str = "hello" 801 | local sig, err = priv:sign(str) 802 | if not sig then 803 | ngx.say("failed to sign:", err) 804 | return 805 | end 806 | 807 | local pub, err = resty_rsa:new({ 808 | public_key = RSA_PKIX_PUB_KEY, 809 | key_type = resty_rsa.KEY_TYPE.PKIX, 810 | algorithm = algorithm, 811 | }) 812 | if not pub then 813 | ngx.say("new rsa err: ", err) 814 | return 815 | end 816 | local verify, err = pub:verify(str, sig) 817 | if not verify then 818 | ngx.say("verify err: ", err) 819 | return 820 | end 821 | ngx.say(verify) 822 | 823 | collectgarbage() 824 | } 825 | } 826 | --- request 827 | GET /t 828 | --- response_body 829 | true 830 | --- no_error_log 831 | [error] 832 | 833 | 834 | 835 | === TEST 16: support PKCS#8 format private key(encrypt) 836 | --- http_config eval: $::HttpConfig 837 | --- config 838 | location /t { 839 | content_by_lua_block { 840 | $TEST_NGINX_PK_CONF 841 | local resty_rsa = require "resty.rsa" 842 | local pub, err = resty_rsa:new({ 843 | public_key = RSA_PUBLIC_KEY, 844 | }) 845 | if not pub then 846 | ngx.say("new rsa err: ", err) 847 | return 848 | end 849 | local encrypted, err = pub:encrypt("hello") 850 | if not encrypted then 851 | ngx.say("failed to encrypt: ", err) 852 | return 853 | end 854 | 855 | local priv, err = resty_rsa:new({ 856 | private_key = RSA_PKCS8_PRIV_KEY, 857 | key_type = resty_rsa.KEY_TYPE.PKCS8, 858 | }) 859 | if not priv then 860 | ngx.say("new rsa err: ", err) 861 | return 862 | end 863 | local decrypted = priv:decrypt(encrypted) 864 | ngx.say(decrypted == "hello") 865 | 866 | collectgarbage() 867 | } 868 | } 869 | --- request 870 | GET /t 871 | --- response_body 872 | true 873 | --- no_error_log 874 | [error] 875 | 876 | 877 | 878 | === TEST 17: support PKCS#8 format private key(verify) 879 | --- http_config eval: $::HttpConfig 880 | --- config 881 | location /t { 882 | content_by_lua_block { 883 | $TEST_NGINX_PK_CONF 884 | local resty_rsa = require "resty.rsa" 885 | local algorithm = "SHA256" 886 | local priv, err = resty_rsa:new({ 887 | private_key = RSA_PKCS8_PRIV_KEY, 888 | key_type = resty_rsa.KEY_TYPE.PKCS8, 889 | algorithm = algorithm, 890 | }) 891 | if not priv then 892 | ngx.say("new rsa err: ", err) 893 | return 894 | end 895 | 896 | local str = "hello" 897 | local sig, err = priv:sign(str) 898 | if not sig then 899 | ngx.say("failed to sign:", err) 900 | return 901 | end 902 | 903 | local pub, err = resty_rsa:new({ 904 | public_key = RSA_PUBLIC_KEY, 905 | algorithm = algorithm, 906 | }) 907 | if not pub then 908 | ngx.say("new rsa err: ", err) 909 | return 910 | end 911 | local verify, err = pub:verify(str, sig) 912 | if not verify then 913 | ngx.say("verify err: ", err) 914 | return 915 | end 916 | ngx.say(verify) 917 | 918 | collectgarbage() 919 | } 920 | } 921 | --- request 922 | GET /t 923 | --- response_body 924 | true 925 | --- no_error_log 926 | [error] 927 | 928 | 929 | 930 | === TEST 18: RSA pass phrase (PKCS8) 931 | --- http_config eval: $::HttpConfig 932 | --- config 933 | location /t { 934 | content_by_lua_block { 935 | $TEST_NGINX_PK_CONF 936 | local resty_rsa = require "resty.rsa" 937 | local pub, err = resty_rsa:new({ public_key = RSA_PASS_PUBLIC_KEY }) 938 | if not pub then 939 | ngx.say("new rsa err: ", err) 940 | return 941 | end 942 | local encrypted, err = pub:encrypt("hello") 943 | if not encrypted then 944 | ngx.say("failed to encrypt: ", err) 945 | return 946 | end 947 | ngx.say("encrypted length: ", #encrypted) 948 | 949 | local priv, err = resty_rsa:new({ 950 | private_key = RSA_PKCS8_PASS_PRIV_KEY, 951 | key_type = resty_rsa.KEY_TYPE.PKCS8, 952 | password = "foobar", 953 | }) 954 | if not priv then 955 | ngx.say("new rsa err: ", err) 956 | return 957 | end 958 | local decrypted = priv:decrypt(encrypted) 959 | ngx.say(decrypted == "hello") 960 | 961 | collectgarbage() 962 | } 963 | } 964 | --- request 965 | GET /t 966 | --- response_body 967 | encrypted length: 128 968 | true 969 | --- no_error_log 970 | [error] 971 | 972 | 973 | 974 | === TEST 19: process the whole error queue when handling OpenSSL error 975 | We have to skip this test in Valgrind mode because when running as Valgrind's 976 | child process, OpenSSL will prompt the password, blocking the test until being killed. 977 | --- skip_eval: 3: defined $ENV{TEST_NGINX_USE_VALGRIND} 978 | --- http_config eval: $::HttpConfig 979 | --- config 980 | location /t { 981 | content_by_lua_block { 982 | $TEST_NGINX_PK_CONF 983 | local resty_rsa = require "resty.rsa" 984 | local priv, err = resty_rsa:new({ 985 | private_key = RSA_PASS_PRIV_KEY, 986 | }) 987 | if not priv then 988 | ngx.say("new rsa err: ", err) 989 | return 990 | end 991 | } 992 | } 993 | --- request 994 | GET /t 995 | --- response_body_like 996 | new rsa err: (processing error: while reading strings: )?problems getting password: (interrupted or cancelled: interrupted or cancelled: )?bad password read 997 | --- no_error_log 998 | [error] 999 | 1000 | 1001 | 1002 | === TEST 20: auto detect PKIX format public key if no key type given 1003 | --- http_config eval: $::HttpConfig 1004 | --- config 1005 | location /t { 1006 | content_by_lua_block { 1007 | $TEST_NGINX_PK_CONF 1008 | local resty_rsa = require "resty.rsa" 1009 | local pub, err = resty_rsa:new({ 1010 | public_key = RSA_PKIX_PUB_KEY, 1011 | }) 1012 | if not pub then 1013 | ngx.say("new rsa err: ", err) 1014 | return 1015 | end 1016 | local encrypted, err = pub:encrypt("hello") 1017 | if not encrypted then 1018 | ngx.say("failed to encrypt: ", err) 1019 | return 1020 | end 1021 | 1022 | local priv, err = resty_rsa:new({ private_key = RSA_PRIV_KEY }) 1023 | if not priv then 1024 | ngx.say("new rsa err: ", err) 1025 | return 1026 | end 1027 | local decrypted = priv:decrypt(encrypted) 1028 | ngx.say(decrypted == "hello") 1029 | 1030 | collectgarbage() 1031 | } 1032 | } 1033 | --- request 1034 | GET /t 1035 | --- response_body 1036 | true 1037 | --- no_error_log 1038 | [error] 1039 | 1040 | 1041 | 1042 | === TEST 21: specify PKCS#1 key type but the format is PKIX 1043 | --- http_config eval: $::HttpConfig 1044 | --- config 1045 | location /t { 1046 | content_by_lua_block { 1047 | $TEST_NGINX_PK_CONF 1048 | local resty_rsa = require "resty.rsa" 1049 | local pub, err = resty_rsa:new({ 1050 | public_key = RSA_PKIX_PUB_KEY, 1051 | key_type = resty_rsa.KEY_TYPE.PKCS1, 1052 | }) 1053 | if not pub then 1054 | ngx.say("new rsa err: ", err) 1055 | return 1056 | end 1057 | } 1058 | } 1059 | --- request 1060 | GET /t 1061 | --- response_body 1062 | new rsa err: no start line: Expecting: RSA PUBLIC KEY 1063 | --- no_error_log 1064 | [error] 1065 | -------------------------------------------------------------------------------- /test_with_valgrind.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | TEST_NGINX_USE_VALGRIND=1 TEST_NGINX_SLEEP=1 prove -r t 2>&1 | grep -B 3 -A 20 "match-leak-kinds: definite" 4 | # Ignore if testing is failed or grep nothing 5 | test $? -eq 0 && exit 1 6 | exit 0 7 | -------------------------------------------------------------------------------- /valgrind.suppress: -------------------------------------------------------------------------------- 1 | { 2 | 3 | Memcheck:Value8 4 | obj:* 5 | } 6 | { 7 | 8 | Memcheck:Cond 9 | obj:/usr/lib64/libcrypto.so* 10 | } 11 | { 12 | 13 | Memcheck:Cond 14 | fun:lj_* 15 | } 16 | { 17 | 18 | Memcheck:Cond 19 | fun:lua_* 20 | } 21 | { 22 | 23 | Memcheck:Leak 24 | fun:malloc 25 | fun:ngx_alloc 26 | } 27 | { 28 | 29 | Memcheck:Cond 30 | fun:str_fastcmp 31 | fun:lj_str_new 32 | } 33 | { 34 | 35 | Memcheck:Cond 36 | fun:tmalloc_small 37 | fun:lj_alloc_malloc 38 | } 39 | { 40 | 41 | Memcheck:Cond 42 | fun:memcpy@@* 43 | fun:SHA1_Update 44 | } 45 | { 46 | 47 | Memcheck:Cond 48 | fun:is_overlap 49 | fun:memcpy@@* 50 | fun:SHA1_Update 51 | } 52 | { 53 | 54 | Memcheck:Cond 55 | fun:memset 56 | fun:lj_cf_ffi_new 57 | } 58 | { 59 | 60 | Memcheck:Cond 61 | fun:malloc 62 | fun:CRYPTO_malloc 63 | } 64 | { 65 | 66 | Memcheck:Cond 67 | fun:gc_* 68 | } 69 | { 70 | 71 | Memcheck:Param 72 | epoll_ctl(event) 73 | fun:epoll_ctl 74 | fun:ngx_epoll_test_rdhup 75 | fun:ngx_epoll_init 76 | fun:ngx_event_process_init 77 | fun:ngx_worker_process_init 78 | fun:ngx_worker_process_cycle 79 | fun:ngx_spawn_process 80 | fun:ngx_start_worker_processes 81 | fun:ngx_master_process_cycle 82 | fun:main 83 | } 84 | --------------------------------------------------------------------------------