ECDSA Signing and Verifying Examples

Photo by Babak Fakhamzadeh on Unsplash
Photo by Babak Fakhamzadeh on Unsplash
Elliptic Curve Digital Signature Algorithm (ECDSA) is a digital signature algorithm (DSA) commonly used for digital signatures. This article will introduce how to use ECDSA to sign and verify in Android and iOS.

Elliptic Curve Digital Signature Algorithm (ECDSA) is a digital signature algorithm (DSA) commonly used for digital signatures. This article will introduce how to use ECDSA to sign and verify in Android and iOS.

The complete Android code for this chapter can be found in .

The complete iOS code for this chapter can be found in .

Digital Signature

Sometimes we need to verify whether the data received from the server is really sent by the server, or if someone else pretends to be the server and sends the data to us. In order to solve this problem, the server will send an additional verification code when sending data. When the client receives the data and verification code, the client will use a specific algorithm to convert the data into a string, and then compare the string with the verification code. If they are the same, we can be sure that the data is sent by the server and claim the data is safe.

Digital signature basically defines the above process and provides a reliable algorithm to prevent others from forging verification codes. The process of generating verification codes on the server side is called signing. First, the server must first process the data with a specific hash function, which will produce a relatively short and fixed-length hash value. Then, this hash value and a private key are input into a specific DSA, and finally it will generate a string of verification codes, called a signature.

Digital Signature - Signing.
Digital Signature – Signing.

The client receives the data and signature passed from the server. It processes the data with a specific hash function and obtains a hash value. Enter the signature and a public key into a specific DSA and it will generate a hash value. Finally, compare the two hash values ​​to see if they are the same. This process is called verifying.

Digital Signature - Verifying.
Digital Signature – Verifying.

ECDSA Signature

ECDSA is a DSA commonly used in recent years. When using ECDSA, the server and client should agree on the same curve parameters. In the example of this article, we choose secp256r1. SEC secp256r1, NIST P-256, and ANSI X9.64 ansix9p256r1 are all the same. It’s just that they are defined by different agencies, please refer to here.

When choosing to use ECDSA and secp256r1, the signing process will be as follows:

ECDSA - Signing.
ECDSA – Signing.

However, the verification process is as follows:

ECDSA - Verification.
ECDSA – Verification.

Example in Kotllin

ECC Keys Generation

import android.util.Base64
import java.security.KeyPairGenerator
import java.security.interfaces.ECPrivateKey
import java.security.interfaces.ECPublicKey
import java.security.spec.ECGenParameterSpec

var privateKeyBase64 = ""
var publicKeyBase64 = ""
var message = "Hello Wayne's Talk!"
var signatureBase64 = ""

fun generateKeys() {
    val keyPairGenerator = KeyPairGenerator.getInstance("EC")
    val ecGenParameterSpec = ECGenParameterSpec("secp256r1")
    keyPairGenerator.initialize(ecGenParameterSpec)

    val keyPair = keyPairGenerator.generateKeyPair()
    val privateKey = keyPair.private as ECPrivateKey
    val publicKey = keyPair.public as ECPublicKey

    privateKeyBase64 = Base64.encodeToString(privateKey.encoded, Base64.NO_WRAP)
    publicKeyBase64 = Base64.encodeToString(publicKey.encoded, Base64.NO_WRAP)

    println("Private Key: $privateKeyBase64")
    println("Private Key Format: ${privateKey.format}")
    println("Public Key: $publicKeyBase64")
    println("Public Key Format: ${publicKey.format}")
}

Signing

import android.util.Base64
import java.security.KeyFactory
import java.security.Signature
import java.security.spec.PKCS8EncodedKeySpec

fun sign() {
    val privateKeyBytes = Base64.decode(privateKeyBase64, Base64.NO_WRAP);
    val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(privateKeyBytes)
    val keyFactory = KeyFactory.getInstance("EC")
    val privateKey = keyFactory.generatePrivate(pkcS8EncodedKeySpec)

    val signature = Signature.getInstance("SHA256withECDSA")
    signature.initSign(privateKey)

    signature.update(message.encodeToByteArray())

    val signatureBytes = signature.sign()
    signatureBase64 = Base64.encodeToString(signatureBytes, Base64.NO_WRAP)

    println("Message: $message")
    println("Signature: $signatureBase64")
}

Verifying

import android.util.Base64
import java.security.KeyFactory
import java.security.Signature
import java.security.spec.X509EncodedKeySpec

fun verify() {
    val publicKeyBytes = Base64.decode(publicKeyBase64, Base64.NO_WRAP)
    val x509EncodedKeySpec = X509EncodedKeySpec(publicKeyBytes)
    val keyFactory = KeyFactory.getInstance("EC")
    val publicKey = keyFactory.generatePublic(x509EncodedKeySpec)

    val signature = Signature.getInstance("SHA256withECDSA")
    signature.initVerify(publicKey)

    signature.update(message.encodeToByteArray())

    val signatureBytes = Base64.decode(signatureBase64, Base64.NO_WRAP)
    val isValid = signature.verify(signatureBytes)
    println("Signature is ${if (isValid) "valid" else "inValid"}")
}

Example in Swift

ECC Keys Generation

import CryptoKit

private var privateKeyBase64 = ""
private var publicKeyBase64 = ""
private var signagureBase64 = ""
private let message = "Hello Wayne's Talk!"

func generateKeys() {
    let privateKey = P256.Signing.PrivateKey()
    let publicKey = privateKey.publicKey
    
    privateKeyBase64 = privateKey.derRepresentation.base64EncodedString()
    publicKeyBase64 = publicKey.derRepresentation.base64EncodedString()
    
    print("Private Key: (privateKeyBase64)")
    print("Public Key: (publicKeyBase64)")
}

Signing

func sign() {
    let privateKeyData = Data(base64Encoded: privateKeyBase64)!
    let privateKey = try! P256.Signing.PrivateKey(derRepresentation: privateKeyData)
    let signature = try! privateKey.signature(for: message.data(using: .utf8)!)
    signatureBase64 = signature.derRepresentation.base64EncodedString()
    
    print("Signature: (signatureBase64)")
}

Verifying

func verify() {
    let publicKeyData = Data(base64Encoded: publicKeyBase64)!
    let publicKey = try! P256.Signing.PublicKey(derRepresentation: publicKeyData)
    
    var sha256 = SHA256()
    sha256.update(data: message.data(using: .utf8)!)
    let messageData = sha256.finalize()
    
    let signatureData = Data(base64Encoded: signatureBase64)!
    let signature = try! P256.Signing.ECDSASignature(derRepresentation: signatureData)
    let isValid = publicKey.isValidSignature(signature, for: messageData)
    print("Signature is (isValid ? "valid" : "invalid")")
}

Conclusion

Under the same key length, ECDSA provides a higher security level than RSA. The use of ECDSA has increased significantly in recent years. Android and iOS do not have all curve parameters built-in. Therefore, when selecting curve parameters, you must first confirm whether the device has built-in curve parameters. Otherwise, it is better to use a third-party package to be safer.

Reference

Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like