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>
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] p>
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]