Using crypto++ to encrypt and decrypt strings

Hi guys, I have been using crypto++ recently and I have managed to make most of my program, except the username and password authentication.

I wanted to make it so that the username + password that is encrypted in the file is compared to the newly entered username + password which is encrypted before comparison, so that they are only compared in encrypted form.

But unfortunately each time I encrypt them, the output is always different (probably a good thing).

Should I just decrypt the data stored in the file and compare the non-encrypted username + password. Or is there another way of doing this?

I am using the default encryption with MAC shown in the test.cpp, but with AES - CBC mode of operation.
If you store the SHA/MD5 hash of the username + password you can compare it to any newly entered username + password hash quite easily.

http://www.cryptopp.com/wiki/Hash_Functions
Could you give me some guidance in how to find and store the hash? I have just been looking at the code and I have no idea when or if a hash is used.

Here is the code used to encrypt it, (userpass is being encrypted, and cipherText is the enrypted output):

1
2
3
DefaultEncryptorWithMAC encryptor(tempPassword, new HexEncoder(new StringSink(cipherText)));
		encryptor.Put((byte *)userpass, strlen(userpass));
		encryptor.MessageEnd();


1
2
3
4
5
6
7
8
9
10
11
12
13
14
class DefaultEncryptor : public ProxyFilter
{
public:
	DefaultEncryptor(const char *passphrase, BufferedTransformation *attachment = NULL);
	DefaultEncryptor(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment = NULL);

protected:
	void FirstPut(const byte *);
	void LastPut(const byte *inString, size_t length);

private:
	SecByteBlock m_passphrase;
	CBC_Mode<Default_BlockCipher>::Encryption m_cipher;
};


1
2
3
4
5
6
7
8
9
10
11
12
13
class DefaultEncryptorWithMAC : public ProxyFilter
{
public:
	DefaultEncryptorWithMAC(const char *passphrase, BufferedTransformation *attachment = NULL);
	DefaultEncryptorWithMAC(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment = NULL);

protected:
	void FirstPut(const byte *inString) {}
	void LastPut(const byte *inString, size_t length);

private:
	member_ptr<DefaultMAC> m_mac;
};
A hash is a one-way encrytion (e.g. MD5 or SHA) for a string, file, etc. In the case of a user/password pair the entire pair can be transformed into a hash string that is unique to the pair. This can be stored so that even in the event of the hash being compromised it cannot be decrypted to reveal the original user/password pair. In the case of your example the `cipherText` variable will be the hash of the string `userpass.` You would store that in a file, database or data structure that you can retrieve for later password authentication.
Ok, I know understand how to make and store a Hash :), but I want to salt the username + password combination before its made into a hash.

How would I do this?


Thanks for your help
Could you perhaps append the user/pass and pass it in as a salt for PBKDF2? The templated function definition is in pwdbased.h and documentation here:

http://www.cryptopp.com/docs/ref/class_p_k_c_s5___p_b_k_d_f2___h_m_a_c.html
So looking at this example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
AutoSeededX917RNG<AES> rng;

	SecByteBlock iv(AES::BLOCKSIZE);
	rng.GenerateBlock(iv,iv.size());

	// See NIST SP 800-132 for detailed recommendations on length, generation and
	// format of the salt. This test program will just generate a random one. That
	// might not be sufficient for every application.
	SecByteBlock pwsalt(AES::DEFAULT_KEYLENGTH);
	rng.GenerateBlock(pwsalt,pwsalt.size());

	SecByteBlock derivedkey(AES::DEFAULT_KEYLENGTH);
	
	cout << "Password is " << Password << endl;
	cout << "Deriving key from password:" << endl;
	cout << endl << derivedkey << endl;

	PKCS5_PBKDF2_HMAC<SHA256> pbkdf;	
	pbkdf.DeriveKey(
		// buffer that holds the derived key
		derivedkey, derivedkey.size(),
		// purpose byte. unused by this PBKDF implementation.
		0x00,
		// password bytes. careful to be consistent with encoding...
		(byte *) Password.data(), Password.size(),
		// salt bytes
		pwsalt, pwsalt.size(),
		// iteration count. See SP 800-132 for details. You want this as large as you can tolerate.
		// make sure to use the same iteration count on both sides...
		iterations
		);
	cout << "Done" << endl;

	cout << endl << derivedkey << endl;

	string cipherText;

	CBC_Mode<AES>::Encryption aesencryption(derivedkey,derivedkey.size(),iv);
	// encrypt message using key derived above, storing the hex encoded result into ciphertext
	StringSource encryptor(userpass, true, new StreamTransformationFilter(aesencryption, new HexEncoder( new StringSink(cipherText))) );

	// hex encode salt and IV for "transport"
	string hexsalt, hexiv;
	ArraySource saltEncoder(pwsalt,pwsalt.size(), true, new HexEncoder(new StringSink(hexsalt)));

	
	ArraySource ivEncoder(iv,iv.size(), true, new HexEncoder(new StringSink(hexiv)));
	cout << "Salt: " << hexsalt << endl;
	cout << "IV: " << hexiv << endl;
	cout << "Ciphertext: " << cipherText << endl;


What is the derived key?

As I put a ' cout' before and after the pbkdf.DeriveKey(...), and they were both the same.


Also is there a way to convert a SecByteBlock to a string, as I want to use this hash function:

1
2
3
4
5
6
CryptoPP::SHA1 sha1;
std::string source = "Hello";  //This is the salted password+username, so source can be replaced by derivedkey
std::string hash = "";
CryptoPP::StringSource(source, true, new CryptoPP::HashFilter(sha1, new CryptoPP::HexEncoder(new CryptoPP::StringSink(hash))));

cout << hash;
I don't know if this is too simplistic an answer for your problem but...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <cryptopp/hex.h>
#include <cryptopp/sha.h>
#include <cryptopp/base64.h>
#include <iostream>
#include <string>

int main()
{
  CryptoPP::SHA256 hash;
  byte digest[CryptoPP::SHA256::DIGESTSIZE];
  std::string username, password, salt, output;
  std::cout << "Enter username: ";
  std::getline(std::cin,username);
  std::cout << std::endl << "Enter password: ";
  std::getline(std::cin,password);
  salt = username + password;

  hash.CalculateDigest(digest,(const byte *)salt.c_str(),salt.size());

  CryptoPP::HexEncoder encoder;
  CryptoPP::StringSink *SS = new CryptoPP::StringSink(output);
  encoder.Attach(SS);
  encoder.Put(digest,sizeof(digest));
  encoder.MessageEnd();

  std::cout << "The username/password salted hash is => " << output << std::endl;
  return 0;
}


Would allow you to store one hash based on both username and password.
This is a bit confusing, firstly I create a new salt, iv and the encrypt the username + password.

But everytime I read the salt, IV and create a new hash from the entered username and password and compare that to the stored hash, they are always different!

Only the derived key is ever the same.

This is the code:

This function creates the salt, iv, derived key and encrypted username+password when a user first logs in:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
void EncryptUserpass(int iterations, string userpass, string Password){
	AutoSeededX917RNG<AES> rng;

	SecByteBlock iv(AES::BLOCKSIZE);
	rng.GenerateBlock(iv,iv.size());

	// See NIST SP 800-132 for detailed recommendations on length, generation and
	// format of the salt. This test program will just generate a random one. That
	// might not be sufficient for every application.
	SecByteBlock pwsalt(AES::DEFAULT_KEYLENGTH);
	rng.GenerateBlock(pwsalt,pwsalt.size());

	SecByteBlock derivedkey(AES::DEFAULT_KEYLENGTH);
	
	cout << endl;
	cout << "Password is " << Password << endl;
	cout << "Deriving key from password:" << endl;

	PKCS5_PBKDF2_HMAC<SHA256> pbkdf;	
	pbkdf.DeriveKey(
		// buffer that holds the derived key
		derivedkey, derivedkey.size(),
		// purpose byte. unused by this PBKDF implementation.
		0x00,
		// password bytes. careful to be consistent with encoding...
		(byte *) Password.data(), Password.size(),
		// salt bytes
		pwsalt, pwsalt.size(),
		// iteration count. See SP 800-132 for details. You want this as large as you can tolerate.
		// make sure to use the same iteration count on both sides...
		iterations
		);
	
	cout << "Derived key: " << derivedkey << endl << endl;


	string cipherText;

	
	//3
	CBC_Mode<AES>::Encryption aesencryption(derivedkey,derivedkey.size(),iv);
	// encrypt message using key derived above, storing the hex encoded result into ciphertext
	StringSource encryptor(userpass, true, new StreamTransformationFilter(aesencryption, new HexEncoder( new StringSink(cipherText))) );

	// hex encode salt and IV for "transport"
	string hexsalt, hexiv;
	ArraySource saltEncoder(pwsalt,pwsalt.size(), true, new HexEncoder(new StringSink(hexsalt)));
	//


	ArraySource ivEncoder(iv,iv.size(), true, new HexEncoder(new StringSink(hexiv)));
	cout << "Salt: " << hexsalt << endl;
	cout << "IV: " << hexiv << endl;
	cout << "Ciphertext: " << cipherText << endl;


	ofstream out1 (SALTfile);
	
	if ( !out1.is_open() ) {
		cout << szErrWrite << endl;
	} else {
		out1 << hexsalt;
		out1.close();
	}

	ofstream out2 (IVfile);
	
	if ( !out2.is_open() ) {
		cout << szErrWrite << endl;
	} else {
		out2 << hexiv;
		out2.close();
	}

	ofstream out3 (RecordsFile);
	
	if ( !out3.is_open() ) {
		cout << szErrWrite << endl;
	} else {
		out3 << cipherText;
		out3.close();
	}

}



This function authenticates the username and password that is entered each time a user logs in:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
bool Authenticate(int iterations, string userpass, string Password){
	int size;
	char *buf;
	string hashText;
	
	//Read in the SALT used for the previous userpass
	ifstream in1(SALTfile, ios::in | ios::binary | ios::ate);
	size = in1.tellg();
	buf = new char[size + 1];
	buf[size] = 0;
	in1.seekg(0, ios::beg);
	in1.read(buf, size);
	in1.close();
	
	string hexsalt = string(buf);

	//Read in the IV used for the previous userpass
	ifstream in2(IVfile, ios::in | ios::binary | ios::ate);
	size = in2.tellg();
	buf = new char[size + 1];
	buf[size] = 0;
	in2.seekg(0, ios::beg);
	in2.read(buf, size);
	in2.close();

	string hexiv = string(buf);

	//Read in the hash used for the previous userpass, this will be compared the to newly entered userpass encrypted with the previous SALT and IV
	ifstream in3(RecordsFile, ios::in | ios::binary | ios::ate);
	size = in3.tellg();
	buf = new char[size + 1];
	buf[size] = 0;
	in3.seekg(0, ios::beg);
	in3.read(buf, size);
	in3.close();

	string cipherText = string(buf);

	cout << endl;
	cout << "Salt: " << hexsalt << endl;
	cout << "IV: " << hexiv << endl;
	cout << "Ciphertext: " << cipherText << endl;
	cout << endl;

	//Recover the SALT and IV from the hex values in the file,
	PKCS5_PBKDF2_HMAC<SHA256> pbkdf;


	SecByteBlock recoveredkey(AES::DEFAULT_KEYLENGTH);
	SecByteBlock recoveredsalt(AES::DEFAULT_KEYLENGTH);
	StringSource saltDecoder(hexsalt,true,new HexDecoder(new ArraySink(recoveredsalt, recoveredsalt.size() ) ) );
	pbkdf.DeriveKey(recoveredkey, recoveredkey.size(), 0x00, (byte *) Password.data(), Password.size(), recoveredsalt, recoveredsalt.size(), iterations);
	SecByteBlock recoverediv(AES::BLOCKSIZE);
	StringSource ivDecoder(hexiv,true,new HexDecoder(new ArraySink(recoverediv, recoverediv.size() ) ) );

	//Encrypt the userpass that has been entered, using the SALT and IV stored in the file
	SecByteBlock derivedkey(AES::DEFAULT_KEYLENGTH);

	//Buffer that holds the derived key, purpose byte (unused), password bytes, salt bytes, iteration count (large as you can tolerate)		
	pbkdf.DeriveKey(derivedkey, derivedkey.size(), 0x00, (byte *) Password.data(), Password.size(), recoveredsalt, recoveredsalt.size(), iterations);

	
	cout << "Derived key: " << derivedkey << endl;
	cout << "Password: " << (byte *) Password.data() << endl;
	cout << "Recovered salt: " << recoveredsalt << endl;
	cout << endl;
	
	//Encrypt the userpass using key derived above, storing the hex encoded result into hashtext
	CBC_Mode<AES>::Encryption aesencryption(derivedkey,derivedkey.size(),recoverediv);
	StringSource encryptor(userpass, true, new StreamTransformationFilter(aesencryption, new HexEncoder( new StringSink(hashText))) );


	cout << "Old hased userpass: " << cipherText << endl;
	cout << "New hased userpass: " << hashText << endl;

	if (hashText == cipherText){
		return true;
	} else {
		return false;
	}
}


But each time the old hash and the new has are compared, they are always different!
Should I be comparing the derived key instead?
Since you are pulling stored values I doubt they are the issue. Are you sure the iteration count is the same when calling both functions?

I popped your functions into CodeBlocks real quick:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194

#include <cryptopp/aes.h>
#include <cryptopp/hex.h>
#include <cryptopp/modes.h>
#include <cryptopp/osrng.h>
#include <cryptopp/pwdbased.h>
#include <fstream>
#include <iostream>
#include <string>

using namespace std;
using namespace CryptoPP;

const char *SALTfile = "salt.txt";
const char *IVfile = "iv.txt";
const char *RecordsFile = "records.txt";
const char *szErrWrite = "Error writing to output file";

void EncryptUserpass(int iterations, string userpass, string Password)
{
	AutoSeededX917RNG<AES> rng;

	SecByteBlock iv(AES::BLOCKSIZE);
	rng.GenerateBlock(iv,iv.size());

	// See NIST SP 800-132 for detailed recommendations on length, generation and
	// format of the salt. This test program will just generate a random one. That
	// might not be sufficient for every application.
	SecByteBlock pwsalt(AES::DEFAULT_KEYLENGTH);
	rng.GenerateBlock(pwsalt,pwsalt.size());

	SecByteBlock derivedkey(AES::DEFAULT_KEYLENGTH);

	cout << endl;
	cout << "Password is " << Password << endl;
	cout << "Deriving key from password:" << endl;

	PKCS5_PBKDF2_HMAC<SHA256> pbkdf;
	pbkdf.DeriveKey(
		// buffer that holds the derived key
		derivedkey, derivedkey.size(),
		// purpose byte. unused by this PBKDF implementation.
		0x00,
		// password bytes. careful to be consistent with encoding...
		(byte *) Password.data(), Password.size(),
		// salt bytes
		pwsalt, pwsalt.size(),
		// iteration count. See SP 800-132 for details. You want this as large as you can tolerate.
		// make sure to use the same iteration count on both sides...
		iterations
		);

	cout << "Derived key: " << derivedkey.BytePtr() << endl << endl;


	string cipherText;


	//3
	CBC_Mode<AES>::Encryption aesencryption(derivedkey,derivedkey.size(),iv);
	// encrypt message using key derived above, storing the hex encoded result into ciphertext
	StringSource encryptor(userpass, true, new StreamTransformationFilter(aesencryption, new HexEncoder( new StringSink(cipherText))) );

	// hex encode salt and IV for "transport"
	string hexsalt, hexiv;
	ArraySource saltEncoder(pwsalt,pwsalt.size(), true, new HexEncoder(new StringSink(hexsalt)));
	//


	ArraySource ivEncoder(iv,iv.size(), true, new HexEncoder(new StringSink(hexiv)));
	cout << "Salt: " << hexsalt << endl;
	cout << "IV: " << hexiv << endl;
	cout << "Ciphertext: " << cipherText << endl;


	ofstream out1 (SALTfile);

	if ( !out1.is_open() ) {
		cout << szErrWrite << endl;
	} else {
		out1 << hexsalt;
		out1.close();
	}

	ofstream out2 (IVfile);

	if ( !out2.is_open() ) {
		cout << szErrWrite << endl;
	} else {
		out2 << hexiv;
		out2.close();
	}

	ofstream out3 (RecordsFile);

	if ( !out3.is_open() ) {
		cout << szErrWrite << endl;
	} else {
		out3 << cipherText;
		out3.close();
	}

}

bool Authenticate(int iterations, string userpass, string Password){
	int size;
	char *buf;
	string hashText;

	//Read in the SALT used for the previous userpass
	ifstream in1(SALTfile, ios::in | ios::binary | ios::ate);
	size = in1.tellg();
	buf = new char[size + 1];
	buf[size] = 0;
	in1.seekg(0, ios::beg);
	in1.read(buf, size);
	in1.close();

	string hexsalt = string(buf);

	//Read in the IV used for the previous userpass
	ifstream in2(IVfile, ios::in | ios::binary | ios::ate);
	size = in2.tellg();
	buf = new char[size + 1];
	buf[size] = 0;
	in2.seekg(0, ios::beg);
	in2.read(buf, size);
	in2.close();

	string hexiv = string(buf);

	//Read in the hash used for the previous userpass, this will be compared the to newly entered userpass encrypted with the previous SALT and IV
	ifstream in3(RecordsFile, ios::in | ios::binary | ios::ate);
	size = in3.tellg();
	buf = new char[size + 1];
	buf[size] = 0;
	in3.seekg(0, ios::beg);
	in3.read(buf, size);
	in3.close();

	string cipherText = string(buf);

	cout << endl;
	cout << "Salt: " << hexsalt << endl;
	cout << "IV: " << hexiv << endl;
	cout << "Ciphertext: " << cipherText << endl;
	cout << endl;

	//Recover the SALT and IV from the hex values in the file,
	PKCS5_PBKDF2_HMAC<SHA256> pbkdf;


	SecByteBlock recoveredkey(AES::DEFAULT_KEYLENGTH);
	SecByteBlock recoveredsalt(AES::DEFAULT_KEYLENGTH);
	StringSource saltDecoder(hexsalt,true,new HexDecoder(new ArraySink(recoveredsalt, recoveredsalt.size() ) ) );
	pbkdf.DeriveKey(recoveredkey, recoveredkey.size(), 0x00, (byte *) Password.data(), Password.size(), recoveredsalt, recoveredsalt.size(), iterations);
	SecByteBlock recoverediv(AES::BLOCKSIZE);
	StringSource ivDecoder(hexiv,true,new HexDecoder(new ArraySink(recoverediv, recoverediv.size() ) ) );

	//Encrypt the userpass that has been entered, using the SALT and IV stored in the file
	SecByteBlock derivedkey(AES::DEFAULT_KEYLENGTH);

	//Buffer that holds the derived key, purpose byte (unused), password bytes, salt bytes, iteration count (large as you can tolerate)
	pbkdf.DeriveKey(derivedkey, derivedkey.size(), 0x00, (byte *) Password.data(), Password.size(), recoveredsalt, recoveredsalt.size(), iterations);


	cout << "Derived key: " << derivedkey.BytePtr() << endl;
	cout << "Password: " << (byte *) Password.data() << endl;
	cout << "Recovered salt: " << recoveredsalt.BytePtr() << endl;
	cout << endl;

	//Encrypt the userpass using key derived above, storing the hex encoded result into hashtext
	CBC_Mode<AES>::Encryption aesencryption(derivedkey,derivedkey.size(),recoverediv);
	StringSource encryptor(userpass, true, new StreamTransformationFilter(aesencryption, new HexEncoder( new StringSink(hashText))) );


	cout << "Old hased userpass: " << cipherText << endl;
	cout << "New hased userpass: " << hashText << endl;

	return (hashText == cipherText);
}

int main()
{
  std::string username, password, salt;
  std::cout << "Enter username: ";
  std::getline(std::cin,username);
  std::cout << std::endl << "Enter password: ";
  std::getline(std::cin,password);
  salt = username + password;
  EncryptUserpass(1,salt,password);
  Authenticate(1,salt,password);
  return 0;
}


Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Enter username: username

Enter password: password

Password is password
Deriving key from password:
╫s§ived key: ª  ΦUƒñÇß■π}▒

Salt: F64BEA5B72521D8D90BDF044EB0C1DB4
IV: A9ED481D9C5ADBA3527CEF88C3B21794
Ciphertext: 26689593BDA2D76662E9E70838AA668F9B4D0ED205BEFCACD8EDFCAD901FF31C

Salt: F64BEA5B72521D8D90BDF044EB0C1DB4
IV: A9ED481D9C5ADBA3527CEF88C3B21794
Ciphertext: 26689593BDA2D76662E9E70838AA668F9B4D0ED205BEFCACD8EDFCAD901FF31C

╫srived key: ª  ΦUƒñÇß■π}▒
Password: password
Recovered salt: ÷KΩ[rR↔ìÉ╜≡Dδ♀↔┤☺☻♥

Old hased userpass: 26689593BDA2D76662E9E70838AA668F9B4D0ED205BEFCACD8EDFCAD901FF31C
New hased userpass: 26689593BDA2D76662E9E70838AA668F9B4D0ED205BEFCACD8EDFCAD901FF31C

Process returned 0 (0x0)   execution time : 35.483 s
Press any key to continue.

Last edited on
The iteration count if defined globally, so should be the same for both. Is this function supposed to return the same value each time its run, if you have the same IV and salt?

If so, im very confused
I may have edited after you responded, if so look above. I was getting a match on the stored and newly generated hash using your 2 functions...
Thanks for all of your help, I have solved the problem using your feedback.

The majority of the problem is that when I called Authenticate(int iterations, string userpass, string Password)
I had the userpass and password variables the wrong way round!

What an awesome programmer I am ;)

Thanks again for all of your help.
Topic archived. No new replies allowed.