├── .github └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── lib └── sm2_crypto.rb ├── sm2-crypto.gemspec └── test └── test_sm2_crypto.rb /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Ruby 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | pull_request: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | name: Ruby ${{ matrix.ruby }} 14 | strategy: 15 | matrix: 16 | ruby: 17 | - '3.4' 18 | - '3.3' 19 | - '3.2' 20 | - '3.1' 21 | - '3.0' 22 | - '2.7' 23 | 24 | steps: 25 | - uses: actions/checkout@v3 26 | - name: Set up Ruby 27 | uses: ruby/setup-ruby@v1 28 | with: 29 | ruby-version: ${{ matrix.ruby }} 30 | bundler-cache: true 31 | - name: Run test 32 | run: bundle exec rake test 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | /Gemfile.lock 10 | *.DS_Store -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [Unreleased] 2 | 3 | ## [0.2.2] - 2025-03-26 4 | 5 | - Add ASN.1 DER format for sm2 sign and verify 6 | 7 | ## [0.2.0] - 2023-04-21 8 | 9 | - Add sm2 sign and verify support 10 | 11 | ## [0.1.0] - 2023-03-27 12 | 13 | - Initial release 14 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | # Specify your gem's dependencies in sm2-crypto.gemspec 6 | gemspec 7 | 8 | gem "rake", "~> 13.2" 9 | gem "minitest", "~> 5.25" 10 | # for ruby 3.4 + 11 | gem "base64", '~> 0.2' 12 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 Seekr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sm2-crypto 2 | 3 | sm2-crypto is an implementation of the SM2 encryption and decryption algorithm in pure Ruby based on the OpenSSL SM2 elliptic curve cryptography standard. 4 | 5 | ## Installation 6 | OpenSSL has added support for SM2/SM3/SM4 encryption algorithms since version [1.1.1](https://www.openssl.org/news/openssl-1.1.1-notes.html). However, Ruby does not wrap the C interface related to SM2 encryption and decryption in OpenSSL. This library is not a wrapper for the C interface, but an implementation of the SM2 encryption and decryption algorithm in pure Ruby, based on the `OpenSSL::PKey::EC` interface. 7 | 8 | Before using, please make sure your Ruby's OpenSSL version is `1.1.1` or higher. 9 | 10 | Check the OpenSSL version in Ruby: 11 | 12 | ```ruby 13 | irb(main):001:0> require "openssl" 14 | => true 15 | irb(main):002:0> OpenSSL::OPENSSL_VERSION 16 | => "OpenSSL 1.1.1k 25 Mar 2021" 17 | ``` 18 | 19 | Add this line to your Gemfile: 20 | 21 | ```ruby 22 | gem "sm2-crypto" 23 | ``` 24 | 25 | Or install it yourself via command line: 26 | 27 | ```shell 28 | $ gem install sm2-crypto 29 | ``` 30 | 31 | ## Usage 32 | 33 | ### Encrypt and Decrypt 34 | 35 | ```ruby 36 | require 'sm2_crypto' 37 | 38 | # Generate key pair 39 | keypair = OpenSSL::PKey::EC.generate("SM2") 40 | private_key = keypair.private_key.to_s(2) 41 | public_key = keypair.public_key.to_octet_string(:uncompressed) 42 | 43 | # Encrypt data 44 | message = "Hello, SM2 encryption!" 45 | encrypted_data = SM2Crypto.encrypt(public_key, message) 46 | puts "Encrypted message: #{encrypted_data}" 47 | 48 | # Decrypt data 49 | decrypted_message = SM2Crypto.decrypt(private_key, encrypted_data) 50 | puts "Decrypted message: #{decrypted_message}" 51 | ``` 52 | 53 | ### Sign and Verify 54 | 55 | ```ruby 56 | message = "Hello, SM2 signature!" 57 | # get signatrue 58 | sign = SM2Crypto.sign(private_key, message) 59 | # verify signatrue 60 | SM2Crypto.verify(public_key, message, sign) 61 | 62 | # user_id should be a hex string, default: "31323334353637383132333435363738" which is equal to utf-8 string: "1234567812345678" 63 | user_id = "31323334353637383132333435363738" 64 | # sign with hash and user_id 65 | sign = SM2Crypto.sign(private_key, message, sm3_hash: true, user_id: user_id) 66 | # verify with hash and user_id 67 | SM2Crypto.verify(public_key, message, sign, sm3_hash: true, user_id: user_id) 68 | 69 | # sign with ASN.1 DER format output 70 | sign = SM2Crypto.sign(private_key, message, asn1: true) 71 | # verify signatrue 72 | SM2Crypto.verify(public_key, message, sign, asn1: true) 73 | ``` 74 | 75 | ### Get Public Key from Private Key 76 | 77 | ```ruby 78 | public_key = SM2Crypto.get_public_key(private_key) 79 | ``` 80 | 81 | ## Contributing 82 | 83 | Contributions to the project are welcome. Please fork the repository, create a feature branch, and submit a pull request. Be sure to add tests and update documentation as needed. 84 | 85 | For bug reports and feature requests, please open an issue on the [GitHub repository](https://github.com/numbcoder/sm2-crypto/issues). 86 | 87 | ## License 88 | 89 | sm2-crypto is released under the [MIT License](https://opensource.org/licenses/MIT). 90 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler/gem_tasks" 4 | require "rake/testtask" 5 | 6 | Rake::TestTask.new(:test) do |t| 7 | t.libs << "test" 8 | t.libs << "lib" 9 | t.test_files = FileList["test/**/test_*.rb"] 10 | end 11 | 12 | task default: :test 13 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "bundler/setup" 5 | require "sm2_crypto" 6 | 7 | require "irb" 8 | IRB.start(__FILE__) 9 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /lib/sm2_crypto.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "openssl" 4 | 5 | module SM2Crypto 6 | module_function 7 | 8 | # Key Derived Function 9 | # 10 | # @param key [String] key data 11 | # @param klen [Integer] export key length 12 | # @return [Array] bytes array 13 | def kdf(key, klen) 14 | # hlen = 32 # 哈希函数 SM3 输出长度 32 字节 15 | n = (klen.to_f / 32).ceil # n = klen/hlen 向上取整 16 | 17 | (1..n).map do |ct| 18 | OpenSSL::Digest.digest("SM3", key + [ct].pack("N")) 19 | end.join.slice(0, klen).bytes 20 | end 21 | 22 | # Encrypt 23 | # 24 | # @param public_key [String] public key, format: binary string 25 | # @param data [String] data 26 | # @param cipher_mode [Integer] 0: C1C2C3, 1: C1C3C2, default: 1 27 | # @return [String] encrypted data, format: binary string 28 | def encrypt(public_key, data, cipher_mode: 1) 29 | data = data.unpack1("a*") unless data.ascii_only? 30 | public_key = "\x04#{public_key}" if public_key.size == 64 && public_key[0] != "\x04" 31 | 32 | point = OpenSSL::PKey::EC::Point.new(OpenSSL::PKey::EC::Group.new("SM2"), OpenSSL::BN.new(public_key, 2)) 33 | random_key = OpenSSL::PKey::EC.generate("SM2") 34 | k = random_key.private_key 35 | c1 = random_key.public_key.to_octet_string(:uncompressed) 36 | 37 | p = point.mul(k) 38 | p_bin_str = p.to_octet_string(:uncompressed) 39 | x2 = p_bin_str[1, 32] 40 | y2 = p_bin_str[33, 32] 41 | 42 | t = kdf(x2 + y2, data.bytesize) 43 | t = kdf(x2 + y2, data.bytesize) while t[0, 8].uniq == [0] && t.uniq == [0] 44 | 45 | c2 = data.each_byte.map.with_index { |b, i| b ^ t[i] }.pack("C*") 46 | 47 | # c3 = hash(x2 || msg || y2) 48 | c3 = OpenSSL::Digest.digest("SM3", x2 + data + y2) 49 | 50 | cipher_mode == 0 ? c1 + c2 + c3 : c1 + c3 + c2 51 | end 52 | 53 | # Decrypt 54 | # 55 | # @param private_key [String] private key, format: binary string 56 | # @param data [String] data to be decrypted, format: binary string 57 | # @param cipher_mode [Integer] 0: C1C2C3, 1: C1C3C2, default: 1 58 | # @return [String] encrypted data, format: binary string 59 | def decrypt(private_key, data, cipher_mode: 1) 60 | data = "\x04#{data}" if data[0] != "\x04" 61 | 62 | c1 = data[0, 65] 63 | c2_size = data.bytesize - 97 64 | if cipher_mode == 0 65 | c2 = data[65, c2_size] 66 | c3 = data[65 + c2_size, 32] 67 | else 68 | c3 = data[65, 32] 69 | c2 = data[97, c2_size] 70 | end 71 | point = OpenSSL::PKey::EC::Point.new(OpenSSL::PKey::EC::Group.new("SM2"), OpenSSL::BN.new(c1, 2)) 72 | pkey = OpenSSL::BN.new(private_key, 2) 73 | p = point.mul(pkey) 74 | p_bin_str = p.to_octet_string(:uncompressed) 75 | x2 = p_bin_str[1, 32] 76 | y2 = p_bin_str[33, 32] 77 | 78 | t = kdf(x2 + y2, c2_size) 79 | raise ArgumentError, "KDF is 0" if t[0, 8].uniq == [0] && t.uniq == [0] 80 | 81 | msg = c2.each_byte.map.with_index { |b, i| b ^ t[i] }.pack("C*") 82 | 83 | digest = OpenSSL::Digest.digest("SM3", x2 + msg + y2) 84 | raise ArgumentError, "Digest no match" if c3 != digest 85 | 86 | msg 87 | end 88 | 89 | # get public key from private key 90 | # 91 | # @param private_key [String] private key, format: binary string 92 | # @return [String] public key, format: binary string 93 | def get_public_key(private_key) 94 | pkey = OpenSSL::BN.new(private_key, 2) 95 | group = OpenSSL::PKey::EC::Group.new("SM2") 96 | group.generator.mul(pkey).to_octet_string(:uncompressed) 97 | end 98 | 99 | # sign with private key 100 | # 101 | # @param private_key [String] private key, format: binary string 102 | # @param data [String] 103 | # @param sm3_hash [Boolean], option to sign with sm3 hash, default: false 104 | # @param user_id [String], format: hex string, default: "31323334353637383132333435363738" which is equal to utf-8 str "1234567812345678" 105 | # @param asn1 [Boolean], option to return asn.1 der format signature, default: false 106 | # @return [String] signature, format: hex string 107 | def sign(private_key, data, sm3_hash: false, user_id: "31323334353637383132333435363738", asn1: false) 108 | data = data.unpack1("a*") unless data.ascii_only? 109 | if sm3_hash 110 | public_key = get_public_key(private_key) 111 | data = OpenSSL::Digest.digest("SM3", za(public_key, user_id) + data) 112 | end 113 | 114 | n = OpenSSL::BN.new("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16) 115 | da = OpenSSL::BN.new(private_key, 2) 116 | e = OpenSSL::BN.new(data, 2) 117 | one = OpenSSL::BN.new(1) 118 | 119 | k = 0 120 | s = 0 121 | r = 0 122 | while s.zero? 123 | while r.zero? || r + k == n 124 | random_key = OpenSSL::PKey::EC.generate("SM2") 125 | k = random_key.private_key 126 | x1 = OpenSSL::BN.new(random_key.public_key.to_octet_string(:uncompressed)[1, 32], 2) 127 | # r = (e + x1) mod n 128 | r = (e + x1) % n 129 | end 130 | # s = ((1 + dA)^-1 * (k - r * dA)) mod n 131 | s = ((one + da).mod_inverse(n) * (k - (r * da))).to_i % n.to_i 132 | end 133 | 134 | if asn1 135 | OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::Integer.new(r), OpenSSL::ASN1::Integer.new(s)]).to_der.unpack1("H*") 136 | else 137 | r.to_s(16).rjust(64, "0") + s.to_s(16).rjust(64, "0") 138 | end 139 | end 140 | 141 | # verify the signature with public_key 142 | # 143 | # @param public_key [String] public key, format: binary string 144 | # @param data [String] 145 | # @param signature [String], hex string 146 | # @param sm3_hash [Boolean], option to sign with sm3 hash, default: false 147 | # @param user_id [String], format: hex string, default: "31323334353637383132333435363738" 148 | # @param asn1 [Boolean], option to verify asn.1 der format signature, default: false 149 | # @return [Boolean] verify result 150 | def verify(public_key, data, signature, sm3_hash: false, user_id: "31323334353637383132333435363738", asn1: false) 151 | if asn1 152 | return false if signature.size < 136 153 | 154 | # parse asn1 der format hex string signature 155 | der_seq = OpenSSL::ASN1.decode([signature].pack("H*")) 156 | r = der_seq.value[0].value 157 | s = der_seq.value[1].value 158 | else 159 | return false if signature.size != 128 160 | 161 | r = OpenSSL::BN.new(signature[0, 64], 16) 162 | s = OpenSSL::BN.new(signature[64, 64], 16) 163 | end 164 | 165 | public_key = "\x04#{public_key}" if public_key.size == 64 && public_key[0] != "\x04" 166 | data = data.unpack1("a*") unless data.ascii_only? 167 | if sm3_hash 168 | data = OpenSSL::Digest.digest("SM3", za(public_key, user_id) + data) 169 | end 170 | n = OpenSSL::BN.new("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16) 171 | e = OpenSSL::BN.new(data, 2) 172 | 173 | # t = (r + s) mod n 174 | t = (r + s) % n 175 | return false if t.zero? 176 | 177 | point = OpenSSL::PKey::EC::Point.new(OpenSSL::PKey::EC::Group.new("SM2"), OpenSSL::BN.new(public_key, 2)) 178 | 179 | # x1y1 = s * G + t * PA 180 | x1y1 = point.mul(t, s) 181 | x1 = OpenSSL::BN.new(x1y1.to_octet_string(:uncompressed)[1, 32], 2) 182 | 183 | # R = (e + x1) mod n 184 | r1 = (e + x1) % n 185 | 186 | r == r1 187 | end 188 | 189 | # ZA = H256(ENTLA || IDA || a || b || gx || gy || px || py) 190 | def za(public_key, user_id) 191 | ida = [user_id].pack("H*") 192 | entla = [ida.size * 8].pack("n") 193 | a = ["FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC"].pack("H*") 194 | b = ["28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93"].pack("H*") 195 | gxgy = OpenSSL::PKey::EC::Group.new("SM2").generator.to_octet_string(:uncompressed)[1, 64] 196 | public_key = public_key[1, 64] if public_key.size == 65 197 | 198 | OpenSSL::Digest.digest("SM3", entla + ida + a + b + gxgy + public_key) 199 | end 200 | end 201 | -------------------------------------------------------------------------------- /sm2-crypto.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Gem::Specification.new do |spec| 4 | spec.name = "sm2-crypto" 5 | spec.version = "0.2.2" 6 | spec.authors = ["Seekr"] 7 | spec.email = ["wzhao23@gmail.com"] 8 | 9 | spec.summary = "An SM2 cryptographic algorithm encryption and decryption library for Ruby" 10 | spec.description = "sm2-crypto is an implementation of the SM2 encryption and decryption algorithm in pure Ruby based on the OpenSSL" 11 | spec.homepage = "https://github.com/numbcoder/sm2-crypto" 12 | spec.license = "MIT" 13 | spec.required_ruby_version = ">= 2.7.0" 14 | 15 | spec.metadata["rubygems_mfa_required"] = "true" 16 | spec.metadata["homepage_uri"] = spec.homepage 17 | spec.metadata["source_code_uri"] = spec.homepage 18 | 19 | # Specify which files should be added to the gem when it is released. 20 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 21 | spec.files = Dir.chdir(__dir__) do 22 | `git ls-files -z`.split("\x0").reject do |f| 23 | (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)}) 24 | end 25 | end 26 | spec.bindir = "exe" 27 | spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } 28 | spec.require_paths = ["lib"] 29 | end 30 | -------------------------------------------------------------------------------- /test/test_sm2_crypto.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "minitest/autorun" 4 | require "sm2_crypto" 5 | require "base64" 6 | require "securerandom" 7 | 8 | class SM2CryptoTest < Minitest::Test 9 | SM2_PRIVATE_KEY = ["7DF1CBE97D602CDF2CD5005A102E95EC0F3B50045EFBAEA2E8364B47C33C585A"].pack("H*") 10 | SM2_PUBLIC_KEY = ["0456B6D322DA7114E7989927DC276D627530A6377F209D7F02C23C5DBB27173F1099667FBC95BF50172F03F45E01A83E293B32664FB425801888C84ABB7677975F"].pack("H*") 11 | 12 | SM2_DATA = "abc1234XYZ@%& 你好,世界!" 13 | SM2_ENCRYPTED_DATA = "BFTtp/3xFw25H/JV9KQ4z8U9GMoIpfWSoyuPtn387bSxWbp8zcjoxURo5pqZb55VzSrAypcmM32WqKjJLJRqUaj/JTG+7IhRm9wFkAbqIQTbMkB89/6AWYIM8zL72mQUGS9s6vQS2dewYsbuYWZzH512ipRY743zh52lIyMof/ik" 14 | 15 | def test_encrypt_and_decrypt 16 | encrypted_data = SM2Crypto.encrypt(SM2_PUBLIC_KEY, SM2_DATA) 17 | decrypted_data = SM2Crypto.decrypt(SM2_PRIVATE_KEY, encrypted_data) 18 | assert_equal SM2_DATA, decrypted_data.force_encoding("UTF-8") 19 | 20 | decrypted_data2 = SM2Crypto.decrypt(SM2_PRIVATE_KEY, ::Base64.decode64(SM2_ENCRYPTED_DATA)) 21 | assert_equal SM2_DATA.bytes, decrypted_data2.bytes 22 | end 23 | 24 | def test_option_cipher_mode 25 | encrypted_data = SM2Crypto.encrypt(SM2_PUBLIC_KEY, SM2_DATA, cipher_mode: 0) 26 | decrypted_data = SM2Crypto.decrypt(SM2_PRIVATE_KEY, encrypted_data, cipher_mode: 0) 27 | assert_equal SM2_DATA, decrypted_data.force_encoding("UTF-8") 28 | end 29 | 30 | def test_encrypt_and_decrypt_long_data 31 | msg = SecureRandom.alphanumeric(rand(1000..10_000)) 32 | encrypted_data = SM2Crypto.encrypt(SM2_PUBLIC_KEY, msg) 33 | decrypted_data = SM2Crypto.decrypt(SM2_PRIVATE_KEY, encrypted_data) 34 | assert_equal msg, decrypted_data 35 | end 36 | 37 | def test_encrypt_and_decrypt_with_padding 38 | # public_key not start with "\x04" 39 | public_key = ["56B6D322DA7114E7989927DC276D627530A6377F209D7F02C23C5DBB27173F1099667FBC95BF50172F03F45E01A83E293B32664FB425801888C84ABB7677975F"].pack("H*") 40 | encrypted_data = SM2Crypto.encrypt(public_key, SM2_DATA) 41 | decrypted_data = SM2Crypto.decrypt(SM2_PRIVATE_KEY, encrypted_data) 42 | assert_equal SM2_DATA, decrypted_data.force_encoding("UTF-8") 43 | 44 | # encrypted_data not start with "\x04" 45 | decrypted_data2 = SM2Crypto.decrypt(SM2_PRIVATE_KEY, encrypted_data[1, encrypted_data.size - 1]) 46 | assert_equal SM2_DATA, decrypted_data2.force_encoding("UTF-8") 47 | end 48 | 49 | def test_generate_keypairs 50 | keypair = OpenSSL::PKey::EC.generate("SM2") 51 | private_key = keypair.private_key.to_s(2) 52 | public_key = keypair.public_key.to_octet_string(:uncompressed) 53 | 54 | msg = SecureRandom.random_bytes(rand(10..100)) 55 | encrypted_data = SM2Crypto.encrypt(public_key, msg) 56 | decrypted_data = SM2Crypto.decrypt(private_key, encrypted_data) 57 | assert_equal msg, decrypted_data 58 | end 59 | 60 | def test_base64 61 | private_key = Base64.decode64("1F2NPo0cF9grhuBNIvH+9vpAC/itfBZjuE2uIYhmaFE=\n") 62 | msg = "abc" 63 | encrypted_data = "BKpGCI/L1mrP0iFDbmB/ykCWWUnrC+5OuXa7/nyFi86yz33NhVvR+JrPml1fLVISFNsMennZNCMZ4uN4mzngBMhldVQJwszbmYys3d5fZ+EJ7ZJXz1uWRKwE7on574SbTCc5zw==" 64 | decrypted_data = SM2Crypto.decrypt(private_key, Base64.decode64(encrypted_data)) 65 | assert_equal msg, decrypted_data 66 | end 67 | 68 | def test_get_publick_key 69 | public_key = SM2Crypto.get_public_key(SM2_PRIVATE_KEY) 70 | assert_equal SM2_PUBLIC_KEY, public_key 71 | 72 | keypair = OpenSSL::PKey::EC.generate("SM2") 73 | private_key = keypair.private_key.to_s(2) 74 | public_key = keypair.public_key.to_bn.to_s(2) 75 | assert_equal public_key, SM2Crypto.get_public_key(private_key) 76 | end 77 | 78 | def test_sign_and_verify 79 | msg = "abc" 80 | sign = SM2Crypto.sign(SM2_PRIVATE_KEY, msg) 81 | assert_equal true, SM2Crypto.verify(SM2_PUBLIC_KEY, msg, sign) 82 | 83 | cjk_msg = "abc1234XYZ@%& 你好,世界!#{SecureRandom.alphanumeric(rand(1000..10_000))}" 84 | sign2 = SM2Crypto.sign(SM2_PRIVATE_KEY, cjk_msg) 85 | assert_equal true, SM2Crypto.verify(SM2_PUBLIC_KEY, cjk_msg, sign2) 86 | end 87 | 88 | def test_sign_and_verify_with_hash 89 | msg = "abc" 90 | user_id = "313233343536373831323334353637AA" 91 | sign = SM2Crypto.sign(SM2_PRIVATE_KEY, msg, sm3_hash: true, user_id: user_id) 92 | assert_equal true, SM2Crypto.verify(SM2_PUBLIC_KEY, msg, sign, sm3_hash: true, user_id: user_id) 93 | 94 | cjk_msg = "abc1234XYZ@%& 你好,世界!#{SecureRandom.alphanumeric(rand(1000..10_000))}" 95 | sign2 = SM2Crypto.sign(SM2_PRIVATE_KEY, cjk_msg, sm3_hash: true) 96 | assert_equal true, SM2Crypto.verify(SM2_PUBLIC_KEY, cjk_msg, sign2, sm3_hash: true) 97 | end 98 | 99 | def test_sign_and_verify_with_asn1 100 | 30.times do 101 | msg = SecureRandom.alphanumeric(rand(1..100)) 102 | sign = SM2Crypto.sign(SM2_PRIVATE_KEY, msg, asn1: true) 103 | verified = SM2Crypto.verify(SM2_PUBLIC_KEY, msg, sign, asn1: true) 104 | assert_equal true, verified 105 | end 106 | end 107 | 108 | def test_sign_and_verify_with_asn1_and_hash 109 | msg = "z0I15DTbsWjF" 110 | signs = %w[ 111 | 304402203bd708d89b9728af3939f77df68bd4c2bc3038cb5de92bfdfd11468165caa87302204136f5345177d1eb663e33c18922fed5440dec0cc68b3c39def76b87ef77d177 112 | 3045022100e84b35c282ec4afbf337fff6e978c99fd3126745f436f8abd3cc897b0c784d160220482aecc68fe47334385ac98af508ea589b11d0d22d4ac065d663bd1034eafd38 113 | 3045022100f10f4403dc670ceb30ae7b15c9ea06f01274fc8c796060ea39ae3d0eab37e310022049eef876e073d0595c6444cc3b23917948da706755a6a38a1f23e3841c67a279 114 | 3046022100f9762c583bef7331a7a16295d98c3413fa4fd7e579ace06842956ca9ce385752022100819eac8da0b92c5babacad868f3fe7af118bf1cbbc59c2629f54e8db925c7e4e 115 | ] 116 | signs.each do |sign| 117 | assert_equal true, SM2Crypto.verify(SM2_PUBLIC_KEY, msg, sign, sm3_hash: true, asn1: true) 118 | end 119 | end 120 | end 121 | --------------------------------------------------------------------------------