Ricardo Fodra
Ricardo Fodra

Reputation: 44

Verify signature with C/OpenSSL

I generated a key pair in PHP using:

$options = [
    'digest_alg' => 'SHA256',
    'private_key_type' => OPENSSL_KEYTYPE_EC,
    'private_key_bits' => 2048,
    'curve_name' => 'secp384r1'];

Which gave me:

-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEieSNdLKtNPdLIo9Y+f1DTfjnpZ4NFMXc
qVV2epFEWdtCyzhZnMubQDtpmhcycVZM+aPELe0dVWvwkFAY+VO0SpLT4I+cVkar
FSkjtp84jWZsWj8DT+7S0ukoEJzraeff
-----END PUBLIC KEY-----

-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDAUQuV5RcleR29UgIK0bjczEHctb6TK5z3PesxjT2R8A3B7iQDl0/I9
bkFjLyMYyFOgBwYFK4EEACKhZANiAASJ5I10sq0090sij1j5/UNN+Oelng0Uxdyp
VXZ6kURZ20LLOFmcy5tAO2maFzJxVkz5o8Qt7R1Va/CQUBj5U7RKktPgj5xWRqsV
KSO2nziNZmxaPwNP7tLS6SgQnOtp598=
-----END EC PRIVATE KEY-----

msg: Hello World!

sig: MGYCMQDa3+Nz6zxJOkvLjI2AVFXb4zfoYuFzy79Hg2bKq3El93oQwNleUgV
k1TuS8ptSj5kCMQCYVyplp5/yoCBLQpwsQw+MKlJ78ncQgiyh9UIBOFHTQvMjWiZ
BvOQ/B0VsZY7xoeo=

How do I read the public key in C/OpenSSL and check if the signature is correct? I can't find the right command in the OpenSSL Documentation.

Upvotes: 0

Views: 48

Answers (1)

Ricardo Fodra
Ricardo Fodra

Reputation: 44

The examples helped. I think it wasn't working because of the base64 conversion to binary, and the '=' characters. Here is a working example:

#include <iostream>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/decoder.h>

void errors() {
    int r;
    char error[256];
    while ((r = ERR_get_error()) != 0) {
        ERR_error_string_n(r, error, 256);
        std::cout << "Error " << r << ": " << error << "\n";
    }
}

int pubKeyFromPem(EVP_PKEY** pkey, const unsigned char* data, size_t datalen) {
    const char* keytype = "EC";
    int selection = OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
    OSSL_DECODER_CTX* dctx = OSSL_DECODER_CTX_new_for_pkey(pkey, "PEM", NULL, keytype, selection, NULL, NULL);
    if (dctx == NULL) {
        return __LINE__;
    }
    if (!OSSL_DECODER_from_data(dctx, &data, &datalen)) {
        return __LINE__;
    }
    OSSL_DECODER_CTX_free(dctx);
    return 0;
}

int base64ToSig(const char* sig64, int sig64len, unsigned char** sig, size_t* siglen) {
    int memory = sig64len / 4 * 3;
    *sig = (unsigned char*)malloc(memory);
    if (*sig == NULL)
        return __LINE__;
    int len = EVP_DecodeBlock(*sig, (const unsigned char*)sig64, sig64len);
    if (len == -1)
        return __LINE__;
    if (sig64[sig64len - 1] == '=')
        len--;
    if (sig64[sig64len - 2] == '=')
        len--;
    *siglen = len;
    return 0;
}

int verify(EVP_PKEY* key, const char* msg, unsigned char* sig, size_t siglen) {
    EVP_MD_CTX* mdctx;
    if (!(mdctx = EVP_MD_CTX_create()))
        return __LINE__;
    if (1 != EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, key))
        return __LINE__;
    if (1 != EVP_DigestVerifyUpdate(mdctx, msg, strlen(msg)))
        return __LINE__;
    int r = EVP_DigestVerifyFinal(mdctx, sig, siglen);
    if (1 == r) {
        std::cout << "Success!\n";
    } else if (0 == r) {
        std::cout << "Failure!\n";
    } else {
        std::cout << "Error!\n";
        errors();
    }
    EVP_MD_CTX_free(mdctx);
    return 0;
}

int check() {
    int line;
    const char* msg = "Hello World!";
    const char* pubKeyPem =
        "-----BEGIN PUBLIC KEY-----\n"
        "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEQl0DmsjriyNARf16wXp8+S3qEkwKHOl4\n"
        "0kSfEo48e67oF03vjUyWKVl/FMnjpzMV9O6UpKUAR19svqJwa6iihhbBRU9PsfGO\n"
        "dGgCwedB85QrwtlJ9UZSrYQ93r9tUha3\n"
        "-----END PUBLIC KEY-----";
    const char* sig64 =
        "MGYCMQDkL/SH5FKGJSUkEWUhUgwvVTgQUg3jd49280/oTuu+EuwNzFiHhKo"
        "NXAeDqzW8r+4CMQDPVRoKGRI/a7qSZQZQuHFmh5kK9Wp9wY2+dvLb/4lCrW+PHM8"
        "Vs0S+ORkp1uoMUDY=";
    unsigned char* sig;
    size_t siglen;

    EVP_PKEY* key = NULL;
    line = pubKeyFromPem(&key, (const unsigned char*)pubKeyPem, strlen(pubKeyPem));
    if (line != 0)
        return line;
    line = base64ToSig(sig64, strlen(sig64), &sig, &siglen);
    if (line != 0)
        return line;
    std::cout << "Verifying with PEM key: ";
    line = verify(key, msg, sig, siglen);
    if (line != 0)
        return line;
    return 0;
}

int main() {
    int line = check();
    if (line != 0) {
        std::cout << __FILE__ << ":" << line << "\n";
        errors();
    }
}

Upvotes: 0

Related Questions