2007年6月29日星期五

OpenSSL中对称加密算法的统一接口

OpenSSL中对称加密算法的统一接口
 
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn

1. 前言
OpenSSL是一个开源的SSL实现,其中集成了多种加密算法。OpenSSL将各算法的各自独特地方封装在内部,对外则使用了统一的算法接 口,因此外部应用只需指定使用何种算法,就可以用相同的方法调用加解密函数而不用考虑其差异。这种算法统一封装的方式在其他很多软件中都采用,也给算法扩 充提供了方便。
以下代码来自OpenSSL-0.9.7b。

2. EVP接口
2.1 数据结构
Openssl/crypto/evp目录下定义各种算法的接口源文件,这些文件要作的事就是要填写描述算法的EVP_CIPHER结构,每个算法都有一个EVP_CIPHER结构进行描述:
Openssl/crypto/evp/evp.h
struct evp_cipher_st
 {
 int nid;
 int block_size;
 int key_len;  /* Default value for variable length ciphers */
 int iv_len;
 unsigned long flags; /* Various flags */
 int (*init)(EVP_CIPHER_CTX *ctx, const unsigned char *key,
      const unsigned char *iv, int enc); /* init key */
 int (*do_cipher)(EVP_CIPHER_CTX *ctx, unsigned char *out,
    const unsigned char *in, unsigned int inl);/* encrypt/decrypt data */
 int (*cleanup)(EVP_CIPHER_CTX *); /* cleanup ctx */
 int ctx_size;  /* how big ctx->cipher_data needs to be */
 int (*set_asn1_parameters)(EVP_CIPHER_CTX *, ASN1_TYPE *); /* Populate a ASN1_TYPE with parameters */
 int (*get_asn1_parameters)(EVP_CIPHER_CTX *, ASN1_TYPE *); /* Get parameters from a ASN1_TYPE */
 int (*ctrl)(EVP_CIPHER_CTX *, int type, int arg, void *ptr); /* Miscellaneous operations */
 void *app_data;  /* Application data */
 } /* EVP_CIPHER */;
typedef struct evp_cipher_st EVP_CIPHER;
nid:算法的ID号,在include/openssl/object.h中定义;
block_size:加解密的分组长度
key_len:密钥长度
iv_len:初始向量长度
flags:标志
(* init):初始化函数,提供密钥,IV向量,算法上下文CTX,加密还是解密
(* do_cipher):加解密函数,提供算法上下文CTX,输出数据,输入数据和输入数据长度
(* clean_up):资源释放
ctx_size:各算法相关数据大小,实际就是各算法的密钥数据
(*set_asn1_parameters):设置asn1参数
(*get_asn1_parameters):获取asn1参数
(*ctrl):其他控制操作
app_data:算法相关数据
 
另一个重要的数据结构是描述加密算法的上下文结构EVP_CIPHER_CTX,这个结构是进入算法前由系统根据指定的算法提供的:
struct evp_cipher_ctx_st
 {
 const EVP_CIPHER *cipher;
 ENGINE *engine; /* functional reference if 'cipher' is ENGINE-provided */
 int encrypt;  /* encrypt or decrypt */
 int buf_len;  /* number we have left */
 unsigned char  oiv[EVP_MAX_IV_LENGTH]; /* original iv */
 unsigned char  iv[EVP_MAX_IV_LENGTH]; /* working iv */
 unsigned char buf[EVP_MAX_BLOCK_LENGTH];/* saved partial block */
 int num;    /* used by cfb/ofb mode */
 void *app_data;  /* application stuff */
 int key_len;  /* May change for variable length cipher */
 unsigned long flags; /* Various flags */
 void *cipher_data; /* per EVP data */
 int final_used;
 int block_mask;
 unsigned char final[EVP_MAX_BLOCK_LENGTH];/* possible final block */
 } /* EVP_CIPHER_CTX */;
typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
参数为:
cipher:算法指针
engine:加解密引擎
encrypt:加密或解密
buf_len:剩余空间
oiv:原始的初始向量
iv:当前的初始向量
buf:保存的部分块数据
num:cfb/ofb方式时的数据数量
app_data:应用相关数据
key_len:密钥长度
flags:标志
cipher_data:各算法相关部分,主要是各算法的key等
final_used:
block_mask:块的掩码
final:最后的分组块
1.2.2 算法接口
每种算法就是要填写各自的EVP_CIPHER结构,以RC4为例,定义了两个RC4的EVP_CIPHER结构,只是密钥长度不同,一个是128位(16字节密钥),一个是40位(5字节密钥),而算法都一样:
#ifndef OPENSSL_NO_RC4
#include <stdio.h>
#include "cryptlib.h"
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/rc4.h>
/* FIXME: surely this is available elsewhere? */
#define EVP_RC4_KEY_SIZE  16

//这个结构是各加密算法独有的,各算法的各自不同
//也就是EVP_CIPHER_CTX结构中cipher_data
typedef struct
    {
    RC4_KEY ks; /* working key */
    } EVP_RC4_KEY;
#define data(ctx) ((EVP_RC4_KEY *)(ctx)->cipher_data)
static int rc4_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
   const unsigned char *iv,int enc);
static int rc4_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
        const unsigned char *in, unsigned int inl);
static const EVP_CIPHER r4_cipher=
 {
 NID_rc4,
 1,EVP_RC4_KEY_SIZE,0,
 EVP_CIPH_VARIABLE_LENGTH,
 rc4_init_key,
 rc4_cipher,
 NULL,
 sizeof(EVP_RC4_KEY),
 NULL,
 NULL,
 NULL
 };
static const EVP_CIPHER r4_40_cipher=
 {
 NID_rc4_40,
 1,5 /* 40 bit */,0,
 EVP_CIPH_VARIABLE_LENGTH,
 rc4_init_key,
 rc4_cipher,
 NULL,
 sizeof(EVP_RC4_KEY),
 NULL,
 NULL,
 NULL
 };
// 返回算法结构指针
const EVP_CIPHER *EVP_rc4(void)
 {
 return(&r4_cipher);
 }
const EVP_CIPHER *EVP_rc4_40(void)
 {
 return(&r4_40_cipher);
 }
// 密钥初始化函数
// ctx:加解密上下文;key:密钥字符串;iv:初始化向量;enc:加密还是解密
static int rc4_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
   const unsigned char *iv, int enc)
 {
// RC4算法设置密钥函数
// 一般来说,加解密时算法里用的密钥并不是用户输入的密码字符串本身,因为算法
// 使用的密钥长度要求是固定的,通常为64位或128位,而用户自己定义的密码长度
// 则不确定,所以一般都都要对用户输入的密码进行变换,映射到一个固定长度密钥
// 上,然后算法再使用该密钥加密,所以算法中用的密钥和用户的密码一般是不同的
 RC4_set_key(&data(ctx)->ks,EVP_CIPHER_CTX_key_length(ctx),
      key);
 return 1;
 }
// 加解密处理函数
// ctx:加解密上下文;out:输出数据;in:输入数据;inl:输入数据长度
static int rc4_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
        const unsigned char *in, unsigned int inl)
 {
 RC4(&data(ctx)->ks,inl,in,out);
 return 1;
 }

没有评论: