BouncyCastleSignatureVerifier.kt 2.84 KB
Newer Older
stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
1
2
3
4
5
package com.lunabeestudio.framework.crypto

import android.util.Base64
import com.lunabeestudio.framework.extension.removePublicKeyDecoration
import org.apache.commons.codec.binary.Base32
stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
6
7
8
import org.bouncycastle.asn1.ASN1EncodableVector
import org.bouncycastle.asn1.ASN1Integer
import org.bouncycastle.asn1.DERSequence
stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
9
import org.bouncycastle.jce.provider.BouncyCastleProvider
stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
10
import java.math.BigInteger
stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.nio.charset.StandardCharsets
import java.security.KeyFactory
import java.security.PublicKey
import java.security.Signature
import java.security.SignatureException
import java.security.spec.X509EncodedKeySpec

object BouncyCastleSignatureVerifier {
    private const val DEFAULT_PUB_KEY_ALGORITHM: String = "ECDSA"
    private const val DEFAULT_SIGNATURE_ALGORITHM: String = "SHA256withECDSA"

    /**
     * Verify the message against the provided signature
     *
     * @param rawPublicKey The raw public key encoded in base64
     * @param message The message to verify
     * @param rawSignature The raw signature used to verify the message (R & S components concatenated) encoded in base32
     * @param publicKeyAlgorithm The algorithm used to generate the public key
     * @param signatureKeyAlgorithm The algorithm used to generate the signature
     */
    @Throws(SignatureException::class)
    fun verifySignature(
        rawPublicKey: String,
        message: String,
        rawSignature: String,
        publicKeyAlgorithm: String = DEFAULT_PUB_KEY_ALGORITHM,
        signatureKeyAlgorithm: String = DEFAULT_SIGNATURE_ALGORITHM,
    ): Boolean {
        val bouncyCastleProvider = BouncyCastleProvider()

        val publicKeySpec = X509EncodedKeySpec(Base64.decode(rawPublicKey.removePublicKeyDecoration(), Base64.NO_WRAP))
        val keyFactory = KeyFactory.getInstance(publicKeyAlgorithm, bouncyCastleProvider)
        val publicKey: PublicKey = keyFactory.generatePublic(publicKeySpec)
        val ecdsaVerify: Signature = Signature.getInstance(signatureKeyAlgorithm)

        val rawMessage = message.toByteArray(StandardCharsets.US_ASCII)
stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
47
        val decodedSignature = Base32().decode(rawSignature)
stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
48

stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
49
        val derSequence = getDERSequenceFromSignature(decodedSignature)
stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
50
51
52
        ecdsaVerify.initVerify(publicKey)
        ecdsaVerify.update(rawMessage)

stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
53
54
55
56
57
58
59
        return ecdsaVerify.verify(derSequence.encoded)
    }

    private fun getDERSequenceFromSignature(decodedSignature: ByteArray): DERSequence {
        val length = decodedSignature.size / 2
        val bytes = ByteArray(length)
        val asn1EncodableVector = ASN1EncodableVector()
stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
60

stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
61
62
        System.arraycopy(decodedSignature, 0, bytes, 0, length)
        asn1EncodableVector.add(ASN1Integer(BigInteger(1, bytes)))
stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
63

stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
64
65
        System.arraycopy(decodedSignature, length, bytes, 0, length)
        asn1EncodableVector.add(ASN1Integer(BigInteger(1, bytes)))
stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
66

stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
67
        return DERSequence(asn1EncodableVector)
stopcovid@lunabee.com's avatar
stopcovid@lunabee.com committed
68
69
    }
}