ECDSA signature verify in kotlin and Golang
Introduction
Elliptic Curve Digital Signature Algorithm (ECDSA) offers a variant of the Digital Signature Algorithm (DSA) which uses elliptic curve cryptography.
ECDSA keys and signatures are shorter than in RSA for the same security level. A 256-bit ECDSA signature has the same security strength like 3072-bit RSA signature.
The ECDSA signing algorithm RFC 6979
takes as input a message msg
+ a private key privKey and produces as output a signature
, which consists of pair of integers {r, s}
.
ECDSA signatures are 2 times longer than the signer’s private key for the curve used during the signing process. For example, for 256-bit elliptic curves (like secp256k1) the ECDSA signature is 512 bits (64 bytes) and for 521-bit curves (like secp521r1) the signature is 1042 bits.
Generate signature in Kotlin
In Android app, ECDSA is supported by default.
Generate a ECDSA key pair and sign
class EcdsaSha256 {
companion object {
private const val KEYPIRE_ALIAS = "MY_ECDSA_KEY_PAIR"
}
/**
* sign `message` with `SHA256withECDSA`.
*
* @return base64 encoded ASN.1 signature, or "" if can not get key pair.
*/
fun sign(message: String): String {
val entry = getKeyPair() ?: return ""
val signature = Signature.getInstance("SHA256withECDSA")
signature.initSign(entry.privateKey)
signature.update(message.toByteArray())
return Base64.encodeToString(signature.sign(), Base64.NO_WRAP)
}
private fun generateKeyPair(): KeyPair {
val keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore")
keyPairGenerator.initialize(KeyGenParameterSpec.Builder(KEYPIRE_ALIAS,
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setKeySize(256)
.build())
return keyPairGenerator.generateKeyPair()
}
private fun getKeyPair(): KeyStore.PrivateKeyEntry? {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
var res = keyStore.getEntry(KEYPIRE_ALIAS, null)
if (res == null) {
generateKeyPair()
keyStore.load(null)
res = keyStore.getEntry(KEYPIRE_ALIAS, null)
}
return res as? KeyStore.PrivateKeyEntry
}
}
Sample SHA256 with ECDSA sign:
ecdsaSha256 = EcdsaSha256()
val message = "To be, or not to be, that is the question."
signature = ecdsaSha256.sign(message)
Log.d(TAG, "publicKey: $publicKey")
Log.d(TAG, "message: $message")
Log.d(TAG, "signature: $signature")
Log output:
publicKey: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbUTlq/bTW47fORzMskrc8+wNV3ekCX1WNKJ03XOjHzOFecniFAdDaRxluAl/osB5LH6dj8Rl4InJC/vEtFZ7dA==
message: To be, or not to be, that is the question.
signature: MEUCIQC0YJirr4yM0JEmprRJfYA/7lVUohR4qEMbiJtwzhm9RwIgS3YkgWyNuMCh6o0FQXliW6xhjEYZT/cTmqsdpNH+YB0=
The signature format is ASN.1 structure message defined in RFC 5480: Elliptic Curve Cryptography Subject Public Key Information :
ECDSA-Sig-Value ::= SEQUENCE {
r INTEGER,
s INTEGER
}
We will see how to parse it in Go in next section.
Verify Signature in Golang
Golang have ecdsa package implements the Elliptic Curve Digital Signature Algorithm.
To verify ECDSA signature in Golang, first need load public key from string:
func loadECPublicKey2(base64PublicKey string) (*ecdsa.PublicKey, error) {
publicKeyBytes, err := base64.StdEncoding.DecodeString(base64PublicKey)
if err != nil {
panic(err)
}
pub, err := x509.ParsePKIXPublicKey(publicKeyBytes)
if err != nil {
return nil, errors.New("Failed to parse ECDSA public key")
}
publicKey, ok := pub.(*ecdsa.PublicKey)
if !ok {
return nil, errors.New("Not a ECDSA public key")
}
return publicKey, nil
}
Note
ECDSA sign is based on hash, so it need first calculatemessage
hash.There are two APIs in ecdsa :
- ecdsa.Verify()
verifies the signature in
r
,s
of hash using the public key,pub
. - ecdsa.VerifyASN1()
verifies the ASN.1 encoded signature, sig, of hash using the public key,
pub
.
ecdsa.Verify()
ecdsa.Verify()
need decode signature to get r
and s
first:
type ECDSASignature struct {
R, S *big.Int
}
// ecdsaVerify1 use `ecdsa.Verify()` to verify signature
func ecdsaVerify1(base64PublicKey string, message string, base64Signature string) bool {
ecPublicKey, err := loadECPublicKey2(base64PublicKey)
if err != nil {
panic(err)
}
hash := sha256.Sum256([]byte(message))
sigBytes, err := base64.StdEncoding.DecodeString(base64Signature)
if err != nil {
panic(err)
}
sig := &ECDSASignature{}
if _, err = asn1.Unmarshal(sigBytes, sig); err != nil {
panic(err)
}
return ecdsa.Verify(ecPublicKey, hash[:], sig.R, sig.S)
}
Usage example:
match := ecdsaVerify1(
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbUTlq/bTW47fORzMskrc8+wNV3ekCX1WNKJ03XOjHzOFecniFAdDaRxluAl/osB5LH6dj8Rl4InJC/vEtFZ7dA==",
"To be, or not to be, that is the question.",
"MEUCIQC0YJirr4yM0JEmprRJfYA/7lVUohR4qEMbiJtwzhm9RwIgS3YkgWyNuMCh6o0FQXliW6xhjEYZT/cTmqsdpNH+YB0=",
)
fmt.Printf("ecdsaVerify1() return %v\n", match)
ecdsa.VerifyASN1()
ecdsa.VerifyASN1()
is a simple way to verify ASN.1 encoded signature, you donot need manually decode signature to get r
and s
:
// ecdsaVerify2 use `ecdsa.VerifyASN1()` to verify signature
func ecdsaVerify2(base64PublicKey string, message string, base64Signature string) bool {
ecPublicKey, err := loadECPublicKey2(base64PublicKey)
if err != nil {
panic(err)
}
hash := sha256.Sum256([]byte(message))
sigBytes, err := base64.StdEncoding.DecodeString(base64Signature)
if err != nil {
panic(err)
}
return ecdsa.VerifyASN1(ecPublicKey, hash[:], sigBytes)
}
Usage example:
match := ecdsaVerify2(
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbUTlq/bTW47fORzMskrc8+wNV3ekCX1WNKJ03XOjHzOFecniFAdDaRxluAl/osB5LH6dj8Rl4InJC/vEtFZ7dA==",
"To be, or not to be, that is the question.",
"MEUCIQC0YJirr4yM0JEmprRJfYA/7lVUohR4qEMbiJtwzhm9RwIgS3YkgWyNuMCh6o0FQXliW6xhjEYZT/cTmqsdpNH+YB0=",
)
fmt.Printf("ecdsaVerify2() return %v\n", match)
Related pages:
- Jailbreak iPhone 8 iOS 16.2 with palera1n and use frida dump to decrypt ipa
- Use frida and objection to penetration test iOS app security
- OpenSSL CSR Examples: Self Signed Certificate and How to Start Test TLS/SSL Server/Client
- testssl.sh examples command line tool check server TLS/SSL (weak) ciphers and detect TLS/SSL vulnerabilities
- Test TLS Connection Ciphers TLS Version and Certificate with OpenSSL Command Line
- Running a DoH Client to encrypt all home DNS traffic
- Secure Squid Proxy Server
References
- Elliptic Curve Digital Signature Algorithm
- FIPS PUB 186-4: Digital Signature Standard (DSS)
- ECDSA: Elliptic Curve Signatures
- RFC 5480: Elliptic Curve Cryptography Subject Public Key Information
- RFC 6979: Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)
OmniLock - Block / Hide App on iOS
Block distractive apps from appearing on the Home Screen and App Library, enhance your focus and reduce screen time.
DNS Firewall for iOS and Mac OS
Encrypted your DNS to protect your privacy and firewall to block phishing, malicious domains, block ads in all browsers and apps