Skip to content

Commit ea8fab4

Browse files
committed
Enhance the SM2Crypto signature and verification features, supporting ASN.1 DER format.
1 parent d020a1c commit ea8fab4

File tree

2 files changed

+49
-7
lines changed

2 files changed

+49
-7
lines changed

lib/sm2_crypto.rb

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,10 @@ def get_public_key(private_key)
101101
# @param private_key [String] private key, format: binary string
102102
# @param data [String]
103103
# @param sm3_hash [Boolean], option to sign with sm3 hash, default: false
104-
# @param user_id [String], format: hex string, default: "31323334353637383132333435363738"
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
105106
# @return [String] signature, format: hex string
106-
def sign(private_key, data, sm3_hash: false, user_id: "31323334353637383132333435363738")
107+
def sign(private_key, data, sm3_hash: false, user_id: "31323334353637383132333435363738", asn1: false)
107108
data = data.unpack1("a*") unless data.ascii_only?
108109
if sm3_hash
109110
public_key = get_public_key(private_key)
@@ -130,7 +131,11 @@ def sign(private_key, data, sm3_hash: false, user_id: "3132333435363738313233343
130131
s = ((one + da).mod_inverse(n) * (k - (r * da))).to_i % n.to_i
131132
end
132133

133-
r.to_s(16).rjust(64, "0") + s.to_s(16).rjust(64, "0")
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
134139
end
135140

136141
# verify the signature with public_key
@@ -140,17 +145,28 @@ def sign(private_key, data, sm3_hash: false, user_id: "3132333435363738313233343
140145
# @param signature [String], hex string
141146
# @param sm3_hash [Boolean], option to sign with sm3 hash, default: false
142147
# @param user_id [String], format: hex string, default: "31323334353637383132333435363738"
148+
# @param asn1 [Boolean], option to verify asn.1 der format signature, default: false
143149
# @return [Boolean] verify result
144-
def verify(public_key, data, signature, sm3_hash: false, user_id: "31323334353637383132333435363738")
145-
return false if signature.size != 128
150+
def verify(public_key, data, signature, sm3_hash: false, user_id: "31323334353637383132333435363738", asn1: false)
151+
if asn1
152+
# return false if signature.size < 138
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
146164

147165
public_key = "\x04#{public_key}" if public_key.size == 64 && public_key[0] != "\x04"
148166
data = data.unpack1("a*") unless data.ascii_only?
149167
if sm3_hash
150168
data = OpenSSL::Digest.digest("SM3", za(public_key, user_id) + data)
151169
end
152-
r = OpenSSL::BN.new(signature[0, 64], 16)
153-
s = OpenSSL::BN.new(signature[64, 64], 16)
154170
n = OpenSSL::BN.new("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
155171
e = OpenSSL::BN.new(data, 2)
156172

test/test_sm2_crypto.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,30 @@ def test_sign_and_verify_with_hash
9595
sign2 = SM2Crypto.sign(SM2_PRIVATE_KEY, cjk_msg, sm3_hash: true)
9696
assert_equal true, SM2Crypto.verify(SM2_PUBLIC_KEY, cjk_msg, sign2, sm3_hash: true)
9797
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+
unless verified
105+
puts "msg: #{msg}"
106+
puts "sign: #{sign}"
107+
end
108+
assert_equal true, verified
109+
end
110+
end
111+
112+
def test_sign_and_verify_with_asn1_and_hash
113+
msg = "z0I15DTbsWjF"
114+
signs = %w[
115+
304402203bd708d89b9728af3939f77df68bd4c2bc3038cb5de92bfdfd11468165caa87302204136f5345177d1eb663e33c18922fed5440dec0cc68b3c39def76b87ef77d177
116+
3045022100e84b35c282ec4afbf337fff6e978c99fd3126745f436f8abd3cc897b0c784d160220482aecc68fe47334385ac98af508ea589b11d0d22d4ac065d663bd1034eafd38
117+
3045022100f10f4403dc670ceb30ae7b15c9ea06f01274fc8c796060ea39ae3d0eab37e310022049eef876e073d0595c6444cc3b23917948da706755a6a38a1f23e3841c67a279
118+
3046022100f9762c583bef7331a7a16295d98c3413fa4fd7e579ace06842956ca9ce385752022100819eac8da0b92c5babacad868f3fe7af118bf1cbbc59c2629f54e8db925c7e4e
119+
]
120+
signs.each do |sign|
121+
assert_equal true, SM2Crypto.verify(SM2_PUBLIC_KEY, msg, sign, sm3_hash: true, asn1: true)
122+
end
123+
end
98124
end

0 commit comments

Comments
 (0)