copenssldsa

How to generate a DSA key pair using OpenSSL libcrypto?


I have the following code trying to generate a DSA key pair.

OpenSSL_add_all_algorithms();
ctx=EVP_PKEY_CTX_new_id(EVP_PKEY_DSA,NULL); EVP_PKEY_keygen_init(ctx);
if (EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx,1024)<=0)      ERR_print_errors_fp(stderr);

and I get the following error

3073906944:error:06089094:digital envelope routines:EVP_PKEY_CTX_ctrl:invalid operation:pmeth_lib.c:398:

Any clue on what I am doing wrong? thanks


Solution

  • You need two contexts; one for the params, and one for the actual keygen. You should do the following two sets of operations :

    Parameter Generation

    1. Create a param generator using EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, NULL)
    2. Set the bits in the param generator context using EVP_PKEY_CTX_set_dsa_paramgen_bits; not the keygen context (which, if you'r'e doing this right, doesn't even exist yet).
    3. Initialize the param generator using EVP_PKEY_paramgen_init
    4. Finally, generate the parameters using EVP_PKEY_paramgen. The result is a EVP_PKEY object (I'll call it pkey_params) that contains the input parameters for the upcoming key generation

    Once the above is done, then you move on to the actual key generation, which is considerably simpler:

    Key Generation

    1. Create a new context using EVP_PKEY_CTX_new(pkey_params, NULL) Note the pkey_params is from the prior series of steps.
    2. Initialize the generator context using EVP_PKEY_keygen_init
    3. Generate the actual key using EVP_PKEY_keygen

    Once done, all resources except the final pkey from above should be properly destroyed. Free the final key once done using it. That's it.

    Example

    This has no error checking whatsoever, but the order of operations is what is important here. So pay attention to that.

    #include <stdio.h>
    #include <openssl/evp.h>
    #include <openssl/dsa.h>
    #include <openssl/pem.h>
    
    // required for any BIO standard stream IO.
    #include <openssl/applink.c>
    
    int main()
    {
        OPENSSL_init();
        OpenSSL_add_all_algorithms();
    
        // build parameters first
        EVP_PKEY_CTX *ctx_params = EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, NULL);
        EVP_PKEY_paramgen_init(ctx_params);
        EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx_params, 1024);
    
        EVP_PKEY* pkey_params = NULL;
        EVP_PKEY_paramgen(ctx_params, &pkey_params);
    
        // using parameters, build DSA keypair
        EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey_params, NULL);
        EVP_PKEY_keygen_init(ctx);
        
        EVP_PKEY* pkey = NULL;
        EVP_PKEY_keygen(ctx, &pkey);
    
        // cleanup everything but the final key
        EVP_PKEY_free(pkey_params);
        EVP_PKEY_CTX_free(ctx_params);
        EVP_PKEY_CTX_free(ctx);
    
        // TODO: whatever you want with the generator pkey. in this
        //  example we're just dumping the full unencrypted key to 
        //  stdout. 
        DSA* dsa = EVP_PKEY_get1_DSA(pkey);
        BIO* bio = BIO_new_fp(stdout, BIO_NOCLOSE);
        PEM_write_bio_DSAPrivateKey(bio, dsa, NULL, NULL, 0, NULL, NULL);
        BIO_flush(bio);
        DSA_free(dsa);
        EVP_PKEY_free(pkey);
    
        return 0;
    }
    

    Output (varies, obviously)

    -----BEGIN DSA PRIVATE KEY-----
    MIIBuwIBAAKBgQDJ+NoL8SZeTcqVA83WI7CCO6INYLw18DiALLMewPqXEPm99mof
    RX2693WJfpbWIjuHi/KXzH6vQ/0sQU+2z1CqgWhudVhQTofGNcsPrUbPpShTDMcP
    OoTx9dRb8rXWbxg7dfhGZ9z2pEhzRtPWpI2y81VxYhGXzVSC3zqW6+ec2QIVALaE
    fynSMqc56gPqDPZfRz1rlq3dAoGAL+vbbYu+gSy8zGqoLykqhG+Vl4/Eh/zQIWoB
    t64bfh7GU6o0wvgTQgcdGZK3/laa9Msa6J3iEGZcP3dd9x4fTQ5vzxDGIYikcC8I
    L8s2JbNi1Jxbr5dw3/sOKsdHIt95rFZ03+gMzaV+9pc8LpATnaXMtp5mmH+lRgsJ
    SIEdLqcCgYAVGpwZHaFUnttqQAf3/ohMtqIQG+RBp/yUf2EA7rcoHpA7bCBADApx
    mG5hH/F4dKjCSciKdHq4Ibf60ctAJNL2sobPKNArTMo/GNuzE+J79Wj6s/b7zwt7
    AF+27H9PAiXB08ftMmCSesXkX7v926EHRxDgSlVAgCPSfkXKNQn1XwIVALF2MF2N
    GRdMtFUxZFnIk2GnqC1R
    -----END DSA PRIVATE KEY-----