1. 程式人生 > >openssl 學習之enc中salt引數解析

openssl 學習之enc中salt引數解析

當我們使用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;
	}