openssl 學習之enc中salt引數解析
阿新 • • 發佈:2019-01-09
當我們使用openssl enc 命令工具進行加密的時候,涉及到一個引數 即salt,下面簡單分析一下salt引數。salt說白了就是一個隨機數,salt與passwd串聯,然後計算其hash值來防禦dictionary attacks 和預計算的rainbow table 攻擊。在openssl 的enc 命令中,通過salt 與passwd 來生加密(解密)金鑰和初始向量IV。
一,openssl中enc 中salt相關引數
需要注意的就是,當僅設定-K時,還需要設定IV。
-pass arg the password source. For more information about the format of arg see the PASS PHRASE ARGUMENTS section in openssl(1). -salt use a salt in the key derivation routines. This is the default. -nosalt don't use a salt in the key derivation routines. This option SHOULD NOT be used except for test purposes or compatibility with ancient versions of OpenSSL and SSLeay. -k password the password to derive the key from. This is for compatibility with previous versions of OpenSSL. Superseded by the -pass argument. -kfile filename read the password to derive the key from the first line of filename. This is for compatibility with previous versions of OpenSSL. Superseded by the -pass argument. -nosalt do not use a salt -salt use salt (randomly generated or provide with -S option) when encrypting (this is the default). -S salt the actual salt to use: this must be represented as a string of hex digits. -K key the actual key to use: this must be represented as a string comprised only of hex digits. If only the key is specified, the IV must additionally specified using the -iv option. When both a key and a password are specified, the key given with the -K option will be used and the IV generated from the password will be taken. It probably does not make much sense to specify both key and password. -iv IV the actual IV to use: this must be represented as a string comprised only of hex digits. When only the key is specified using the -K option, the IV must explicitly be defined. When a password is being specified using one of the other options, the IV is generated from this password.
二,openssl中enc.c 檔案分析
enc.c檔案是openssl中enc命令對應的原始碼,我們專注於salt相關引數,故只摘錄部分程式碼。
首先我們來看一下,openssl中呼叫 EVP_BytesToKey通過passwd和salt來生成Key 和IV,在enc.c 中有如下程式碼
//line 555 Opensl1.0.1c EVP_BytesToKey(cipher,dgst,sptr,(unsigned char *)str, strlen(str),1,key,iv); //函式原型 //line 115 Evp_key.c Openssl1.0.1c int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md, const unsigned char *salt, const unsigned char *data, int datal, int count, unsigned char *key, unsigned char *iv)
通過函式原型,除了str 引數,其他引數我們非常容易看清其意思。我們向前查詢,發現各個引數的意思以及具體如何定義的。
1,dgst 為MD5
//line 340~343 Openssl1.0.1c
//摘要演算法使用MD5
if (dgst == NULL)
{
dgst = EVP_md5();
2,sptr 為salt
//line513~ 553 if (str != NULL) { /* Salt handling: if encrypting generate a salt and * write to output BIO. If decrypting read salt from * input BIO. */ unsigned char *sptr; if(nosalt) sptr = NULL;//如果引數制定nosalt 則sptr為NULL else { if(enc) { /**line 270~274 使用者指定salt值 **else if (strcmp(*argv,"-S") == 0) **{ ** if (--argc < 1) goto bad; ** hsalt= *(++argv); **} **/ if(hsalt) { if(!set_hex(hsalt,salt,sizeof salt)) { BIO_printf(bio_err,"invalid hex salt value\n"); goto end; } } else if (RAND_pseudo_bytes(salt, sizeof salt) < 0) goto end; /* If -P option then don't bother writing */ if((printkey != 2)&& (BIO_write(wbio,magic, sizeof magic-1) != sizeof magic-1 || BIO_write(wbio,(char *)salt,sizeof salt) != sizeof salt)) { BIO_printf(bio_err,"error writing output file\n"); goto end; } } else if(BIO_read(rbio,mbuf,sizeof mbuf) != sizeof mbuf || BIO_read(rbio,(unsigned char *)salt,sizeof salt) != sizeof salt){ BIO_printf(bio_err,"error reading input file\n"); goto end; } else if(memcmp(mbuf,magic,sizeof magic-1)) { BIO_printf(bio_err,"bad magic number\n"); goto end; } sptr = salt; }
3,str為passwd
//line 180~184 Openssl1.0.1c
else if (strcmp(*argv,"-pass") == 0) // passwd 引數,新版本,替代-k
{
if (--argc < 1) goto bad;
passarg= *(++argv);
}//line 223~227 Openssl1.0.1celse
if (strcmp(*argv,"-k") == 0)// passwd 引數,舊版本,建議使用-pass引數
{
if (--argc < 1)
goto bad;
str= *(++argv);
}
//line 414~421 Openssl1.0.1c
if(!str && passarg) {
if(!app_passwd(bio_err, passarg, NULL, &pass, NULL)) {
BIO_printf(bio_err, "Error getting password\n");
goto end;
}
str = pass;
}
三,EVP_BytesToKey 函式
EVP_BytesToKey函式的功能就是生產key和iv。
int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md,
const unsigned char *salt, const unsigned char *data, int datal,
int count, unsigned char *key, unsigned char *iv)
{
EVP_MD_CTX c;
unsigned char md_buf[EVP_MAX_MD_SIZE];
int niv,nkey,addmd=0;
unsigned int mds=0,i;
int rv = 0;
nkey=type->key_len;
niv=type->iv_len;
OPENSSL_assert(nkey <= EVP_MAX_KEY_LENGTH);
OPENSSL_assert(niv <= EVP_MAX_IV_LENGTH);
if (data == NULL) return(nkey);
EVP_MD_CTX_init(&c);
for (;;)
{
if (!EVP_DigestInit_ex(&c,md, NULL))
return 0;
if (addmd++)
if (!EVP_DigestUpdate(&c,&(md_buf[0]),mds))
goto err;
// 將passwd和salt串聯進行hash
if (!EVP_DigestUpdate(&c,data,datal))
goto err;
if (salt != NULL)
if (!EVP_DigestUpdate(&c,salt,PKCS5_SALT_LEN))
goto err;
if (!EVP_DigestFinal_ex(&c,&(md_buf[0]),&mds))
goto err;
for (i=1; i<(unsigned int)count; i++)// 對hash結果再進行count次hash
{
if (!EVP_DigestInit_ex(&c,md, NULL))
goto err;
if (!EVP_DigestUpdate(&c,&(md_buf[0]),mds))
goto err;
if (!EVP_DigestFinal_ex(&c,&(md_buf[0]),&mds))
goto err;
}
i=0;
if (nkey)//設定key
{
for (;;)
{
if (nkey == 0) break;
if (i == mds) break;
if (key != NULL)
*(key++)=md_buf[i];
nkey--;
i++;
}
}
if (niv && (i != mds))//設定IV
{
for (;;)
{
if (niv == 0) break;
if (i == mds) break;
if (iv != NULL)
*(iv++)=md_buf[i];
niv--;
i++;
}
}
if ((nkey == 0) && (niv == 0)) break;
}
rv = type->key_len;
err:
EVP_MD_CTX_cleanup(&c);
OPENSSL_cleanse(&(md_buf[0]),EVP_MAX_MD_SIZE);
return rv;
}