[Computer and Network Security] Programming with OpenSSL

来源:互联网 发布:hp1010墨盒清零软件 编辑:程序博客网 时间:2024/05/01 10:39
零、SSL和OpenSSL
SSL是HTTPS(安全的HTTP)背后的安全协议,它可以保证在TCP上工作的任何协议的安全。OpenSSL是对SSL全功能的实现,包括TLS(传输层安全)。

OpenSSL综述:OpenSSL是一个开源的密码学函数的实现。它包括密码学函数的可执行命令和一个API库,程序员可以使用这个库来开发密码学程序。

一、BIGNUM
公钥密码学需要处理非常大的整数,标准的C数据类型是不够的。BIGNUM包对数字的上界实际上是没有限制的。
头文件:#include <openssl/bn.h>
1.1 初始化&撤销BIGNUM

BIGNUM static_bn,*dynamic_bn;/*initialize a static BIGNUM*/BN_init(&static_bn);/*allocate a dynamic BIGNUM*/dynamic_bn=BN_new();/*free the BIGNUMs*/BN_free(dynamic_bn);BN_free(&static_bn);
1.2 复制BIGNUMS

当你复制一个BIGNUM对象时需要深拷贝

BIGNUM a,b,*c;BN_copy(&a,&b);/*copies b to a*/c = BN_dup(&b);/*creates c and initialize it to same value as b*/
1.3 BIGNUM到二进制
当你要把BIGNUM保存在一个文件中或者通过socket连接传送时,你需要将BIGNUM转为二进制表现方式。
简单地,可以将BIGNUM转换成十进制或十六进制表达。

BIGNUM *num;/*converting from BIGNUM to binary*/len=BN_num_bytes(num);buf=(unsigned char*)calloc(len,sizeof(unsigned char));len=BN_bn2bin(num,buf);/*converting from binary to BIGNUM*/BN_bin2bn(buf,len,num);num=BN_bin2bn(buf,len,NULL);

1.4 模运算

BIGNUM *r,*g,*x,*p;BN_CTX *ctx=BN_CTX_new();/*store temporary results*//*..call BN_new() on r,g,x,p*//*r=g^x mod p*/BN_mod_exp(r,g,x,p,ctx);/*when done,free r,g,x,p and ctx*/
BN_CTX保存操作的临时值,为了提高性能。
1.5 生成伪随机指数
BIGNUM *prime = BN_generate_prime();

二、OpenSSL中的对称密码
头文件:#include <openssl/aes.h>
找到被加密消息的实际长度,使其长度为128bits的倍数。

unsigned int message_len = strlen((char*)input_string)+1;//including '\0'unsigned encrypt_len = (message_len % AES_BLOCK_SIZE == 0)?message_len:(message_len/AES_BLOCK_SIZE+1)*AES_BLOCK_SIZE;
定义密钥(假设128bits)

unsigned char key[16];AES aes;int ret = AES_set_encrypt_key(key,128,&aes);//ret<0->error
定义IV

unsigned char iv[AES_BLOCK_SIZE];memset(iv,0,AES_BLOCK_SIZE);
加密明文(注意iv会被更新)

AES_cbc_encrypt(input_string,encrypt_string,encrypt_len,&aes,iv,AES_ENCRYPT);
解密密文,解密端必须同步iv和密钥,其中iv可以明文传输,key必须秘密传输

AES_set_decrypt_key(key,128,&aes);memset(iv,0,AES_BLOCK_SIZE);...AES_cbc_encrypt(encrypt_string,decrypt_string,encrypt_len,&aes,iv,AES_DECRYPT);
三、OpenSSL中的公钥密码
关注:RSA的公钥加密,Diffie-Hellman的密钥管理,DSA的数字签名。
在BIGNUM上的公钥密码操作
typedef struct{BIGNUM *n;//public modulusBIGNUM *e;//public exponentBIGNUM *d;//private exponentBIGNUM *p;//secret prime factorBIGNUM *q;//...}RSA;
3.1 RSA in OpenSSL
Bob首先生成RSA密钥,例如1024位,指数为3.
#include <openssl/rsa.h>RSA ×rsa = RSA_generate_key(1024,3,NULL,NULL);
Bob把公钥传递给Alice
#include <openssl/bn.h>unsigned char* n_b = (unsigned char*)calloc(RSA_size(rsa),sizeof(unsigned char));unsigned char* e_b = (unsigned char*)calloc(RSA_size(rsa),sizeof(unsigned char));int n_size = BN_bn2bin(rsa->n,n_b);int b_size = BN_bn2bin(rsa->e,e_b);
Alice从公共参数中构建RSA上下文
RSA *encrypt_rsa = RSA_new();encrypt_rsa->n = BN_bin2bn(n_b,n_size,NULL);encrypt_rsa->e = BN_bin2bn(e_b,b_size,NULL);
Alice现在可以加密数据
unsigned char* encrypt_string=(unsigned char*)calloc(RSA_size(encrypt_rsa),sizeof(unsigned char));int encrypt_size = RSA_public_encrypt(strlen((char*)input_string),input_string,encrypt_string,encrypt_rsa,RSA_PKCS1_OAEP_PADDING);
推荐使用RSA_PKCS1_OAEP_PADDING,输入信息块的尺寸必须比RSA(size)-41要小
Bob随后可以解密
unsigned char* decrypt_string = (unsigned char*)calloc(RSA_size(rsa),sizeof(unsigned char));int decrypt_size = RSA_private_decrypt(encrypt_size,encrypt_string,decrypt_string,rsa,RSA_PKCS1_OAEP_PADDING);
在加密之前填充数据包:
RSA的填充机制:
RSA_PKCS1_PADDING:明文长度要小于RSA_size(rsa)-11
RSA_PKCS1_OAEP_PADDING:明文长度要小于RSA_size(rsa)-41
RSA_SSLV23_PADDING:很少用
RSA_NO_PADDING:假设调用者执行填充,明文长度必须等于RSA_size(rsa),不推荐使用。
你可以在命令行中生成参数并且将其保存在一个PEM文件中。
3.2 Diffie-Hellman in OpenSSL
头文件:#include <openssl/dh.h>
DH的结构体
typedef struct{BIGNUM *p; //prime numberBIGNUM *g; //generatorBIGNUM *pub_key; //public keyBIGNUM *pri_key; //private key}DH;
在命令行中生成DH参数(例如指数p和生成器g)
%openssl dhparam -out dh1024.pem 1024
文件中读参数
#include <openssl/dh.h>#include <openssl/pem.h>FILE *fp = fopen("dh1024.pem","r");DH *dh1 = PEM_read_DHparams(fp,NULL,NULL,NULL);
文件中没有密钥,你需要单独生成密钥
unsigned char* key1 = (unsigned char*)calloc(DH_size(dh1),sizeof(unsigned char));unsigned char* key2 = (unsigned char*)calloc(DH_size(dh2),sizeof(unsigned char));DH_compute_key(key1,dh2_pub_key,dh1);DH_compute_key(key2,dh1_pub_key,dh2);
3.3 DSA in OpenSSL
头文件:<openssl/dsa.h>
发送者发送DSA参数(比如,1024位)
DSA *dsa = DSA_generate_parameters(1024,NULL,0,NULL,NULL,NULL);
生成密钥
DSA_generate_key(dsa);
对消息签名
unsigned char* sign_string = (unsigned char*)calloc(DSA_size(dsa),sizeof(unsigned char));int ret = DSA_sign(0,input_string,strlen((char*)input_string),sign_string,&sig_len,dsa);
认证消息
int is_valid = DSA_verify(0,input_string,strlen((char*)input_string),sign_string,sig_len,dsa);
is_valid=1意味着认证完成,0意味着错误签名。
四、OpenSSL的哈希函数
头文件:#include <openssl/md5.h>
MD5_CTX hash_ctx:MD5_Init(&hash_ctx);//initalizechar input_string[100];strcpy(input_string,"abcdefg");MD5_Update(&hash_ctx,input_string,strlen(input_string));//updateunsigned char hash_ret[16];MD5_Final(hash_ret,&hash_ctx);//compute the hash value
五、OpenSSL中的证书
目标:认证一个通过公钥签名的CA发布的证书
主要思想:在命令行中生成证书,并且在程序中通过API来调用certs
第一步,我们需要一个CA,首先创建一个子签名的证书。
%openssl genrsa -aes128 -out cakey.pem 1024
%openssl req -x509 -newkey rsa:1024 -out cacert.pem -outform PEM -days 365 -key cakey.pem
使用'-days'选项指定过期时间为1年,证书被编码为PEM格式,可以通过以下命令查看其内容
%openssl x509 -in cacert.pem -text -noout
创建了两个文件:cakey.pem---CA的私钥;cacert.pem---CA的证书
第二步,当用户想要应用证书时,CA会生成一个新的公钥和私钥对,相应的证书请求如下:
%openssl genrsa -aes128 -out key.pem 1024
%openssl req -new -key key.pem -keyform PEM -out req.pem -outform PEM
上面的命令当在证书发布时会用口令短语(passphrase)来加密私钥文件(key.pem),它会提示你输入口令短语。
第三步,CA会基于请求生成一个证书
%openssl ca -in req.pem -out cert.pem -config ca.conf
注意在调用此命令之前,我们必须准备好两个文件:index.txt(可以是空的),和序列(存储一个数字,例如01)。私钥文件(key.pem)和证书(cert.pem)将会给用户。
认证某证书是否被正确地创建,可运行如下命令:
%openssl verify -CAfile cacert.pem cert.pem

六、在OpenSSL中使用Cert签名/认证的方法
签名的人:使用私钥去签名;发布公钥证书
认证的人:使用证书中的公钥去认证
这里我们证明EVP API的使用
EVP API提供了一个通用接口来进行密码openssl的给出。当你更改使用新的密码学算法时,你仅仅需要在初始化的过程中注册新的算法。

6.1 在Openssl中使用证书来签名
第一步,载入私钥文件
当文件被加密时,我们需要指定使用何种加密算法,为了防止麻烦,我们仅仅需要调用OpenSSL添加所有algorithm()来包含所有可能需要的密码和摘要算法。
#include <openssl/evp.h>#include <openssl/pem.h>...OpenSSL_add_all_algorithms();...//load the private keyFILE *fp = fopen("key.pem","r");EVP_PKEY *priv_key = PEM_read_PrivateKey(fp,NULL,NULL,(char*)"5470");fclose(fp);if (priv_key==NULL){fprintf(stderr,"cannot read private key.\n");exit(-1);}
第二步,签名消息摘要,你不需要签名整个消息,仅仅需要签名消息摘要
int sig_len=128;//1024-bit keyunsigned char sign_string[128];EVP_MD_CTX evp_md_ctx;EVP_SignInit(&evp_md_ctx,EVP_sha1());EVP_SignUpdate(&evp_md_ctx,input_string,strlen((char*)input_string));if(EVP_SignFinal(&evp_md_ctx,sign_string,&sig_len,priv_key)==0){fprintf(stderr,"Unable to sign.\n");exit(-1);}
6.2 在openssl中使用证书来认证
第一步,在认证消息之前,我们需要先获得签名者的证书,证书中包含公钥。
X509 *cert;EVP_PKEY *pub_key;FILE *fp=fopen("cert.pem","r");if((cert=PEM_read_X509(fp,NULL,NULL,NULL))==NULL){fprintf(stderr,"cannot read certfile\n");exit(-1);}fclose(fp);if((pub_key=X509_get_pubkey(cert))==NULL){fprintf(stderr,"cannot read X509's public key\n");exit(-1);}
第二步,认证消息摘要
EVP_VerifyInit(&evp_md_ctx,EVP_sha1());EVP_VerifyUpdate(&evp_md_ctx,input_string,strlen((char*)input_string));if(EVP_VerifyFinal(&evp_md_ctx,sign_string,sig_len,pub_key)){printf("Verified\n");}else{printf("Wrong\n");}
七、SSL/TLS编程
SSL/TLS编程步骤
第1步,初始化SSL库,初始化所有的密码/哈希算法。
第2步,创建SSL上下文结构:指定SSL版本。
第3步,建立证书和密钥
       SSL服务器:服务器自己的证书(强制的)和CA证书(可选)
  SSL客户端:CA证书(强制的)用来认证服务器的证书和客户端自己的证书(可选)
第4步,建立证书认证
  可以指定链长度(认证深度verify_depth)
  客户端可以设置SSL_VERIFY_PEER来验证服务器的证书确实是CA发布
第5步,创建SSL结构和TCP/IP套接字并与其绑定
第6步,SSL握手
  当客户调用SSL_connect()来触发
  如果之前证书认证被指定,那么实际的认证工作在这里会执行
第7步,传输SSL数据
第8步,关闭SSL结构

OpenSSL的总结:
底层设计:BIGNUM:使用BIGNUM来建立你自己的密码学基元。
中层设计:密码学基元:使用不同的密码学基元组合成一个密码学系统。
高层设计:SSL编程,SSL实施的方法。




0 0
原创粉丝点击