2013-01-08 35 views
5

我可以使用下面做出HMAC:我如何解密HMAC?

var encrypt = crypto.createHmac("SHA256", secret).update(string).digest('base64'); 

我試圖解密編碼HMAC與祕密:

var decrypt = crypto.createDecipher("SHA256", secret).update(string).final("ascii"); 

以下是不成功的。我如何使用密鑰解密HMAC?

我得到以下錯誤:

node-crypto : Unknown cipher SHA256 

crypto.js:155 
    return (new Decipher).init(cipher, password); 
         ^
Error: DecipherInit error 

回答

28

HMAC是MAC /加密散列,而不是一個密碼。它不是爲了解密而設計的。如果要加密某些內容,請使用AES等密碼,最好使用AES-GCM等驗證模式。

「解密」的唯一方法是猜測整個輸入,然後比較輸出。

+3

即使是這樣,你不能解密,因爲多個鍵可以散列到相同的價值。哈希是一個單向函數。欲瞭解更多信息:http://en.wikipedia.org/wiki/Hash_function –

+3

@JustinEthier在許多情況下,輸入空間足夠小,猜測工作。在實踐中,你永遠不會用正確的散列猜測錯誤的輸入。您將猜測正確的輸入,或者根本找不到匹配的輸入。 |但是由於HMAC是鍵控的,只有那些知道密鑰的人才能使用這種猜測攻擊,所以這通常不是問題(使用無密鑰的哈希猜測輸入通常是一個問題)。 – CodesInChaos

+0

嗯,真的,自從那條評論以來,我一直向前移動,摧毀它。 –

1

正如CodesInChaos所述,帶有SHA256的HMAC只能用於散列一個值,這個值只能是單向的。如果您希望能夠加密/解密,則必須使用密碼,例如aesdes

例如在如何加密/解密:

const crypto = require("crypto"); 

// key and iv 
var key = crypto.createHash("sha256").update("OMGCAT!", "ascii").digest(); 
var iv = "123456789"; 

// this is the string we want to encrypt/decrypt 
var secret = "ermagherd"; 

console.log("Initial: %s", secret); 

// create a aes256 cipher based on our password 
var cipher = crypto.createCipheriv("aes-256-cbc", key, iv); 
// update the cipher with our secret string 
cipher.update(secret, "ascii"); 
// save the encryption as base64-encoded 
var encrypted = cipher.final("base64"); 

console.log("Encrypted: %s", encrypted); 

// create a aes267 decipher based on our password 
var decipher = crypto.createDecipheriv("aes-256-cbc", key, iv); 
// update the decipher with our encrypted string 
decipher.update(encrypted, "base64"); 

console.log("Decrypted: %s", decipher.final("ascii")); 

注:你必須保存加密/解密成自己的變量,並.update後也一定不要鏈.final

如果你想知道什麼密碼都可以在系統上,使用以下命令:

openssl list-cipher-algorithm 
+0

使用哪種鏈接模式?缺乏鹽/ iv也很奇怪。 – CodesInChaos

+0

@CodesInChaos'.update'不返回一個Cipher對象,所以你不能像他的例子那樣執行'.update(data).final()'。缺乏salt/iv並不奇怪?你必須爲此使用'.createCipheriv'和'.createDecipheriv'。 – mekwall

+2

我詢問了加密鏈接模式。你不能用AES加密數據,你需要一個鏈接模式和一個填充模式。例如ECB,CBC,GCM ......是常用鏈接模式。 ECB被破壞了,當使用CBC時,你通常需要自己添加認證,而你沒有。 |用密碼加密並且不加鹽是很奇怪的。除非API爲內部的每個呼叫創建不同的鹽,否則這是一個安全弱點。 |所以你的代碼很可能會因爲使用壞模式或缺乏salt/IV而變得不安全。 – CodesInChaos

8

再次重申哈希沒有被設計成被解密。但是,一旦你有一個散列,你可以檢查任何字符串是否與該散列相同,方法是通過具有相同祕密的相同加密。

var crypto = require('crypto') 

var secret = 'alpha' 
var string = 'bacon' 

var hash = crypto.createHmac('SHA256', secret).update(string).digest('base64'); 
// => 'IbNSH3Lc5ffMHo/wnQuiOD4C0mx5FqDmVMQaAMKFgaQ=' 

if (hash === crypto.createHmac('SHA256', secret).update(string).digest('base64')) { 
    console.log('match') // logs => 'match' 
} else { 
    console.log('no match') 
} 

似乎很明顯,但非常強大。

+1

這個答案值得更多的愛。其他答案分歧並談論加密,而這很好地解釋了應該如何使用HMAC本身。 – Zaccone

+1

小心使用不會泄漏多少個字符通過時間匹配的比較功能。 – CodesInChaos

0

代碼爲簡約查看清理和清除雜亂: 注:IIFE在節點REPL運行的「原樣」

!function(){ 

const crypto = require("crypto"); 

// key 
var key = crypto.createHash("sha256").digest(); 


// this is the string we want to encrypt/decrypt 
var secret = "ermagherd"; 

console.log("Initial: %s", secret); 

// create a aes256 cipher based on our password 
var cipher = crypto.createCipher("aes-256-cbc", key); 

// update the cipher with our secret string 
cipher.update(secret); 

// save the encryption 
var encrypted = cipher.final(); 

console.log("Encrypted: %s", encrypted); 

// create a aes267 decipher based on our password 
var decipher = crypto.createDecipher("aes-256-cbc", key); 

// update the decipher with our encrypted string 
decipher.update(encrypted); 

console.log("Decrypted: %s", decipher.final()); //default is utf8 encoding final("utf8") not needed for default 

}() 

/* REPL Output 

      Initial: ermagherd 
    Encrypted: T)��l��Ʀ��,�' 
    Decrypted: ermagherd 
    true 
*/