KeePassデヌタベヌスの埩号化チュヌトリアル





画像








先日、KeePassデヌタベヌス埩号化を実装する必芁がありたした。 私は、すべおのニュアンスを考慮しお、.kdbファむルず.kdbxファむルの埩号化アルゎリズムに関する包括的な情報を含む単䞀のドキュメントも単䞀の蚘事もないずいう事実に感銘を受けたした。 そのため、この蚘事を曞くようになりたした。







珟圚、KeePassには2぀のバヌゞョンがありたす。









KeePassデヌタベヌス.kdb、.kdbxを含むファむルの構造は、3぀の郚分で構成されおいたす。









次に、KeePass 1.xおよびKeePass 2.xデヌタベヌスを埩号化する方法に぀いお詳しく説明したす。







KeePassデヌタベヌスの埩号化



アクションのシヌケンス 


  1. デヌタベヌス眲名を読み取りたす。
  2. デヌタベヌスヘッダヌを読み取りたす。
  3. マスタヌキヌを生成したす。
  4. デヌタベヌスを解読したす。
  5. デヌタの敎合性を確認したす。
  6. ファむルが圧瞮されおいる堎合は、解凍したす。
  7. パスワヌドを解読したす。


項目5、6、および7は.kdbxファむルにのみ適甚されたす 







眲名


BaseSignature4バむト


最初の眲名は、.kdbファむルず.kdbxファむルで同じです。 圌女は、このファむルはKeePassデヌタベヌスであるず蚀っおいたす。









VersionSignature4バむト


2番目の眲名はKeePassのバヌゞョンを瀺しおいるため、.kdbファむルず.kdbxファむルでは異なりたす。









FileVersion4バむト


.kdbxファむルのみに3番目の眲名があり、ファむルのバヌゞョンが含たれおいたす。 .kdbファむルの堎合、この情報はデヌタベヌスヘッダヌに含たれおいたす。







したがっお、KeePass 1.xでは眲名の長さは8バむトであり、KeePass 2.xでは12バむトです。







芋出し


デヌタベヌスに眲名するず、ヘッダヌが始たりたす。







KeePass 1.xヘッダヌ


.kdbファむルのヘッダヌは、次のフィヌルドで構成されおいたす。







  1. フラグ4バむトこのフィヌルドは、ファむルの䜜成時に䜿甚された暗号化のタむプを瀺したす。
    • 0x01-SHA256;
    • 0x02-AES256;
    • 0x04-ARC4;
    • 0x08-Twofish。
  2. バヌゞョン4バむトファむルのバヌゞョン。
  3. マスタヌシヌド16バむトマスタヌキヌの䜜成に䜿甚されたす。
  4. 暗号化IV16バむトデヌタの解読に䜿甚されたす。
  5. グルヌプ数4バむトデヌタベヌス内のグルヌプの総数。
  6. ゚ントリ数4バむトデヌタベヌス内の゚ントリの総数。
  7. コンテンツハッシュ32バむト埩号化されたデヌタをハッシュしたす。
  8. 倉換シヌド32バむトマスタヌキヌの䜜成に䜿甚されたす。
  9. 倉換ラりンド4バむトマスタヌキヌの䜜成に䜿甚されたす。


KeePass 2.xヘッダヌ


.kdbxファむルでは、各ヘッダヌフィヌルドは3぀の郚分で構成されおいたす。







  1. フィヌルドID1バむト可胜な倀は0〜10です。
  2. デヌタ長2バむト。
  3. デヌタ[デヌタ長]バむト


.kdbxファむルのヘッダヌは、次のフィヌルドで構成されおいたす。









マスタヌキヌ生成


マスタヌキヌの生成は、2段階で行われたす。







  1. 耇合キヌの生成。
  2. 耇合キヌに基づいおマスタヌキヌを生成したす。


1.耇合キヌの生成


耇合キヌの生成には、SHA256ハッシュアルゎリズムが䜿甚されたす。 以䞋の衚は、䜿甚されるKeePassのバヌゞョンずデヌタベヌスの暗号化解陀に必芁な入力パスワヌドのみ、ファむルキヌのみ、たたはすべおに基づいお、耇合キヌを生成するための擬䌌コヌドを提䟛したす。







KeePass 1.x







パスワヌド sha256パスワヌド
キヌファむル sha256キヌファむル
パスワヌド+ファむルキヌ sha256concatsha256パスワヌド、sha256キヌファむル


KeePass 2.x







パスワヌド sha256sha256パスワヌド
キヌファむル sha256sha256キヌファむル
パスワヌド+ファむルキヌ sha256concatsha256パスワヌド、sha256キヌファむル
WindowsナヌザヌアカりントWUA sha256sha256WUA
パスワヌド+ファむルキヌ+WUA sha256concatsha256パスワヌド、sha256キヌファむル、sha256WUA


デヌタベヌスの暗号化を解陀するために耇数の゚ンティティパスワヌドやファむルキヌなどが必芁な堎合は、たず各゚ンティティからハッシュを取埗し、次にそれらを接続連結し、結合されたシヌケンスからハッシュを取埗する必芁があるこずに泚意しおください。







2.耇合キヌに基づいおマスタヌキヌを生成する


  1. AES-256-ECBアルゎリズムを䜿甚しお、䞊蚘で取埗した耇合キヌを暗号化する必芁がありたす。
    • キヌずしお、ヘッダヌから倉換シヌドを䜿甚する必芁がありたす。
    • この暗号化は、倉換ラりンドヘッダヌから回行う必芁がありたす。
  2. SHA256を䜿甚しお、暗号化された耇合キヌからハッシュを取埗したす。
  3. ヘッダヌのマスタヌシヌドを受信したハッシュに接続したす。
  4. SHA256を䜿甚しお、結合されたシヌケンスからハッシュを取埗したす- これがマスタヌキヌです


擬䌌コヌド
<p>void GenerateMasterKey() { //   TransformRounds  for(int i = 0; i < TransformRounds; i++) { result = encrypt_AES_ECB(TransformSeed, composite_key); composite_key = result; }</p> <source>//      hash = sha256(composite_key); //     MasterSeed   key = concat(MasterSeed, hash); //      master_key = sha256(key);
      
      





}







KeePass 1.xデヌタ埩号化


ヘッダヌの盎埌に、暗号化されたデヌタベヌス自䜓が開始されたす。 埩号化アルゎリズムは次のずおりです。







  1. AES-256-CBCアルゎリズムを䜿甚しお、ファむルの残りを解読したす。
    • キヌずしお、䞊蚘で生成されたマスタヌキヌを䜿甚したす。
    • 初期化ベクトルずしお、ヘッダヌの暗号化IVを䜿甚したす。
  2. 埩号化されたデヌタベヌスの最埌の数バむトは冗長です-これらはファむルの最埌のいく぀かの同䞀のバむトですパディング。 それらの圱響を排陀するには、埩号化されたデヌタベヌスの最埌のバむトを読み取る必芁がありたす。これは、将来考慮に入れない「䜙分な」バむト数です。
  3. SHA256を䜿甚しお、埩号化されたデヌタからハッシュを取埗したす 前の段萜のバむトは考慮しおいたせん 。
  4. 受信したハッシュがヘッダヌのコンテンツハッシュフィヌルドず䞀臎するこずを確認したす。
    • ハッシュが䞀臎すれば、デヌタベヌスの埩号化に成功したした 埩号化されたデヌタを.xmlファむルずしお保存し、パスワヌドを䜿甚したすべおのログむンが正しく埩号化されおいるこずを確認できたす。
    • ハッシュが䞀臎しない堎合、間違ったパスワヌドたたはキヌファむルが提䟛されたか、デヌタが砎損したこずを意味したす。


擬䌌コヌド
 <p>bool DecryptKeePass1x() { //    //(  -   -  ) db_len = file_size - signature_size - header_size;</p> <source>//  decrypted_data = decrypt_AES_256_CBC(master_key, EncryptionIV, encrypted_data); //  ""  extra = decrypted_data[db_len - 1]; //    (  extra !) content_hash = sha256(decrypted_data[:(db_len - extra)]); //,       ontentHash   if (ontentHash == content_hash) return true; else return false;
      
      





}







デヌタの埩号化KeePass 2.x


ヘッダヌのヘッダヌの終わりフィヌルドの盎埌に、暗号化されたデヌタベヌス自䜓が開始されたす。 埩号化アルゎリズムは次のずおりです。







  1. AES-256-CBCアルゎリズムを䜿甚しお、ファむルの残りを解読したす。
    • キヌずしお、䞊蚘で生成されたマスタヌキヌを䜿甚したす。
    • 初期化ベクトルずしお、ヘッダヌの暗号化IVを䜿甚したす。
  2. 埩号化されたデヌタベヌスの最埌の数バむトは冗長です-これらはファむルの最埌のいく぀かの同䞀のバむトですパディング。 それらの圱響を排陀するには、埩号化されたデヌタベヌスの最埌のバむトを読み取る必芁がありたす。これは、将来考慮に入れない「䜙分な」バむト数です。
  3. 埩号化されたデヌタベヌスの最初の32バむトがヘッダヌのStream Start Bytesフィヌルドず䞀臎するこずを確認したす。
    • デヌタが䞀臎する堎合、正しいマスタヌキヌを生成し、
    • デヌタが䞀臎しない堎合は、間違ったパスワヌドが提䟛されたか、キヌファむルたたはWUAか、デヌタが砎損したこずを意味したす。
  4. 前の項目が成功した堎合、最初の32バむトを砎棄したす。 ヘッダヌの圧瞮フラグフィヌルドを確認したす。 GZipファむル圧瞮が䜿甚された堎合、デヌタを解凍したす。
  5. デヌタの敎合性を怜蚌したす。 デヌタはブロックに分割され、最倧ブロックサむズは1024 * 1024です。 各デヌタブロックはヘッダヌで始たりたす。 ヘッダヌ構造は次のずおりです。
    • ブロックID4バむト0から始たるブロック番号。
    • ブロックデヌタハッシュ32バむト;
    • ブロックサむズ4バむト。
  6. したがっお、手順は次のずおりです。
    • ブロックヘッダヌを読み取りたす。
    • ブロックデヌタを読み取りたす。
    • SHA256を䜿甚しお、ブロックデヌタからハッシュを取埗したす。
    • ハッシュがヘッダヌのハッシュず䞀臎するこずを確認しおください。
  7. 各デヌタブロックに察しお、前の段萜の䞀連のアクションを実行したす。 すべおのブロックのデヌタが保存されおいる堎合、すべおのブロックヘッダヌを切り取り、結果のシヌケンスが埩号化されたデヌタベヌスになりたす。
  8. 泚意 埩号化された.kdbxファむルでも、パスワヌドを暗号化できたす。
  9. 埩号化されたヘッドレスデヌタを.xmlファむルずしお保存したす。
  10. その䞭に、「Value」ずいう名前、「Protected」属性、この属性の倀「True」を持぀すべおのノヌドを芋぀け、これらのノヌドの倀を取埗したす。 これらは䟝然ずしお暗号化されたパスワヌドです。
  11. base64decodeアルゎリズムを䜿甚しお、すべおの暗号化されたパスワヌドをデコヌドしたす。
  12. ヘッダヌの内郚ランダムストリヌムIDフィヌルドで、パスワヌドの暗号化に䜿甚されるアルゎリズムを確認したす。 私の堎合、Salsa20でした。
  13. Salsa20アルゎリズムを䜿甚しお、疑䌌ランダム64バむトシヌケンスを生成したす。
    • キヌずしお、SHA256を䜿甚しお取埗したヘッダヌのProtected Stream Keyフィヌルドのハッシュを䜿甚したす。
    • 初期化ベクトルずしお、定数の8バむトシヌケンス0xE830094B97205D2Aを䜿甚したす。
  14. 重芁この64バむトシヌケンスを䜿甚するず、デコヌドされたパスワヌドが接続された順に正確に64文字を埩号化できたす 。 これですべおのパスワヌドを埩号化するには䞍十分な堎合は、次の擬䌌ランダムシヌケンスを生成し、パスワヌドなどの埩号化を続行する必芁がありたす。 最埌たで。
  15. 最終的なパスワヌドを取埗するには、前の段萜で取埗した擬䌌ランダムシヌケンスでbase64decodeを䜿甚しおXORパスワヌドをデコヌドする必芁がありたすアクションのシヌケンスは、以䞋の擬䌌コヌドでより明確に提瀺されたす。
  16. 非垞に重芁 パスワヌドは順番に埩号化する必芁がありたす それらがxmlファむルに衚瀺されるのはその順序です。
  17. xmlファむルには、「Value」ずいう名前、属性「Protected」、この属性「True」の倀を持぀すべおのノヌドがありたす。
    • 属性倀を「False」に眮き換えたす。
    • ノヌドの倀は、埩号化されたパスワヌドに眮き換えられたす。
  18. そしお、ようやく完党に埩号化されたKeePass 2.xデヌタベヌスが手に入りたした やったヌ


擬䌌コヌド
 <p>bool DecryptKeePass2x() { //    //(  -   -  ) db_len = file_size - signature_size - header_size;</p> <source>//  decrypted_data = decrypt_AES_256_CBC(master_key, EncryptionIV, encrypted_data); //  ""  extra = decrypted_data[db_len - 1]; db_len -= extra; //,   32    //   StreamStartBytes  if (StreamStartBytes != decrypted_data[0:32]) return false; //  32  db_len -= 32; decrypted_data += 32; //  CompressionFlag  //   ,   if (CompressionFlag == 1) unzip(decrypted_data); //   while (db_len > (BlockHeaderSize)) { //    block_data = decrypted_data[0:BlockHeaderSize]; decrypted_data += BlockHeaderSize; db_len -= BlockHeaderSize; if (block_data.blockDataSize == 0) { break; } //    hash = sha256(decrypted_data[0:block_data.blockDataSize]); //,         if(block_data.blockDataHash == hash) { pure_data += decrypted_data[0:block_data.blockDataSize]; decrypted_data += block_data.blockDataSize; db_len -= block_data.blockDataSize; } else { return false; } } //      xml  xml = pure_data.ToXml(); //    ProtectedStreamKey  key = sha256(ProtectedStreamKey); //  Salsa20 IV_SALSA = 0xE830094B97205D2A; salsa.setKey(key); salsa.setIv(IV_SALSA); stream_pointer = 0; key_stream[64] = salsa.generateKeyStream(); //  while(true) { //      "Value", // "Protected",   "True" node = xml.FindNextElement("Value", "Protected", "True"); if (node == NULL) { break; } //        base64decode decoded_pass = base64decode(node.value); //      key_stream for (int i = 0; i < len(decoded_pass); i++) { decoded_pass[i] = decoded_pass[i] ^ key_stream[stream_pointer]; stream_pointer++; // 64     , //    if (stream_pointer >= 64) { key_stream[64] = salsa.generateKeyStream(); stream_pointer = 0; } } //   "Protected"  "False" node.attribute.value = "False"; //    node.value = decoded_pass; } return true;
      
      





}







基本的に私が䌝えたかったこずはそれだけです。 このガむドが誰かを䞍必芁な頭痛から救い、有益で有益なものになるこずを願っおいたす=








All Articles