NTLM 認証プロトコルとセキュリティサポート プロバイダ より
文中のコードはRuby
- ユーザパスワード: SecREt01
- Type 2 チャレンジ: 0x0123456789abcdef
Unicodeベースの大文字小文字が混在したパスワードは16進数で 「0x53006500630052004500740030003100」となる。
"SecREt01".encode("UTF-16LE").unpack("H*").first.gsub(/..(?=.)/, '\0 ')
=> "53 00 65 00 63 00 52 00 45 00 74 00 30 00 31 00"
この値の MD4 ハッシュを計算するこ とで、「0xcd06ca7c7e10c99b1d33b7485a2ed808」という値が生成される。これが NTLM ハ ッシュである。
require 'openssl'
OpenSSL::Digest::MD4.digest("SecREt01".encode("UTF-16LE")).unpack("H*").first.gsub(/..(?=.)/, '\0 ')
=> "cd 06 ca 7c 7e 10 c9 9b 1d 33 b7 48 5a 2e d8 08"
これを 21 バイトになるように NULL パディングして「0xcd06ca7c7e10c99b1d33b7485a2ed8080000000000」という値が生成される。
digest = OpenSSL::Digest::MD4.digest("SecREt01".encode("UTF-16LE"))
v = digest + "\x00" * (21 - digest.size)
v.unpack("H*").first.gsub(/..(?=.)/, '\0 ')
=> "cd 06 ca 7c 7e 10 c9 9b 1d 33 b7 48 5a 2e d8 08 00 00 00 00 00"
この値を 3 つの 7 バイトの値に分割し、「0xcd06ca7c7e10c9」、「0x9b1d33b7485a2e」、 「0xd8080000000000」という値が生成される。
digest = OpenSSL::Digest::MD4.digest("SecREt01".encode("UTF-16LE"))
v = digest + "\x00" * (21 - digest.size)
v.scan(/.{1,7}/).map{ |d| d.unpack("H*").first.gsub(/..(?=.)/, '\0 ') }
=> ["cd 06 ca 7c 7e 10 c9", "9b 1d 33 b7 48 5a 2e", "d8 08 00 00 00 00 00"]
この 3 つの値は 3 つの DES 鍵を生成するのに用いられる。最初の値 11001101 00000110 11001010 01111100 01111110 00010000 11001001からは パリティ処理を行なった DES 鍵として 11001101 10000011 10110011 01001111 11000111 11110001 01000011 10010010 (16 進数で「0xcd83b34fc7f14392」)が生成される。
No. | 元の値 | DESパリティ処理後 |
---|---|---|
No.1 | cd 06 ca 7c 7e 10 c9 | cd 83 b3 4f c7 f1 43 92 |
No.2 | 9b 1d 33 b7 48 5a 2e | 9b 8f 4c 76 75 43 68 5d |
No.2 | d8 08 00 00 00 00 00 | d9 04 01 01 01 01 01 01 |
これら 3 つの鍵が、Type 2 メッセージのチャレンジ(「0x0123456789abcdef」)の DES 暗号 化に用いられる。これにより、以下の値、 「0x25a98c1c31e81847」 (1 つ目の鍵を使用)、 「0x466b29b2df4680f3」 (2 つ目を使用)、 「0x9958fb8c213a9cc6」 (3 つ目の鍵を使用) が 生成される。
これら 3 つの暗号文を結合して、24 バイトのバイト列としたものが、以下の NTLM レス ポンスである。 0x25a98c1c31e81847466b29b2df4680f39958fb8c213a9cc6