Convertir la firma ECDSA del formato liso al DER

1

Estoy realizando la autenticación activa de los pasaportes electrónicos de acuerdo con el estándar ICAO 9303 (parte11).

  1. Envíe un desafío al chip, el chip lo firma utilizando su clave privada (almacenada dentro del chip, no legible)
  2. El chip envía la firma.
  3. Luego verifica la firma usando la clave pública (se puede leer desde el chip)

Hay dos posibles algoritmos de firma: RSA (RSA-SHA1, RSA-PSS, RSA-SHA224, RSA-SHA256, RSA-SHA512) y ECDSA.

En la sección 6.1.2.3 de ICAO9303 parte 11  se lee sobre ECDSA: "Para ECDSA, el formato de firma simple de acuerdo con TR-03111 SE DEBE usar. Solo curvas sin comprimir Los puntos DEBEN ser utilizados. Un algoritmo hash, cuya longitud de salida es de la misma longitud o más corta que la longitud de la La clave ECDSA en uso, SE DEBE usar. "

Lo que no entiendo es cómo uso la firma ECDSA en este formato simple con OpenSSL? En el siguiente código EVP_DigestVerifyFinal devuelve -1. ¿Cómo puedo llevar la firma oSIG del formato plano 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;
}

EDITAR: este es el error que obtengo: error:0D07207B:lib(13):func(114):reason(123)

    
pregunta tzippy 22.11.2017 - 15:07
fuente

1 respuesta

1

Como ya está utilizando OpenSSL, puede construir una estructura ECDSA_SIG y codificarla; vea enlace .

O puede escribir su propio codificador DER para este caso específico que es bastante simple, aunque un codificador ASN.1 general no es:

// 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
    
respondido por el dave_thompson_085 23.11.2017 - 01:03
fuente

Lea otras preguntas en las etiquetas