Converti la firma ECDSA dal formato normale al formato DER

1

Sto facendo l'autenticazione attiva di ePassports secondo lo standard ICAO 9303 (parte 11).

  1. Invia una sfida al chip, il chip lo firma usando la sua chiave privata (memorizzata all'interno del chip, non leggibile)
  2. Il chip invia la sigla.
  3. Quindi verifica la firma utilizzando la chiave pubblica (può essere letta dal chip)

Esistono due possibili algoritmi di firma: RSA (RSA-SHA1, RSA-PSS, RSA-SHA224, RSA-SHA256, RSA-SHA512) e ECDSA.

Nella sezione 6.1.2.3 di ICAO9303 parte 11  si legge su ECDSA: "Per ECDSA, il formato di firma semplice in base a TR-03111 DEVE essere utilizzato. Solo le prime curve con non compresso i punti DEVONO essere usati. Un algoritmo di hash, la cui lunghezza di uscita è della stessa lunghezza o inferiore alla lunghezza del Utilizzare la chiave ECDSA. "

Quello che non capisco è come utilizzare la firma ECDSA in questo formato normale con OpenSSL? Nel seguente codice EVP_DigestVerifyFinal restituisce -1. Come posso portare la firma oSIG dal formato normale al formato DER?

bool doAA(std::vector<unsigned char> oRND, std::vector<unsigned char> oSIG, std::vector<unsigned char> oPKEY)
{


    EVP_PKEY* m_pPubkey;
    BIO* keyBio = BIO_new_mem_buf(&oPKEY[0], (int)oPKEY.size());
    m_pPubkey = d2i_PUBKEY_bio(keyBio, NULL);       
    BIO_free(keyBio);

    if (NULL == m_pPubkey)
    {
        std::cout << "Error Public key is NULL" << std::endl;
        return false;
    }

    // check if ECDSA signature and then verify
    int nRes = 0;
    int type = EVP_PKEY_base_id(m_pPubkey);
    if (type == EVP_PKEY_EC)
    {
        EVP_MD_CTX* ctx = EVP_MD_CTX_create();

        nRes = EVP_DigestVerifyInit(ctx, NULL, EVP_sha1(), NULL, m_pPubkey);
        if (1 != nRes)
        {
            EVP_MD_CTX_cleanup(ctx);
            std::cout << "Error EVP_DigestVerifyInit" << std::endl;
            return false;
        }

        nRes = EVP_DigestVerifyUpdate(ctx, &oRND[0], oRND.size());
        if (1 != nRes)
        {
            EVP_MD_CTX_cleanup(ctx);
            std::cout << "Error EVP_DigestVerifyUpdate" << std::endl;
            return false;
        }

        nRes = EVP_DigestVerifyFinal(ctx, &oSIG[0], oSIG.size());
        EVP_MD_CTX_cleanup(ctx);
        if (nRes < 0)
        {
            std::cout << "Error EVP_DigestVerifyFinal failed" << std::endl;
            return false;
        }
        else if (nRes == 0)
        {
            std::cout << "Error EVP_DigestVerifyFinal: Signature could not be verified" << std::endl;
            return false;
        }



    }
    else
    {
        std::cout << "Not a ECDSA Signature" << std::endl;
        return false;
    }

    return nRes == 1;
}

EDIT: questo è l'errore che ottengo: error:0D07207B:lib(13):func(114):reason(123)

    
posta tzippy 22.11.2017 - 15:07
fonte

1 risposta

1

Dato che stai già usando OpenSSL puoi costruire una struttura ECDSA_SIG e codificarla; vedi link .

Oppure puoi scrivere il tuo proprio codificatore DER per questo caso specifico che è piuttosto semplice, sebbene un codificatore ASN.1 generale non lo sia:

// given int l is the curve-and-field size in octets with l<61 
// (i.e. P-256 or P-384 is okay for this code but P-521 is not) 
// and unsigned char plain [2*l] is the 'plain' signature
unsigned char * der = malloc(2*l+8); // worst case
int o = 2, m = raw[0]&0x80 != 0;
der[o++]=2; der[o++]=l+m; if(m) der[o++]=0; memcpy(der+o, raw+0, l); o+=l;
m = raw[l]&0x80 != 0;
der[o++]=2; der[o++]=l+m; if(m) der[o++]=0; memcpy(der+o, raw+l, l); o+=l;
der[0]=0x30; der[1]=o-2;
// use der for length o as the signature buffer
// when done free(der)
// or in C++ use new[] and delete[] if you or your style rules prefer
    
risposta data 23.11.2017 - 01:03
fonte

Leggi altre domande sui tag