Open SSL 和Lockbox的兼容性问题
Lockbox是Toubo Power中的加密SDK,封装了常见的加密算法,如RSA,3DES(Triple-DES),DES,MD5等等,在Delphi/C++ Builder中使用非常简单方便。Open SSL是一个开源的C/C++ 加密库,同样实现了RSA,3DES,MD5等加密算法,在Linux等C/C++环境下使用也非常方便。但对于使用Open SSL加密的数据,直接拿到Lockbox下是无法解密的,因为两者的兼容性存在一些问题,如RSA KEY文件存储两者是不一致的,导致RSA等加密算法实现差异,无法互通。
对于RSA等公钥加密算法,涉及到KEY文件的定义和输出,Open SSL和Lockbox是不一致的。高精度整数输出时LockBox是逆序输出高位在后,而OpenSSL是顺序序输出高位在前。
还有LockBox是以Byte形式进行操作,OpenSSL是以Int(可能是64位、32位、16位、8位根据宏定义进行选择)形式进行操作。
因此,针对以上情况,利用Lockbox生成密钥对之后,Open SSL在读取Lockbox的密钥文件的时候,要进行特殊处理:
//取得lockbox key,直接从文件中读取到内存块
key=d2i_RSAPublicKey_bio(in_,NULL);
//转化成openssl认识的key
memset(p,0,sizeof(p)-1);
num=BN_num_bytes(key->n);
memcpy(p,key->n->d,num);
tokey->n=BN_bin2bn(p,num,tokey->n);
//这里先取得明文加密长度
plainbytes=num-cRSAMinPadBytes;
//转化成openssl认识的key
memset(p,0,sizeof(p)-1);
num=BN_num_bytes(key->e);
memcpy(p,key->e->d,num);
tokey->e=BN_bin2bn(p,num,tokey->e);
完整的代码如下:
//2.RSA,调用代码如下:
void Encrypt()
{
static int BUFFER_SIZE=256;
RSA *key,*tokey;
char *infile=NULL,*fromfile,*tofile=NULL;
BIO *in_=NULL,*fromio,*toio=NULL;
int modulus=0;
long num,i,j;
int plen,outlength;
unsigned char *ptext,*p,*p1;
BUF_MEM * buf,*outbuf;
BIGNUM ff,ret,ee,nn;
//rsa最新补齐字节
static int cRSAMinPadBytes = 11;
//加密块明文最大长度
int plainbytes;
//加密块明文 实际长度
int actualbytes;
//加密块数
int blockcount;
//lockbox产生的公钥
infile= "D:\\openssl-0.9.6m\\test\\PublicKey.dat"; // PublicKey由LockboxGenerateKeyPaire并SaveToFile生成的。
//明文文件
fromfile= "D:\\openssl-0.9.6m\\test\\ctext.dat";
//保存密文目的文件
tofile= "D:\\openssl-0.9.6m\\test\\ctext.dat.enc";
in_=BIO_new(BIO_s_file());
fromio=BIO_new(BIO_s_file());
toio=BIO_new(BIO_s_file());
BN_CTX *ctx=NULL;
ptext=(unsigned char *)OPENSSL_malloc(BUFFER_SIZE);
p =(unsigned char *)OPENSSL_malloc(BUFFER_SIZE);
BN_init(&ff);
BN_init(&ret);
BN_init(&ee);
BN_init(&nn);
ctx=BN_CTX_new();
if (BIO_read_filename(in_,infile) <= 0)
{
//perror(infile);
return;
}
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF);
//取得lockbox key
key=d2i_RSAPublicKey_bio(in_,NULL);
tokey = RSA_new();
//转化成openssl认识的key
memset(p,0,sizeof(p)-1);
num=BN_num_bytes(key->n);
memcpy(p,key->n->d,num);
tokey->n=BN_bin2bn(p,num,tokey->n);
//这里先取得明文加密长度
plainbytes=num-cRSAMinPadBytes;
//转化成openssl认识的key
memset(p,0,sizeof(p)-1);
num=BN_num_bytes(key->e);
memcpy(p,key->e->d,num);
tokey->e=BN_bin2bn(p,num,tokey->e);
//读取明文
if(in_ != NULL) BIO_free(in_);
if (BIO_read_filename(fromio,fromfile) <= 0)
{
return;
}
plen=0;
if ((buf=BUF_MEM_new()) == NULL) return;
for (;;)
{
if (!BUF_MEM_grow(buf,(int)plen+BUFSIZ)) return;
i=BIO_read(fromio,&(buf->data[plen]),BUFSIZ);
if (i <= 0) break;
plen+=i;
}
blockcount = plen / plainbytes;
if(plen% plainbytes> 0)blockcount++;
//开始加密
//loop begin
//for 0 to count -1 block
//every block content less than
outlength= 0;
if ((outbuf=BUF_MEM_new()) == NULL) return;
for(i=0;i<blockcount;i++){
p1=(unsigned char *)&(buf->data[i*plainbytes]);
actualbytes=plainbytes;
if(i==blockcount-1) actualbytes=plen - (plainbytes * i);
memset(p,0,sizeof(p)-1);
//字节倒序
for(j=0;j<actualbytes;j++){
p[j]=p1[actualbytes-1-j];
}
num = BN_num_bytes(tokey->n);
//补齐按PKCS1
RSA_padding_add_PKCS1_type_2(ptext,num,p,actualbytes);
/* make data into a big number */
BN_bin2bn(ptext,num,&ff);
//加密
num = BN_mod_exp(&ret,&ff,tokey->e,tokey->n,ctx);
num=BN_num_bytes(&ret);
BN_bn2bin(&ret,p);
memset(ptext,0,sizeof(ptext)-1);
//取回字节倒序
for(j=0;j<num;j++){
ptext[j]=p[num-1-j];
}
if (!BUF_MEM_grow(outbuf,(int)outlength+num)) return;
memcpy(&(outbuf->data[outlength]),ptext,num);
outlength=outlength+num;
//loop end
}
//写流
BIO_write_filename(toio,tofile);
BIO_write(toio,outbuf->data,outlength);
if(toio != NULL) BIO_free(toio);
if(buf != NULL) BUF_MEM_free(buf);
if (ctx != NULL) BN_CTX_free(ctx);
BN_clear_free(&ff);
BN_clear_free(&ret);
BN_clear_free(&ee);
BN_clear_free(&nn);
if (p != NULL)
{
OPENSSL_cleanse(p,BUFFER_SIZE);
OPENSSL_free(p);
}
if (ptext != NULL)
{
OPENSSL_cleanse(ptext,BUFFER_SIZE);
OPENSSL_free(ptext);
}
return;
}
//TripleDES,调用代码如下:
// 利用下面的方式加密的数据,Lockbox可以直接解密
//ecb_out:返回加密流
unsigned char * ecb_out=des_ede3_ecb_encrypt(ecb_in,f_len,&len,ks,ks2,ks,DES_ENCRYPT);
/*
input:待加密数据流
length:待加密数据流长度
count :加密后长度
ks1,ks2,ks3:密钥
enc:TRUE为加密,FALSE为解密
返回:加密后数据流
*/
unsigned char * des_ede3_ecb_encrypt(const unsigned char *input,
long length,long * count, des_key_schedule ks1, des_key_schedule ks2,
des_key_schedule ks3, int enc)
{
//register DES_LONG tin0,tin1;
register unsigned char *in;
register unsigned char *out;
register long i,blocks;
unsigned char * pin,*pout;
int iLen;
DES_LONG tin[2];
if (enc)
{
iLen = (length % 8)==0?8:length % 8;
in = (unsigned char *)malloc(length+(8 -iLen));
pin=in;
out= (unsigned char *)malloc(length+(8 -iLen));
pout=out;
memcpy((void *)in,input,length);
for(i=length;i<length + (8-iLen);i++){
in[i] = iLen;
}
blocks=(length+(8-iLen))/8;
for (i=0;i<blocks; i++)
{
c2l(in,tin[0]);
c2l(in,tin[1]);
des_encrypt3((DES_LONG *)tin,ks1,ks2,ks3);
l2c(tin[0],out);
l2c(tin[1],out);
}
*count=length + (8-iLen);
free(pin);
return pout;
}
else
{
in=(unsigned char *)input;
out= (unsigned char *)malloc(length);
pout=out;
blocks=length/8;
for (i=0;i<blocks; i++)
{
c2l(in,tin[0]);
c2l(in,tin[1]);
des_decrypt3((DES_LONG *)tin,ks1,ks2,ks3);
l2c(tin[0],out);
l2c(tin[1],out);
}
iLen=pout[length-1];
iLen=length -(8-iLen);
*count=iLen;
return pout;
}
}
描述
OpenSSL 是目前用途非常广的一个加密解密库 ,LockBox 是 Delphi 实现的一个加密解密库,当用两者其中一个进行加密,另外一个解密时,往往是解不出来,这就非常令人困惑:既然算法相同,只是实现不一样,应该是没有问题才对?!其实,问题出在 RFC 文档没有规定到的一些细节上,比如:当用 DES 加密时,如果待加密字节长度不够 8 的倍数时该怎么补齐?
代码示例
DES 都是按照 8 字节进行加密操作的 ,Triple DES 也是一样 , 只不过 DES 是用一个 Key 进行加密解密 , Triple DES 是用 3 个 Key 进行。 OpenSSL 和 LockBox 两者的算法是一致的,差别在于两者在加密字节尾部的操作, OpenSSL 的明文有多少个字节,加密出来的密文就有多少个字节。 LockBox 处理是当明文不是 8 的倍数就补齐够 8 的倍数,并且最后一个字节是 最后 8 个加密字节 有几个是有效的。所以, OpenSSL 要做些改动,代码如下:
1 、 OpenSSL 加密 :
Ret = Gen2RandomKeys(&k1, &k2, &ks1, &ks2);
ecb_out = des_ede3_ecb_encrypt(ecb_in,f_len,&len,ks1,ks2,ks1, DES_ENCRYPT);
// 这两个方法 OpenSSL 里面没有,要自己添加,代码如下:
/* 随机产生 Key 的方法:
k1,k2 是 key 的字节表示,用这两个连起来给 Lockbox 进行解密
ks1,ks2 是 OpenSSL 用来加密解密的分别对等 k1,k2 的对象
*/
int Gen2RandomKeys(des_cblock *k1, des_cblock *k2, des_key_schedule *ks1, des_key_schedule *ks2)
{
int j;
char str[64];
printf("Generate random keys.\n");
srand(time(NULL));
for(j = 0; j < 63; j++){
str[j] = rand() * 255;
printf("%d ", str[j]);
}
str[63] = '\0';
printf("\n");
des_string_to_2keys(str, k1, k2);
if ((j = des_set_key_checked(k1, *ks1)) != 0){
printf("Key error %d\n", j);
return 0;
}
if ((j = des_set_key_checked(k2, *ks2)) != 0){
printf("Key2 error %d\n", j);
return 0;
}
return 1;
}
/*
加密解密方法:
input: 待加密数据流
length :待加密数据流长度
count : 加密后长度
ks1,ks2,ks3: 密钥
enc:TRUE 为加密 ,FALSE 为解密
返回:加密后数据流
*/
unsigned char * des_ede3_ecb_encrypt(const unsigned char *input,
long length,long * count, des_key_schedule ks1, des_key_schedule ks2,
des_key_schedule ks3, int enc)
{
//register DES_LONG tin0,tin1;
register unsigned char *in;
register unsigned char *out;
register long i,blocks;
unsigned char * pin,*pout;
int iLen;
DES_LONG tin[2];
if (enc)
{
iLen = (length % 8)==0?8:length % 8;
in = (unsigned char *)malloc(length+(8 -iLen));
pin=in;
out= (unsigned char *)malloc(length+(8 -iLen));
pout=out;
memcpy((void *)in,input,length);
for(i=length;i<length + (8-iLen);i++){
in[i] = iLen;
}
blocks=(length+(8-iLen))/8;
for (i=0;i<blocks; i++)
{
c2l(in,tin[0]);
c2l(in,tin[1]);
des_encrypt3((DES_LONG *)tin,ks1,ks2,ks3);
l2c(tin[0],out);
l2c(tin[1],out);
}
*count=length + (8-iLen);
free(pin);
return pout;
}
else
{
in=(unsigned char *)input;
out= (unsigned char *)malloc(length);
pout=out;
blocks=length/8;
for (i=0;i<blocks; i++)
{
c2l(in,tin[0]);
c2l(in,tin[1]);
des_decrypt3((DES_LONG *)tin,ks1,ks2,ks3);
l2c(tin[0],out);
l2c(tin[1],out);
}
iLen=pout[length-1];
iLen=length -(8-iLen);
*count=iLen;
return pout;
}
}
2 、 LockBox 解密:
// TripleDESEncryptStream 定义:
// InStream: 待操作流,密文或明文
// OutStream :结果流 : 明文或密文
// Key:Key ,长度只有 128 的
// Encrypt:True 为加密, False 为解密
procedure TripleDESEncryptStream(InStream, OutStream : TStream;
const Key : TKey128; Encrypt : Boolean);
RSA 加密解密
RSA 加密解密 OpenSSL 和 LockBox 的差异主要在于 Key 的表示方法、加密块增加 padding 位置、明文密文的字节序上。代码示例如下:
1 、 OpenSSL 加密:
void Encrypt()
{
static int BUFFER_SIZE=256;
RSA *key,*tokey;
char *infile=NULL,*fromfile,*tofile=NULL;
BIO *in_=NULL,*fromio,*toio=NULL;
int modulus=0;
long num,i,j;
int plen,outlength;
unsigned char *ptext,*p,*p1;
BUF_MEM * buf,*outbuf;
BIGNUM ff,ret,ee,nn;
//rsa 最新补齐字节
static int cRSAMinPadBytes = 11;
// 加密块明文最大长度
int plainbytes;
// 加密块明文 实际长度
int actualbytes;
// 加密块数
int blockcount;
//lockbox 产生的公钥 , 格式 ASN.1
infile= "D:\\openssl-0.9.6m\\test\\PublicKey.dat";
// 明文文件
fromfile= "D:\\openssl-0.9.6m\\test\\ctext.dat";
// 保存密文目的文件
tofile= "D:\\openssl-0.9.6m\\test\\ctext.dat.enc";
// 文件 IO
in_=BIO_new(BIO_s_file());
fromio=BIO_new(BIO_s_file());
toio=BIO_new(BIO_s_file());
// 上下文
BN_CTX *ctx=NULL;
ptext=(unsigned char *)OPENSSL_malloc(BUFFER_SIZE);
p =(unsigned char *)OPENSSL_malloc(BUFFER_SIZE);
BN_init(&ff);
BN_init(&ret);
BN_init(&ee);
BN_init(&nn);
ctx=BN_CTX_new();
if (BIO_read_filename(in_,infile) <= 0)
{
//perror(infile);
return;
}
// 取得 lockbox key
key=d2i_RSAPublicKey_bio(in_,NULL);
tokey = RSA_new();
// 转化成 openssl 认识的 key
memset(p,0,sizeof(p)-1);
num=BN_num_bytes(key->n);
memcpy(p,key->n->d,num);
tokey->n=BN_bin2bn(p,num,tokey->n);
// 这里先取得明文加密长度
plainbytes=num-cRSAMinPadBytes;
// 转化成 openssl 认识的 key
memset(p,0,sizeof(p)-1);
num=BN_num_bytes(key->e);
memcpy(p,key->e->d,num);
tokey->e=BN_bin2bn(p,num,tokey->e);
// 读取明文
if(in_ != NULL) BIO_free(in_);
if (BIO_read_filename(fromio,fromfile) <= 0)
{
return;
}
plen=0;
if ((buf=BUF_MEM_new()) == NULL) return;
for (;;)
{
if (!BUF_MEM_grow(buf,(int)plen+BUFSIZ)) return;
i=BIO_read(fromio,&(buf->data[plen]),BUFSIZ);
if (i <= 0) break;
plen+=i;
}
blockcount = plen / plainbytes;
if(plen% plainbytes> 0)blockcount++;
// 开始加密
//loop begin
//for 0 to count -1 block
//every block content less than
outlength= 0;
if ((outbuf=BUF_MEM_new()) == NULL) return;
for(i=0;i<blockcount;i++){
p1=(unsigned char *)&(buf->data[i*plainbytes]);
actualbytes=plainbytes;
if(i==blockcount-1) actualbytes=plen - (plainbytes * i);
memset(p,0,sizeof(p)-1);
// 字节倒序
for(j=0;j<actualbytes;j++){
p[j]=p1[actualbytes-1-j];
}
num = BN_num_bytes(tokey->n);
// 补齐按 PKCS1
RSA_padding_add_PKCS1_type_2(ptext,num,p,actualbytes);
/* make data into a big number */
BN_bin2bn(ptext,num,&ff);
// 加密
num = BN_mod_exp(&ret,&ff,tokey->e,tokey->n,ctx);
num=BN_num_bytes(&ret);
BN_bn2bin(&ret,p);
memset(ptext,0,sizeof(ptext)-1);
// 取回字节倒序
for(j=0;j<num;j++){
ptext[j]=p[num-1-j];
}
if (!BUF_MEM_grow(outbuf,(int)outlength+num)) return;
memcpy(&(outbuf->data[outlength]),ptext,num);
outlength=outlength+num;
//loop end
}
// 写流
BIO_write_filename(toio,tofile);
BIO_write(toio,outbuf->data,outlength);
if(toio != NULL) BIO_free(toio);
if(buf != NULL) BUF_MEM_free(buf);
if (ctx != NULL) BN_CTX_free(ctx);
BN_clear_free(&ff);
BN_clear_free(&ret);
BN_clear_free(&ee);
BN_clear_free(&nn);
if (p != NULL)
{
OPENSSL_cleanse(p,BUFFER_SIZE);
OPENSSL_free(p);
}
if (ptext != NULL)
{
OPENSSL_cleanse(ptext,BUFFER_SIZE);
OPENSSL_free(ptext);
}
return;
}
2 、 LockBox 调用示例:
// RSAEncryptStream 定义:
// InStream: 待操作流,密文或明文
// OutStream :结果流 : 明文或密文
// Key:Key ,长度只有 128 的
// Encrypt:True 为加密, False 为解密
RSAEncryptStream(instream, outStream, lbRsaKey, False);