1. 程式人生 > >基於cryptopp -- A Password Manager密碼管理系統

基於cryptopp -- A Password Manager密碼管理系統

系統設計安全目標

  • 私密性(confidentiality):訊息內容不讓其他人看到訊息:只有該看的人才能看
  • 完整性(integrity):訊息不被其他人篡改或者篡改之後可以被發現:看到的訊息是正確的
  • 可用性(availability):加密後,解密仍然能夠讀到資訊:想看隨時看

基本功能

登入介面

提示使用者輸入使用者名稱和master password

  • 檢查文件是否存在

    • 不存在建立一個新的文件,並以輸入的master password作為這個使用者的master password
    • 存在則檢查文件中的master password(需要先進行解密)和使用者輸入的是否一致,

      • 如果不一致則報錯,保留在登入介面
      • 如果一致則進行完整性檢查:將每一行的網站域名抽出來通過HMAC做雜湊,將雜湊以後的值和儲存的對應雜湊值最對比,如果有某一項不一致則報錯,保留在登陸介面;如果一致就進入使用者功能介面

使用者功能介面

功能1:實現對於網站域名 + 雜湊值 + 加密以後的密碼的增加

  • 提示使用者輸入網站域名和密碼
  • 檢查文件中是否有相同的域名,如果有則報錯;沒有則將網站域名 + 通過HMAC算出的雜湊值 + 加密以後的密碼增加到檔案尾部,並輸出成功資訊

功能2:實現對於網站域名 + 雜湊值 + 加密以後的密碼的刪除

  • 提示使用者輸入網站域名
  • 檢查文件中是否有相同的域名,如果沒有則報錯;有則將對應的記錄從該文件中刪除

功能3:實現對於輸入網站域名後對於密碼的查詢

  • 提示使用者輸入網站域名
  • 檢查文件中是否有相同的域名,如果沒有則報錯;有則將對應加密後的密碼解密後輸出出來

功能4:實現對於輸入網站域名後對於密碼的修改

  • 提示使用者輸入網站域名和密碼
  • 檢查文件中是否有相同的域名,如果沒有則報錯;有則將輸入的密碼進行加密後替換掉文件中的對應項

功能5:返回登入介面

完整程式碼

#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <fstream> 
#include <sstream> //AES #include "cryptopp563/filters.h" // StringSink, StringSource, StreamTransformationFilter #include "cryptopp563/aes.h" // AES #include "cryptopp563/modes.h" // CBC_Mode_* //PBKDF2 #include "cryptopp563/base64.h" // Base64Decoder #include "cryptopp563/sha.h" // SHA1 #include "cryptopp563/pwdbased.h" // PKCS5_PBKDF2_HMAC #include "cryptopp563/hex.h" // HexEncoder //HMAC #include "cryptopp563/hmac.h" // HMAC<T> using namespace std; using namespace CryptoPP; #define DEFAULT_STRING_SIZE (32) //十進位制與十六進位制之間相互轉換 string decToHex(byte num);//十進位制轉十六進位制 byte hexToDec(string num);//十六進位制轉十進位制 //CBC加密 string CBC_encrypt(string str); string CBC_encrypt(byte* key, string source); //CBC解密 string CBC_decrypt(string source); string CBC_decrypt(byte* key, string source); bool masterKey(string username, string password); //比對輸入密碼與txt密碼是否一致 void integrity(string username); //檔案內容是否具備完整性 //與key相關的雜湊函式對domain進行加密 string digest(string key, string domain); string deriveKey(string domain, string masterPassword); void addNewKVS(string username, string password); //增加KVS void deleteKVS(string username, string password); //刪除KVS void searchPassword(string username, string password); //查詢對應域名下的密碼 void changePassword(string username, string password); //修改對應域名下的密碼 int main (int argc, char const *argv[]){ string username; //使用者名稱 string masterPassword; //密碼 string filename; //使用者名稱補充檔名 string masterPasswordInTxt; /* 登入介面 */ while(1){ //輸入使用者名稱和密碼 cout << "\nPlease input the username and password." << endl; cin >> username >> masterPassword; //依據使用者名稱讀取檔案 filename = username + ".txt"; char *masterPasswordChar = (char*)masterPassword.data(); char *cname = (char*)filename.data(); ifstream in(cname); if(in == NULL){ //使用者不存在 新建以username命名的txt檔案,儲存masterPassword cout << "Hello, new friend." <<endl; in.close(); string passwordAfterEncrypt = CBC_encrypt(masterPasswordChar); ofstream out(cname); out << passwordAfterEncrypt <<endl; out.close(); break;//新建檔案完畢 跳出迴圈 } else{ //使用者已存在,對密碼進行驗證 getline(in, masterPasswordInTxt); //cout << masterPasswordInTxt + " !"<< endl; in.close(); if(masterKey(username, masterPassword)) { //密碼正確 cout << "Hello, " << username << "."<< endl; integrity(username); break; } else { //密碼錯誤 cout << "The password is wrong!" << endl; } } } /* 功能選擇介面 */ int kind; // 功能型別 cout << "--------------------------------------------------------------" <<endl; cout << "\nWelcome to Password Manager!\n" <<endl; while(1){ cout << "--------------------------------------------------------------" <<endl; cout << "1.Add a domain / password;" <<endl; cout << "--------------------------------------------------------------" <<endl; cout << "2.Remove a domain / password;" <<endl; cout << "--------------------------------------------------------------" <<endl; cout << "3.Check the corresponding password according to the domain;" <<endl; cout << "--------------------------------------------------------------" <<endl; cout << "4.Modify the corresponding password according to the domain;" <<endl; cout << "--------------------------------------------------------------" <<endl; cout << "5.Exit." <<endl; cout << "--------------------------------------------------------------" <<endl; cout << "Please input the number between 1 and 5." << endl; cin >> kind; while(kind < 1 || kind > 5){ cout << "Please input the right number."; cin >> kind; } if(kind == 1){ addNewKVS(username, masterPassword); } else if(kind == 2){ deleteKVS(username, masterPassword); } else if(kind == 3){ searchPassword(username, masterPassword); } else if(kind == 4){ changePassword(username, masterPassword); } else{ break; } } system("pause"); return 0; } string CBC_encrypt(string str) { // 初始化金鑰和初始向量 byte key[CryptoPP::AES::DEFAULT_KEYLENGTH]; byte iv[CryptoPP::AES::BLOCKSIZE]; memset(key, 0x00, CryptoPP::AES::DEFAULT_KEYLENGTH); memset(iv, 0x00, CryptoPP::AES::BLOCKSIZE); //進行CBC加密 string ciphertext; CryptoPP::AES::Encryption aesEncryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH); CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv); StreamTransformationFilter stfEncryptor(cbcEncryption, new StringSink(ciphertext)); stfEncryptor.Put(reinterpret_cast<const unsigned char*>(str.c_str()), str.length() + 1); stfEncryptor.MessageEnd(); // 獲得加密結果 string result; for(int i = 0; i < ciphertext.size(); i++) result.append(decToHex(0xFF & static_cast<byte>(ciphertext[i]))); return result; } void integrity(string username) { string masterPassword, domain, hash, password; //開啟檔案 string filename; filename = username + ".txt"; char *cname = (char*)filename.data(); ifstream in(cname); in >> masterPassword; //若密碼長度不為default_string_size的倍數則密碼不完整 if(masterPassword.size() % DEFAULT_STRING_SIZE != 0) { cout << "Fatal error: File is modified improperly" << endl; exit(0); } //迴圈讀取所有記錄 while(in >> domain >> hash >> password) { if(password.size() % DEFAULT_STRING_SIZE != 0) { //若密碼長度不為default_string_size的倍數則密碼不完整 cout << "Fatal error: File is modified improperly" << endl; exit(0); } else if(strcmp(hash.c_str(), digest(deriveKey(domain, CBC_decrypt(masterPassword)), domain).c_str()) != 0) { //若域名雜湊後與儲存的對應值不一樣則檔案不完整 cout << "Fatal error: File is modified improperly" << endl; exit(0); } } in.close(); } string decToHex(byte num) { char result[3] = {'0', '0'}; int i = 2; while(num > 0 && i-- > 0) { result[i] += num % 16; if(result[i] > '9') result[i] += ('A' - 10 - '0'); num /= 16; } return result; } byte hexToDec(string num) { byte result = 0; result += 16 * (num[0] < 'A' ? num[0] - '0' : num[0] - 'A' + 10); result += num[1] < 'A' ? num[1] - '0' : num[1] - 'A' + 10; return result; } bool masterKey(string username, string password) { //開啟檔案 string filename; filename = username + ".txt"; char *cname = (char*)filename.data(); ifstream in(cname); //讀取密碼 string passwordInTxt; in >> passwordInTxt; in.close(); //對讀取到的密碼進行解密 string decryptMasterKey = CBC_decrypt(passwordInTxt); //比對兩個密碼是否一致 if(strcmp(decryptMasterKey.c_str(), password.c_str()) == 0) return true; else return false; } string CBC_encrypt(byte* key, string source) { // IV setup byte iv[AES::BLOCKSIZE]; memset(iv, 0x00, AES::BLOCKSIZE); // Create Cipher Text string ciphertext; AES::Encryption aesEncryption(key, AES::DEFAULT_KEYLENGTH); CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv); StreamTransformationFilter stfEncryptor(cbcEncryption, new StringSink(ciphertext)); stfEncryptor.Put(reinterpret_cast<const unsigned char*>(source.c_str()), source.length() + 1); stfEncryptor.MessageEnd(); // get result string result; for(int i = 0; i < ciphertext.size(); i++) result.append(decToHex(0xFF & static_cast<byte>(ciphertext[i]))); return result; } string CBC_decrypt(string source) { // check modified if(source.size() % DEFAULT_STRING_SIZE != 0) { cout << "Fatal error: File is modified improperly" << endl; exit(0); } // get cipher text string cipher; for(int i = 0; i < source.length(); i += 2) cipher.append(1, hexToDec(source.substr(i, 2))); // Key and IV setup byte key[AES::DEFAULT_KEYLENGTH], iv[AES::BLOCKSIZE]; memset(key, 0x00, AES::DEFAULT_KEYLENGTH); memset(iv, 0x00, AES::BLOCKSIZE); // decrypt string decryptedtext; AES::Decryption aesDecryption(key, AES::DEFAULT_KEYLENGTH); CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv); StreamTransformationFilter stfDecryptor(cbcDecryption, new StringSink(decryptedtext)); stfDecryptor.Put(reinterpret_cast<const unsigned char*>(cipher.c_str()), cipher.size()); stfDecryptor.MessageEnd(); return decryptedtext; } string CBC_decrypt(byte* key, string source) { // check modified if(source.size() % DEFAULT_STRING_SIZE != 0) { cout << "Fatal error: File is modified improperly" << endl; exit(0); } // get cipher text string cipher; for(int i = 0; i < source.length(); i += 2) cipher.append(1, hexToDec(source.substr(i, 2))); // Key and IV setup byte iv[AES::BLOCKSIZE]; memset(iv, 0x00, AES::BLOCKSIZE); // decrypt string decryptedtext; AES::Decryption aesDecryption(key, AES::DEFAULT_KEYLENGTH); CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv); StreamTransformationFilter stfDecryptor(cbcDecryption, new StringSink(decryptedtext)); stfDecryptor.Put(reinterpret_cast<const unsigned char*>(cipher.c_str()), cipher.size()); stfDecryptor.MessageEnd(); return decryptedtext; } string digest(string key, string domain) { byte result[AES::DEFAULT_KEYLENGTH]; HMAC<SHA >((byte *)key.c_str(), key.size()).CalculateDigest(result, (byte *)domain.c_str(), domain.size()); string value; for(int i = 0; i < AES::DEFAULT_KEYLENGTH; i++) value.append(decToHex(result[i])); return value; } string deriveKey(string domain, string masterPassword) { byte derived[AES::DEFAULT_KEYLENGTH]; PKCS5_PBKDF2_HMAC<CryptoPP::SHA1> pbkdf2; pbkdf2.DeriveKey(derived, sizeof(derived), 0, (byte *)masterPassword.c_str(), masterPassword.size(), (byte *)domain.c_str(), domain.size(), 1); string result; HexEncoder encoder(new StringSink(result)); encoder.Put(derived, sizeof(derived)); encoder.MessageEnd(); return result; } void addNewKVS(string username, string password) { //輸出提示資訊,提示輸入需要新增的域名和密碼 string domainInput, passwordInput; cout << "Please input the domain you want to add: "; cin >> domainInput; cout << "Please input the new password of the domain: "; cin >> passwordInput; string key = deriveKey(domainInput, password); ofstream file; file.open((username + ".txt").c_str(), ios::out|ios::app); //對應輸出域名,雜湊後的域名,加密後的密碼到檔案中 file << domainInput << " " << digest(key, domainInput) << " " << CBC_encrypt((byte *)key.c_str(), passwordInput) << endl; file.close(); cout << "Success!" << endl; } void deleteKVS(string username, string password) { //提示輸入需要刪除的域名 string domainInput; cout << "Please input the domain you want to delete: "; cin >> domainInput; bool find = false; string masterPassword; vector<string> domainVector, hashVector, passwordVector; //臨時儲存記錄 string domain, hash, pw; //開啟檔案,逐條記錄比對域名是否與輸入的域名一致 fstream file; file.open((username + ".txt").c_str(), ios::in); file >> masterPassword; while(file >> domain >> hash >> pw) { if(strcmp(domain.c_str(), domainInput.c_str()) == 0) //域名是否與輸入的域名一致,不放入儲存向量中 find = true; else { domainVector.push_back(domain); hashVector.push_back(hash); passwordVector.push_back(pw); } } file.close(); file.open((username + ".txt").c_str(), ios::out|ios::trunc); file << masterPassword << endl; //將記錄從臨時儲存的向量中移回檔案中 for(int i = 0; i < domainVector.size(); i++) file << domainVector[i] << " " << hashVector[i] << " " << passwordVector[i] << endl; file.close(); if(find) cout << "Success!" << endl; else cout << "Can not find the record of the domain" << endl; } void searchPassword(string username, string password) { //提示輸入需要查詢的域名 string domainInput; cout << "Please input the domain you want to search: "; cin >> domainInput; bool find = false; string masterPassword; string domain, hash, pw; ifstream file; file.open((username + ".txt").c_str()); file >> masterPassword; //逐條記錄比對,若域名一致則找到,將密碼解密出來並輸出 while(file >> domain >> hash >> pw) { if(strcmp(domain.c_str(), domainInput.c_str()) == 0) { find = true; string key = deriveKey(domainInput, password); cout << "The password of domain is: " << CBC_decrypt((byte *)key.c_str(), pw) << endl; break; } } if(!find) cout << "Can not find the record of the domain" << endl; } void changePassword(string username, string password) { //提示輸入需要修改的域名和密碼 string domainInput, passwordInput; cout << "Please input the domain to be changed: "; cin >> domainInput; cout << "Please input your new password: "; cin >> passwordInput; bool find = false; string masterPassword; //轉存向量 vector<string> domainVector, hashVector, passwordVector; string domain, hash, pw; //開啟對應檔案 fstream file; file.open((username + ".txt").c_str(), ios::in); file >> masterPassword; //逐條進行比對,若發現域名一致的記錄則將新的密碼加密後存入向量 while(file >> domain >> hash >> pw) { if(strcmp(domain.c_str(), domainInput.c_str()) == 0) { domainVector.push_back(domain); hashVector.push_back(hash); string key = deriveKey(domainInput, password); passwordVector.push_back(CBC_encrypt((byte *)key.c_str(), passwordInput)); find = true; } else { domainVector.push_back(domain); hashVector.push_back(hash); passwordVector.push_back(pw); } } file.close(); file.open((username + ".txt").c_str(), ios::out|ios::trunc); file << masterPassword << endl; //將在向量內臨時儲存的內容移回檔案 for(int i = 0; i < domainVector.size(); i++) file << domainVector[i] << " " << hashVector[i] << " " << passwordVector[i] << endl; file.close(); if(find) cout << "Success!" << endl; else cout << "Can not find the record of the domain" << endl; }