Skip to content

Commit d4e99c3

Browse files
committed
feat(app): support sm2 signature hex format
1 parent 6e2ee6f commit d4e99c3

3 files changed

Lines changed: 80 additions & 21 deletions

File tree

app/src/main/kotlin/me/leon/ext/crypto/SM.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ package me.leon.ext.crypto
33
import java.security.*
44
import java.security.spec.PKCS8EncodedKeySpec
55
import java.security.spec.X509EncodedKeySpec
6+
import me.leon.ext.hex2ByteArray
67
import org.bouncycastle.asn1.gm.GMNamedCurves
78
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
89
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
910
import org.bouncycastle.asn1.x9.ECNamedCurveTable
1011
import org.bouncycastle.crypto.CipherParameters
1112
import org.bouncycastle.crypto.engines.SM2Engine
1213
import org.bouncycastle.crypto.params.*
14+
import org.bouncycastle.crypto.signers.SM2Signer
1315
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util
1416
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil
1517
import org.bouncycastle.jcajce.spec.OpenSSHPrivateKeySpec
@@ -19,8 +21,10 @@ import org.bouncycastle.jce.interfaces.ECPublicKey
1921
import org.bouncycastle.jce.provider.BouncyCastleProvider
2022
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec
2123
import org.bouncycastle.util.BigIntegers
24+
import org.bouncycastle.util.Strings
2225

2326
private val x9ECParameters = GMNamedCurves.getByName("sm2p256v1")
27+
private val USER_ID = Strings.toByteArray("1234567812345678")
2428
private val ecDomainParameters =
2529
ECDomainParameters(x9ECParameters.curve, x9ECParameters.g, x9ECParameters.n, x9ECParameters.h)
2630

@@ -58,6 +62,33 @@ fun ByteArray.toECPublicKeyParams(): AsymmetricKeyParameter {
5862
return generatePublicKeyParameter(public)
5963
}
6064

65+
fun ByteArray.sm2Sign(privateKeyHex: String, userId: ByteArray = USER_ID): ByteArray {
66+
val sm2Signer = SM2Signer()
67+
val param = privateKeyHex.hex2ByteArray().toECPrivateKeyParams()
68+
69+
sm2Signer.init(
70+
true,
71+
ParametersWithID(ParametersWithRandom(param, SecureRandom.getInstance("SHA1PRNG")), userId),
72+
)
73+
sm2Signer.update(this, 0, this.size)
74+
return sm2Signer.generateSignature()
75+
}
76+
77+
fun ByteArray.sm2Verify(publicKey: String, signedHex: String) =
78+
sm2Verify(publicKey, signedHex.hex2ByteArray())
79+
80+
fun ByteArray.sm2Verify(
81+
publicKey: String,
82+
signed: ByteArray,
83+
userId: ByteArray = USER_ID,
84+
): Boolean {
85+
val sm2Signer = SM2Signer()
86+
val param = publicKey.hex2ByteArray().toECPublicKeyParams()
87+
sm2Signer.init(false, ParametersWithID(param, userId))
88+
sm2Signer.update(this, 0, this.size)
89+
return sm2Signer.verifySignature(signed)
90+
}
91+
6192
private fun generatePrivateKeyParameter(key: PrivateKey): AsymmetricKeyParameter =
6293
when (key) {
6394
is ECPrivateKey -> {
@@ -82,10 +113,12 @@ private fun generatePrivateKeyParameter(key: PrivateKey): AsymmetricKeyParameter
82113
ECPrivateKeyParameters(key.d, ECDomainParameters(s!!.curve, s.g, s.n, s.h, s.seed))
83114
}
84115
}
116+
85117
is java.security.interfaces.ECPrivateKey -> {
86118
val s = EC5Util.convertSpec(key.params)
87119
ECPrivateKeyParameters(key.s, ECDomainParameters(s.curve, s.g, s.n, s.h, s.seed))
88120
}
121+
89122
else -> {
90123
// see if we can build a key from key.getEncoded()
91124
val bytes = key.encoded
@@ -100,13 +133,15 @@ private fun generatePublicKeyParameter(key: PublicKey): AsymmetricKeyParameter =
100133
val s = key.parameters
101134
ECPublicKeyParameters(key.q, ECDomainParameters(s.curve, s.g, s.n, s.h, s.seed))
102135
}
136+
103137
is java.security.interfaces.ECPublicKey -> {
104138
val s = EC5Util.convertSpec(key.params)
105139
ECPublicKeyParameters(
106140
EC5Util.convertPoint(key.params, key.w),
107141
ECDomainParameters(s.curve, s.g, s.n, s.h, s.seed),
108142
)
109143
}
144+
110145
else -> {
111146
// see if we can build a key from key.getEncoded()
112147
val bytes = key.encoded

app/src/main/kotlin/me/leon/ext/crypto/Signature.kt

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,41 @@
11
package me.leon.ext.crypto
22

3-
import java.security.*
3+
import java.security.KeyPair
4+
import java.security.KeyPairGenerator
5+
import java.security.SecureRandom
6+
import java.security.Signature
47
import java.security.spec.ECGenParameterSpec
5-
import me.leon.encode.base.base64
68
import me.leon.ext.catch
79

810
private fun String.properKeyPairAlg() = takeUnless { it.equals("SM2", true) } ?: "EC"
911

1012
fun ByteArray.sign(kpAlg: String, sigAlg: String, pri: String): ByteArray =
11-
Signature.getInstance(sigAlg.properKeyPairAlg())
12-
.apply {
13-
initSign(pri.toPrivateKey(kpAlg))
14-
update(this@sign)
15-
}
16-
.sign()
13+
if (kpAlg == "SM2" && HEX_REGEX.matches(pri)) {
14+
sm2Sign(pri)
15+
} else {
16+
Signature.getInstance(sigAlg.properKeyPairAlg())
17+
.apply {
18+
initSign(pri.toPrivateKey(kpAlg))
19+
update(this@sign)
20+
}
21+
.sign()
22+
}
1723

1824
fun ByteArray.verify(kpAlg: String, sigAlg: String, pub: String, signed: ByteArray) =
1925
catch({
2026
println("verify err $it")
2127
false
2228
}) {
23-
Signature.getInstance(sigAlg.properKeyPairAlg())
24-
.apply {
25-
initVerify(pub.toPublicKey(kpAlg))
26-
update(this@verify)
27-
}
28-
.verify(signed)
29+
if (kpAlg == "SM2" && HEX_REGEX.matches(pub)) {
30+
sm2Verify(pub, signed)
31+
} else {
32+
Signature.getInstance(sigAlg.properKeyPairAlg())
33+
.apply {
34+
initVerify(pub.toPublicKey(kpAlg))
35+
update(this@verify)
36+
}
37+
.verify(signed)
38+
}
2939
}
3040

3141
val ecdsaCurveMap = mapOf("ES256" to "secp256r1", "ES384" to "secp384r1", "ES512" to "secp521r1")
@@ -36,10 +46,3 @@ fun generateEcKeyPair(jwtAlg: String = "ES256"): KeyPair? {
3646
kpg.initialize(spec, SecureRandom())
3747
return kpg.generateKeyPair()
3848
}
39-
40-
fun main() {
41-
generateEcKeyPair()?.let {
42-
println(it.public.encoded.base64())
43-
println(it.private.encoded.base64())
44-
}
45-
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package me.leon.sign
2+
3+
import kotlin.test.Test
4+
import me.leon.ext.crypto.sm2Sign
5+
import me.leon.ext.crypto.sm2Verify
6+
import me.leon.ext.toHex
7+
import kotlin.test.assertTrue
8+
9+
class SmSignatureTest {
10+
val pub =
11+
"042b515d98e00cd3f5770e20ba69998da6f9637085a0c89dec0a5dec7771fa4d0569a68a3d" +
12+
"eba36ee5ffd89391e62d775551f6d69de7d155f0a3fdcbacb0631ea9"
13+
val pri = "7ac2d094b66dbde1170875190bca025ed38c8293dabad39e00692318ca624e60"
14+
15+
@Test
16+
fun smSignatureTest() {
17+
val text = "Hello, SM2!"
18+
val sign = text.toByteArray().sm2Sign(pri).toHex()
19+
assertTrue(text.toByteArray().sm2Verify(pub, sign))
20+
}
21+
}

0 commit comments

Comments
 (0)