Uso de OpenSSL EVP

OpenSSL EVP (funciones criptográficas de alto nivel) proporciona una rica variedad de funciones en criptografía. En OpenSSL se implementan varios algoritmos de simetría, algoritmos de resumen y algoritmos de firma/verificación de firma. La función EVP encapsula estos algoritmos específicos.

EVP encapsula principalmente las siguientes funciones funcionales:

1) Implementa la codificación y decodificación BASE64

2) Implementa el cifrado y descifrado BIO

;

3) BIO de resumen implementado

4) BIO confiable implementado

5) Encapsulado del algoritmo de resumen

6) Simetría encapsulada Cifrado y descifrado; algoritmo

7) Encapsula cifrado de clave asimétrica (clave pública), descifrado (clave privada), firma y verificación, y funciones auxiliares

8) Cifrado basado en contraseña (PBE);

9) Procesamiento de clave simétrica;

10) Sobre digital: el sobre digital utiliza la clave pública de la otra parte para cifrar la clave simétrica y los datos utilizan esta clave de cifrado simétrica. . Al enviar a la otra parte, envíe el texto cifrado de clave simétrica y el texto cifrado de datos al mismo tiempo. El receptor primero descifra el texto cifrado clave con su propia clave privada para obtener la clave simétrica y luego la usa para descifrar los datos.

11) Otras funciones auxiliares.

Este artículo asume que ha instalado OpenSSL y tiene una copia del código fuente 1.1.1.

Los archivos de encabezado relacionados con EVP están en evp.h y los archivos fuente están en el directorio crypto/evp.

Dado que las funciones de EVP son demasiado poderosas y mi energía y nivel son limitados, solo extraeré y explicaré algunas de las funciones por el momento.

Esta estructura define el método abstracto del algoritmo de resumen. Significado del campo principal:

tipo: NID del algoritmo de resumen.

pkey_type: el NID clave asociado con el algoritmo de resumen.

md_size: el tamaño de salida del valor de resumen.

banderas - banderas internas.

init - función de inicialización.

actualización: función de cálculo de entrada.

final - función de cálculo de salida.

copiar——Función de copia del contexto de la operación de resumen.

limpieza——Función de limpieza del contexto de la operación de resumen.

block_size - Tamaño del grupo de operaciones de resumen.

ctx_size: tamaño del búfer de agrupación de operaciones de resumen.

md_ctrl: función de control de instrucciones de operación resumida.

Los algoritmos de resumen admitidos incluyen:

const EVP_MD *EVP_md5(void);

const EVP_MD *EVP_sha1(void); EVP_MD *EVP_sha256(void);

const EVP_MD *EVP_sha512(void);

Tome EVP_md5() como ejemplo, su valor de retorno es:

A continuación Estas funciones consultan la información de atributos de md:

A veces no estamos familiarizados con el algoritmo de resumen utilizado y estas funciones son muy útiles.

EVP_MD_CTX *EVP_MD_CTX_new(void);

void EVP_MD_CTX_free(EVP_MD_CTX *ctx);

Estas dos funciones se utilizan para crear y liberar objetos de contexto de resumen simétricos.

int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type);

Inicializa el contexto de resumen, el tipo es la colección abstracta de algoritmos de resumen.

Devuelve 1 en caso de éxito y 0 en caso de error.

int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt);

Ingrese un dato a la estructura de esponja del cálculo del resumen.

Devuelve 1 en caso de éxito y 0 en caso de error.

int EVP_DigestFinal(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s);

Genera el resumen final y genera el valor y la longitud del resumen.

Devuelve 1 en caso de éxito y 0 en caso de error.

int EVP_Digest(const void *data, size_t count, unsigned char *md, unsigned int *size, const EVP_MD *type, ENGINE *impl

Usar envuelto una vez); El método calcula un resumen de una pequeña porción de datos.

Devuelve 1 en caso de éxito y 0 en caso de error.

struct evp_cipher_st {

int nid;

int block_size

/* Valor predeterminado para cifrados de longitud variable /

int key_len;

int iv_len;

/ Varios indicadores /

indicadores largos sin firmar

/ clave de inicio /

int (init) (EVP_CIPHER_CTX *ctx, const unsigned char *key,

const unsigned char iv, int enc

/ cifrar/descifrar datos /);

int (do_cipher) (EVP_CIPHER_CTX *ctx, unsigned char *out,

const unsigned char in, size_t inl

/ cleanup ctx /

p>

int (limpieza) (EVP_CIPHER_CTX);

/ qué tan grande debe ser ctx-gt; cipher_data /

int ctx_size;

/ Completar un ASN1_TYPE con parámetros /

int ( set_asn1_parameters) (EVP_CIPHER_CTX *, ASN1_TYPE );

/ Obtener parámetros de un ASN1_TYPE /

int ( get_asn1_parameters) ( EVP_CIPHER_CTX *, ASN1_TYPE );

/ Operaciones varias /

int ( ctrl ) (EVP_CIPHER_CTX *, int type, int arg, void ptr

/ Datos de la aplicación */

void app_data;

} / EVP_CIPHER */

typedef struct evp_cipher_st EVP_CIPHER

Esta estructura define un enfoque abstracto para los algoritmos de cifrado simétrico. Significado del campo principal:

nid - NID del algoritmo de cifrado.

block_size - tamaño del bloque.

key_len - longitud de la clave.

iv_len - longitud inicial del vector.

banderas - banderas internas.

init - función de inicialización.

do_cipher - función de operación intermedia.

limpieza - función de operación final.

ctx_size - tamaño del contexto.

ctrl - función de control.

app_data - datos de la aplicación.

Los algoritmos de cifrado y descifrado abstractos de CIPHER admitidos incluyen:

const EVP_CIPHER *EVP_des_ecb(void);

const EVP_CIPHER *EVP_des_ede3(void);

const EVP_CIPHER *EVP_aes_128_ecb(void);

const EVP_CIPHER *EVP_aes_128_cbc(void);

Las siguientes funciones consultan la información del atributo de cifrado:

int EVP_CIPHER_nid(const EVP_CIPHER *cipher);

int EVP_CIPHER_type(const EVP_CIPHER *ctx);

# define EVP_CIPHER_name(e) OBJ_nid2sn(EVP_CIPHER_nid(e))

int EVP_CIPHER_block_size(const EVP_CIPHER *cifrado);

int EVP_CIPHER_key_length(const EVP_CIPHER *cifrado);

int EVP_CIPHER_iv_length(const EVP_CIPHER *cifrado);

En ocasiones no estamos familiarizados con el algoritmo de cifrado utilizado, estas funciones son de gran ayuda.

EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void);

void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *c);

Estas dos funciones se utilizan para crear y liberar contexto de cifrado y descifrado simétrico. objetos.

int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen);

Cuando la longitud de la clave del algoritmo simétrico es variable, establezca la longitud de la clave del algoritmo simétrico.

Devuelve 1 en caso de éxito y 0 en caso de error.

int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *c, int pad);

Establece el relleno del algoritmo simétrico El algoritmo simétrico a veces implica relleno.

Pad toma los valores 0 y 1. Cuando pad es 1, significa que se utiliza relleno. La estrategia de llenado predeterminada adopta la especificación PKCS5, es decir, cuando el último paquete se llena con n bytes, su valor de llenado es n.

Devuelve 1 en caso de éxito y 0 en caso de error.

int EVP_EncryptInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, const unsigned char *key, const unsigned char *iv);

Inicializa el contexto de cifrado simétrico.

Devuelve 1 en caso de éxito y 0 en caso de error.

int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl

Cifrar un fragmento de texto sin formato.

Devuelve 1 en caso de éxito y 0 en caso de error. En caso de éxito, outl genera la longitud del texto cifrado.

int EVP_EncryptFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);

Cifre el texto sin formato restante.

Devuelve 1 en caso de éxito y 0 en caso de error. En caso de éxito, outl genera la longitud del texto cifrado.

int EVP_DecryptInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, const unsigned char *key, const unsigned char *iv);

Inicializa el contexto de descifrado simétrico.

Devuelve 1 en caso de éxito y 0 en caso de error.

int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl

Descifrar un texto cifrado.

Devuelve 1 en caso de éxito y 0 en caso de error. En caso de éxito, outl genera la longitud del texto sin formato.

int EVP_DecryptFinal(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl);

Descifrar el texto cifrado restante.

Devuelve 1 en caso de éxito y 0 en caso de error. En caso de éxito, outl genera la longitud del texto sin formato.

int EVP_BytesToKey(const EVP_CIPHER *tipo, const EVP_MD *md,

const unsigned char *salt,

const unsigned char *datos, int datal, int count,

unsigned char *key, unsigned char *iv);

Función de clave de cálculo, que calcula una clave simétrica en función del tipo de algoritmo, el algoritmo de resumen, la sal y los datos de entrada, y vector de inicialización iv. Devuelve la longitud de la clave.

Esta función se utiliza al generar claves basadas en contraseñas en la función PEM_do_header().

Esta estructura define el contenedor de almacenamiento para información de claves asimétricas. Significado del campo principal:

tipo: NID del algoritmo de cifrado asimétrico.

save_type: el tipo de PKEY guardado.

pkey: puntero PKEY guardado, como el puntero de estructura RSA.

EVP_PKEY *EVP_PKEY_new(void);

void EVP_PKEY_free(EVP_PKEY *pkey);

Estas dos funciones se utilizan para crear y liberar objetos de contexto PKEY.

int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key);

Estructura de contexto que especifica el tipo de algoritmo para la asociación PKEY. Por ejemplo, la definición de macro para la asociación RSA es. de la siguiente manera:

p>

# define EVP_SignInit(a, b) EVP_DigestInit(a, b)

# define EVP_SignUpdate(a, b, c) EVP_DigestUpdate(a, b, c)

int EVP_SignFinal(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s,

EVP_PKEY *pkey

Cálculo de firma. A partir de la definición de la macro, podemos ver que el resumen en realidad se calcula primero y luego se cifra con la clave privada RSA.

Devuelve 1 en caso de éxito y 0 en caso de error.

# define EVP_VerifyInit(a, b) EVP_DigestInit(a, b)

# define EVP_VerifyUpdate(a, b, c) EVP_DigestUpdate(a, b, c)

int EVP_VerifyFinal(EVP_MD_CTX *ctx, const unsigned char *sigbuf,

unsigned int siglen, EVP_PKEY *pkey);

Cálculo de verificación de firma. A partir de la definición de la macro, podemos ver que el resumen se calcula primero, luego la firma se descifra utilizando la clave pública RSA y luego se compara con el resumen.

Devuelve 1 en caso de éxito y 0 en caso de error.

El siguiente ejemplo demuestra el proceso de cálculo de resumen utilizando dos métodos de MD5.

Salida:

EVP_DigestInit() ret: [1]

EVP_DigestUpdate() ret: [1]

EVP_DigestFinal() ret :[1]

e380e88e8d09ebf8d8659a15b0ea70b5

EVP_Digest() ret: 1

e380e88e8d09ebf8d8659a15b0ea70b5

El siguiente ejemplo demuestra el uso de DES para cifrado y proceso de descifrado. Para facilitar la implementación del programa, std::string se utiliza como excepción.

Salida:

EVP_EncryptInit() ret: [1]

EVP_EncryptUpdate() ret: [1]

nCipherLen: [24 ]

EVP_EncryptFinal() ret: [1]

nCipherLen: [8]

tamaño de cifrado: [32]

EVP_DecryptInit( ) ret: [1]

EVP_DecryptUpdate() ret: [1]

nTextLen: [24]

EVP_DecryptFinal() ret: [1]

nTextLen: [2]

tamaño del texto: [26] cuerpo: [abcdefghijklmnopqrstuvwxyz]

El siguiente ejemplo demuestra el uso de SHA1 para el proceso de cálculo de verificación y firma RSA .

Salida:

RSA_generate_key_ex() ret: [1]

EVP_PKEY_assign_RSA() ret: [1]

EVP_SignInit() ret :[1]

EVP_SignUpdate() ret: [1]

EVP_SignFinal() ret: [1]

sha1 len: [64]

EVP_VerifyInit() retirada: [1]

EVP_VerifyUpdate() retirada: [1]

EVP_VerifyFinal() retirada: [1]