1. 哈希算法
- 散列值的形式?原因?
答:散列值表现为是一串16进制数,但实质是一堆二进制数值但无法显示,为了直观显示,将二进制数值转为16进制数。 - 哈希算法的散列值长度?
答:MD4/MD5:散列值长度16byte,碰撞性已经被攻破;SHA1:散列值长度20byte;SHA2:包含有SHA224/SHA256/SHA384/SHA512; - 如何得到MD5的散列值?
答:通过MD5_Init()/MD5_Update()/MD5_Final()
三个函数或MD5()
函数来实现。其中前三个函数可以将数据分多次输入后再得到散列值,而MD5()
函数的数据只能一次性输入。
int MD5_Init(MD5_CTX *c);
c:传出参数,获取的变量;
返回值:成功 - 1;失败 - 其他;
int MD5_Update(MD5_CTX *c, const void *data, unsigned long len);
c:MD5_Init所获得的变量
data:要进行MD5运算的数据
len:data的长度
返回值:成功 - 1;失败 - 其他;
int MD5_Final(unsigned char *md, MD5_CTX *c);
md:传出参数,得到的散列值;
c:MD5_Init得到的变量
返回值:成功 - 1;失败 - 其他;
unsigned char *MD5(const unsigned char *d, unsigned long n, unsigned char *md);
d:进行MD5运算的字符串
n:d的长度
md:传出参数,得到的散列值
返回值:得到的散列值
- 示例得到哈希值?
#include <iostream>
#include <openssl/md5.h>
using namespace std;
void md5Test(const char *p)
{
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, p, strlen(p));
unsigned char md[MD5_DIGEST_LENGTH] = {0};
MD5_Final(md, &ctx);
cout << "md5: " << md << endl;
unsigned char res[MD5_DIGEST_LENGTH * 2 + 1];
for(int i = 0; i < MD5_DIGEST_LENGTH; i++)
{
sprintf(&res[i * 2], "%02x", md[i]);
}
cout << "md5: " << res << endl;
}
int main()
{
char *p = "hello world";
md5Test(p);
}
- 如何配置vs的openssl环境?
答:包含目录位于安装openssl的include文件路径中, 库目录位于安装openssl的lib目录中,附加依赖项为libcrypto.lib - 为什么转换的数据中含有中文,得到的结果不同?
答:中文需要先转成utf-8或其他码再进行哈希运算,编码方式不同,得到的结果则不同。 - 如何对哈希运算过程进行C++封装?
2. 非对称加密
- RSA密钥的长度?
答:RSA 算法密钥长度越长,安全性越好,加密解密所需时间越长。可以通过bits参数进行随便设置,单位为bit,一般设为1028;长度越长,生成密钥和加密的时间就越长。 - 如何生成RSA密钥对?
答:(1)申请一块内存;(2)生成密钥对,并分发;(3)释放内存
RSA *RSA_NEW(void);
返回值:申请的存储密钥的内存;
int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
rsa: RSA_NEW生成的结构体,用来存储密钥对;
bits:密钥的长度,单位为bit,一般最小为512,否则容易破解
e: BIG_NEW生成的结构体, BIGNUM *BN_NEW(void);
cb: 函数指针,通常为NULL
RSA *RSAPublickey_dup(RSA *rsa);
RSA *RSAPrivateKey_dep(RSA *rsa);
void generateKey()
{
RSA *r = RSA_NEW();
BIGNUM *e = BN_NEW();
BN_set_word(e, 10);
RSA_generate_key_ex(r, 1024, e, NULL);
RSA_free(r);
BN_free(e);
}
- 如何将密钥写入/读出磁盘?
答:(1)打开文件;(2)将密钥写入/读出;(3)关闭文件
int PEM_write_RSAPublicKey(FILE* fp, const RSA* r);
int PEM_write_RSAPrivateKey(FILE* fp, const RSA* r, const EVP_CIPHER* enc, unsigned char* kstr, int klen, pem_password_cb *cb, void* u);
fp:要写入密钥的磁盘文件,.pem格式
r: 生成的密钥
enc/kstr/klen/cb/u: 一般写NULL/NULL/0/NULL/NULL;
RSA* PEM_read_RSAPublicKey(FILE* fp, RSA** r, pem_password_cb *cb, void* u);
RSA* PEM_read_RSAPrivateKey(FILE* fp, RSA** r, pem_password_cb *cb, void* u);
fp: 要读出密钥的磁盘文件
r:传出参数,存储密钥的地址
cb/u:一般写NULL
- 如何配置vs环境?
答:包含目录、库目录安装路径lib文件夹、附加依赖项libcrypto.lib
FILE *fp = fopen("public.pem", "w");
PEM_write_RSAPublicKey(fp, r);
fclose(fp);
fp = fopen("private.pem", "w");
PEM_write_RSARrivateKey(fp, r, NULL, NULL, 0, NULL, NULL);
fclose(fp);
RSA *x = RSA_new();
FILE *fp = fopen("public.pem", "r");
PEM_read_RSAPublicKey(fp, &x, NULL, NULL);
RSA *x = RSA_new();
FILE *fp = fopen("private.pem", "r");
PEM_read_RSAPrivateKey(fp, &x, NULL, NULL);
- 如何用RSA算法加/解密?、
答:(1)生成密钥;(2)将密钥刷入磁盘;(3)从磁盘中提取公/私钥;(4)对数据进行加/解密;(5)将加/解密后的密/明文返回。
int RSA_public_encrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding);
int RSA_private_decrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding);
int RSA_private_encrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding);
int RSA_public_decrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding);
flen:要加密数据的长度,最大长度为密钥长度-11;若长度不够,则需要分组
from:要加密的数据
to:加密后的密文
rsa:公钥/私钥
padding:填充数据,大小为11,设置为RSA_PKCS1_PADDING自动填充。
- 为什么要填充数据?
答:加密后的数据是一堆二进制文件存放在buf中,填充数据用来分隔加密后的数据与buf中其他乱码
void generateKey()
{
RSA *r = RSA_new();
BIGNUM *e = BN_new();
BN_set_word(e, 111);
RSA_generate_key_ex(r, 1024, e, NULL);
FILE *fp = fopen("public.pem", "w");
PEM_write_RSAPublicKey(fp, r);
fclose(fp);
fp = fopen("private.pem", "w");
PEM_write_RSAPrivateKey(fp, r, NULL, NULL, 0, NULL, NULL);
fclose(fp);
RSA_free(r);
BN_free(e);
}
string getKeyToenCode()
{
RSA *x = RSA_new();
FILE *fp = fopen("public.pem", "r");
PEM_read_RSAPublicKey(fp, &x, NULL, NULL);
const char *s = "让编程改变世界, hello world,i'm coming";
char *buf = new char[1024];
memset(buf, 0, sizeof(buf));
int ret = RSA_public_encrypt(strlen(s), (const unsigned char *)s, (unsigned char *)buf, x, RSA_PKCS1_PADDING);
string str = string(buf, ret);
delete[] buf;
return str;
}
string getKeyToDeCode(string s)
{
RSA *x = RSA_new();
FILE *fp = fopen("private.pem", "r");
PEM_read_RSAPrivateKey(fp, &x, NULL, NULL);
char *buf = new char[1024];
memset(buf, 0, sizeof(buf));
int ret = RSA_private_decrypt(s.size(), (const unsigned char *)s.c_str(), (unsigned char *)buf, x, RSA_PKCS1_PADDING);
string str = string(buf, ret);
delete[]buf;
return str;
}
int main()
{
generateKey();
string str = getKeyToenCode();
str = getKeyToDeCode(str);
cout << "解密数据为:" << str << endl;
return 0;
}
- 如何对数据进行签名和验证?
答:A:(数据 * 哈希运算 * 私钥)= 密文, 密文+数据==>B ;
B:数据 * 哈希运算 == 密文 * 公钥 ?
int RSA_sign(int type, const unsigned char *m, unsigned int m_length, unsigned char *sigret, unsigned int *siglen, RSA *rsa);
int RSA_verify(int type, const unsigned char *m, unsigned int m_length, const unsigned char *sigbuf, unsigned int siglen, RSA *rsa);
type:哈希运算的方法,NID_MD5/NID_SH1...
m:要进行加密的数据
m_length: m的长度
sigret:加密后的密文,传出参数
sigbuf:要解密的密文,传入参数
siglen:sigret/sigbuf的长度,传出参数
rsa:加/解密的密钥
void signAndVerify()
{
const char *s = "让编程改变世界, hello world,i'm coming";
char buf[1024] = { 0 };
unsigned int len;
BIO *priBio = BIO_new_file("private.pem", "r");
RSA *priKey = RSA_new();
PEM_read_bio_RSAPrivateKey(priBio, &priKey, NULL, NULL);
RSA_sign(NID_sha1, (const unsigned char *)s, strlen(s), (unsigned char *)buf, &len, priKey);
BIO *pubBio = BIO_new_file("public.pem", "r");
RSA *pubKey = RSA_new();
PEM_read_bio_RSAPublicKey(pubBio, &pubKey, NULL, NULL);
int ret = RSA_verify(NID_sha1, (const unsigned char *)s, strlen(s), (const unsigned char *)buf, len, pubKey);
if (ret == 1)
{
cout << "校验成功" << endl;
}
else
{
cout << "校验失败" << endl;
}
}
3. 对称加密
- AES算法加密方式?特点?
答:密钥长度16、24、32byte,字节越大加密程度越高;将数据分成128bit每块进行逐块加密,加密后的密文与明文长度相同。 - 如何用AES算法对数据进行加密?
答:
int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
userKey: 获取密钥的字符串,随便填写,长度可选: 16byte, 24byte, 32byte
bits:密钥的长度,单位bit
key:传出参数,保存密钥的信息
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, size_t length, const AES_KEY *key, unsigned char *ivec, const int enc);
in:要加/解密的明/密文
out:传出参数,加/解密后的密/明文
length:in的长度,必须为16的整数倍,通过strlen(in) % 16判断,若余数不为0,则将length修改为(strlen(in)/16+1)*16
key: 密钥
ivec:初始化字符串,随便设置,和第一个明文分组进行位运算, 取16个字符加解密的时候保证该数组中的数据完全相同
enc: 加解密的标志
# define AES_ENCRYPT 1
# define AES_DECRYPT 0
void aestest()
{
char ivec[AES_BLOCK_SIZE];
const char* key = "1234567887654321";
const char* text = "AES是一套对称密钥的密码术,目前已广泛使用,用于替代已经不够安全的DES算法。所谓对称密钥,就是说加密和解密用的是同一个密钥,消息的发送方和接收方在消息传递前需要享有这个密钥。和非对称密钥体系不同,这里的密钥是双方保密的,不会让任何第三方知道。对称密钥加密法主要基于块加密,选取固定长度的密钥,去加密明文中固定长度的块,生成的密文块与明文块长度一样。显然密钥长度十分重要,块的长度也很重要。如果太短,则很容易枚举出所有的明文-密文映射;如果太长,性能则会急剧下降。AES中规定块长度为128 bit,而密钥长度可以选择128, 192或256 bit 。暴力破解密钥需要万亿年,这保证了AES的安全性。";
memset(ivec, 9, sizeof(ivec));
int length = strlen(text) + 1;
if (length % 16 != 0)
{
length = (length / 16 + 1) * 16;
}
AES_KEY encKey;
AES_set_encrypt_key((const unsigned char*)key, 128, &encKey);
char *encText = new char[length];
AES_cbc_encrypt((const unsigned char*)text,
(unsigned char*)encText, length, &encKey, (unsigned char*)ivec, AES_ENCRYPT);
AES_KEY decKey;
memset(ivec, 9, sizeof(ivec));
AES_set_decrypt_key((const unsigned char*)key, 128, &decKey);
char *decText = new char[length];
AES_cbc_encrypt((const unsigned char*)encText, (unsigned char*)decText,
length, &decKey, (unsigned char*)ivec, AES_DECRYPT);
cout << "解密之后的数据: " << decText << endl;
}