1. 程式人生 > >NodeJS學習筆記 (13)數據加密-crypto(OK)

NodeJS學習筆記 (13)數據加密-crypto(OK)

tor html uri aes256 nds audio ext HA known

寫在前面

本章節寫得差不多了,不過還需要再整理一下(TODO)。

hash例子

hash.digest([encoding]):計算摘要。encoding可以是hexlatin1或者base64。如果聲明了encoding,那麽返回字符串。否則,返回Buffer實例。註意,調用hash.digest()後,hash對象就作廢了,再次調用就會出錯。

hash.update(data[, input_encoding]):input_encoding可以是utf8ascii或者latin1。如果data是字符串,且沒有指定 input_encoding,則默認是utf8。註意,hash.update()方法可以調用多次。

var crypto = require(‘crypto‘);
var fs = require(‘fs‘);

var content = fs.readFileSync(‘./test.txt‘, {encoding: ‘utf8‘});
var hash = crypto.createHash(‘sha256‘);
var output;

hash.update(content);

output = hash.digest(‘hex‘); 

console.log(output);
// 輸出內容為:
// b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

也可以這樣:

var crypto = require(‘crypto‘);
var fs = require(‘fs‘);

var input = fs.createReadStream(‘./test.txt‘, {encoding: ‘utf8‘});
var hash = crypto.createHash(‘sha256‘);

hash.setEncoding(‘hex‘);

input.pipe(hash).pipe(process.stdout)

// 輸出內容為:
// b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

hash.digest()後,再次調用digest()或者update()

var crypto = require(‘crypto‘);
var fs = require(‘fs‘);

var content = fs.readFileSync(‘./test.txt‘, {encoding: ‘utf8‘});
var hash = crypto.createHash(‘sha256‘);
var output;

hash.update(content);
hash.digest(‘hex‘); 

// 報錯:Error: Digest already called
hash.update(content);

// 報錯:Error: Digest already called
hash.digest(‘hex‘);


也可以這樣:

var crypto = require(‘crypto‘);
var fs = require(‘fs‘);

var input = fs.createReadStream(‘./test.txt‘, {encoding: ‘utf8‘});
var hash = crypto.createHash(‘sha256‘);

hash.setEncoding(‘hex‘);

input.pipe(hash).pipe(process.stdout)

// 輸出內容為:
// b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

hash.digest()後,再次調用digest()或者update()

var crypto = require(‘crypto‘);
var fs = require(‘fs‘);

var content = fs.readFileSync(‘./test.txt‘, {encoding: ‘utf8‘});
var hash = crypto.createHash(‘sha256‘);
var output;

hash.update(content);
hash.digest(‘hex‘); 

// 報錯:Error: Digest already called
hash.update(content);

// 報錯:Error: Digest already called
hash.digest(‘hex‘);


hash.digest()後,再次調用digest()或者update()

var crypto = require(‘crypto‘);
var fs = require(‘fs‘);

var content = fs.readFileSync(‘./test.txt‘, {encoding: ‘utf8‘});
var hash = crypto.createHash(‘sha256‘);
var output;

hash.update(content);
hash.digest(‘hex‘); 

// 報錯:Error: Digest already called
hash.update(content);

// 報錯:Error: Digest already called
hash.digest(‘hex‘);


HMAC例子

HMAC的全稱是Hash-based Message Authentication Code,也即在hash的加鹽運算。

算法細節可以參考附錄鏈接,具體到使用的話,跟hash模塊差不多,選定hash算法,指定“鹽”即可。

例子1:

var crypto = require(‘crypto‘);
var fs = require(‘fs‘);

var secret = ‘secret‘;
var hmac = crypto.createHmac(‘sha256‘, secret);
var input = fs.readFileSync(‘./test.txt‘, {encoding: ‘utf8‘});

hmac.update(input);

console.log( hmac.digest(‘hex‘) );
// 輸出:
// 734cc62f32841568f45715aeb9f4d7891324e6d948e4c6c60c0621cdac48623a

例子2:

var crypto = require(‘crypto‘);
var fs = require(‘fs‘);

var secret = ‘secret‘;
var hmac = crypto.createHmac(‘sha256‘, secret);
var input = fs.createReadStream(‘./test.txt‘, {encoding: ‘utf8‘});

hmac.setEncoding(‘hex‘);

input.pipe(hmac).pipe(process.stdout)
// 輸出:
// 734cc62f32841568f45715aeb9f4d7891324e6d948e4c6c60c0621cdac48623a


加密/解密

加解密主要用到下面兩組方法:

加密:

  • crypto.createCipher(algorithm, password)
  • crypto.createCipheriv(algorithm, key, iv)

解密:

  • crypto.createDecipher(algorithm, password)
  • crypto.createDecipheriv(algorithm, key, iv)

crypto.createCipher(algorithm, password)

先來看下 crypto.createCipher(algorithm, password),兩個參數分別是加密算法、密碼

  • algorithm:加密算法,比如aes192,具體有哪些可選的算法,依賴於本地openssl的版本,可以通過openssl list-cipher-algorithms命令查看支持哪些算法。
  • password:用來生成密鑰(key)、初始化向量(IV)。

備註:這裏nodejs屏蔽了AES的使用/實現細節,關於key、IV,感興趣的同學可以自行谷歌下。

var crypto = require(‘crypto‘);
var secret = ‘secret‘;

var cipher = crypto.createCipher(‘aes192‘, secret);
var content = ‘hello‘;
var cryptedContent;

cipher.update(content);
cryptedContent = cipher.final(‘hex‘);
console.log(cryptedContent);
// 輸出:
// 71d30ec9bc926b5dbbd5150bf9d3e5fb

crypto.createDecipher(algorithm, password)

可以看作 crypto.createCipher(algorithm, password) 逆向操作,直接看例子

var crypto = require(‘crypto‘);
var secret = ‘secret‘;

var cipher = crypto.createCipher(‘aes192‘, secret);
var content = ‘hello‘;
var cryptedContent;

cipher.update(content);
cryptedContent = cipher.final(‘hex‘);
console.log(cryptedContent);
// 輸出:
// 71d30ec9bc926b5dbbd5150bf9d3e5fb

var decipher = crypto.createDecipher(‘aes192‘, secret);
var decryptedContent;

decipher.update(cryptedContent, ‘hex‘);
decryptedContent = decipher.final(‘utf8‘);
console.log(decryptedContent);
// 輸出:
// hello

crypto.createCipheriv(algorithm, key, iv)

相對於 crypto.createCipher() 來說,crypto.createCipheriv() 需要提供keyiv,而 crypto.createCipher() 是根據用戶提供的 password 算出來的。

key、iv 可以是Buffer,也可以是utf8編碼的字符串,這裏需要關註的是它們的長度:

  • key:根據選擇的算法有關,比如 aes128、aes192、aes256,長度分別是128、192、256位(16、24、32字節)
  • iv:都是128位(16字節)
var crypto = require(‘crypto‘);
var key = crypto.randomBytes(192/8);
var iv = crypto.randomBytes(128/8);
var algorithm = ‘aes192‘;

function encrypt(text){
    var cipher = crypto.createCipheriv(algorithm, key, iv);
    cipher.update(text);
    return cipher.final(‘hex‘);
}

function decrypt(encrypted){
    var decipher = crypto.createDecipheriv(algorithm, key, iv);
    decipher.update(encrypted, ‘hex‘);
    return decipher.final(‘utf8‘);
}

var content = ‘hello‘;
var crypted = encrypt(‘hello‘);
console.log( crypted );

var decrypted = decrypt( crypted );
console.log( decrypted );  // 輸出:utf8


數字簽名/簽名校驗

假設:

1、服務端原始信息為M,摘要算法為Hash,Hash(M)得出的摘要是H。 2、公鑰為Pub,私鑰為Piv,非對稱加密算法為Encrypt,非對稱解密算法為Decrypt。 3、Encrypt(H)得到的結果是S。 4、客戶端拿到的信息為M1,利用Hash(M1)得出的結果是H1。

數字簽名的產生、校驗步驟分別如下:

1、數字簽名的產生步驟:利用摘要算法Hash算出M的摘要,即Hash(M) == H,利用非對稱加密算法對摘要進行加密Encrypt( H, Piv ),得到數字簽名S。 2、數字簽名的校驗步驟:利用解密算法D對數字簽名進行解密,即Decrypt(S) == H,計算M1的摘要 Hash(M1) == H1,對比 H、H1,如果兩者相同,則通過校驗。

私鑰如何生成不是這裏的重點,這裏采用網上的服務來生成,點擊[這裏](在線生成非對稱加密公鑰私鑰對、在線生成公私鑰對、RSA Key pair create、生成RSA密鑰對)。

了解了數字簽名產生、校驗的原理後,相信下面的代碼很容易理解:

var crypto = require(‘crypto‘);
var fs = require(‘fs‘);
var privateKey = fs.readFileSync(‘./private-key.pem‘);  // 私鑰
var publicKey = fs.readFileSync(‘./public-key.pem‘);  // 公鑰
var algorithm = ‘RSA-SHA256‘;  // 加密算法 vs 摘要算法

// 數字簽名
function sign(text){
    var sign = crypto.createSign(algorithm);
    sign.update(text);
    return sign.sign(privateKey, ‘hex‘);    
}

// 校驗簽名
function verify(oriContent, signature){
    var verifier = crypto.createVerify(algorithm);
    verifier.update(oriContent);
    return verifier.verify(publicKey, signature, ‘hex‘);
}

// 對內容進行簽名
var content = ‘hello world‘;
var signature = sign(content);
console.log(signature);

// 校驗簽名,如果通過,返回true
var verified = verify(content, signature);
console.log(verified);


DiffieHellman

DiffieHellman:Diffie–Hellman key exchange,縮寫為D-H,是一種安全協議,讓通信雙方在預先沒有對方信息的情況下,通過不安全通信信道,創建一個密鑰。這個密鑰可以在後續的通信中,作為對稱加密的密鑰加密傳遞的信息。

代碼如下,原理待補充 TODO

const crypto = require(‘crypto‘);
const assert = require(‘assert‘);

// Generate Alice‘s keys...
const alice = crypto.createDiffieHellman(2048);
const alice_key = alice.generateKeys();

// Generate Bob‘s keys...
const bob = crypto.createDiffieHellman(alice.getPrime(), alice.getGenerator());
const bob_key = bob.generateKeys();

// Exchange and generate the secret...
const alice_secret = alice.computeSecret(bob_key);
const bob_secret = bob.computeSecret(alice_key);

// OK
assert.equal(alice_secret.toString(‘hex‘), bob_secret.toString(‘hex‘));


DiffieHellman

DiffieHellman:Diffie–Hellman key exchange,縮寫為D-H,是一種安全協議,讓通信雙方在預先沒有對方信息的情況下,通過不安全通信信道,創建一個密鑰。這個密鑰可以在後續的通信中,作為對稱加密的密鑰加密傳遞的信息。

代碼如下,原理待補充 TODO

const crypto = require(‘crypto‘);
const assert = require(‘assert‘);

// Generate Alice‘s keys...
const alice = crypto.createDiffieHellman(2048);
const alice_key = alice.generateKeys();

// Generate Bob‘s keys...
const bob = crypto.createDiffieHellman(alice.getPrime(), alice.getGenerator());
const bob_key = bob.generateKeys();

// Exchange and generate the secret...
const alice_secret = alice.computeSecret(bob_key);
const bob_secret = bob.computeSecret(alice_key);

// OK
assert.equal(alice_secret.toString(‘hex‘), bob_secret.toString(‘hex‘));

ECDH:Elliptic Curve Diffie-Hellman

代碼如下,原理待補充 TODO

const crypto = require(‘crypto‘);
const assert = require(‘assert‘);

// Generate Alice‘s keys...
const alice = crypto.createECDH(‘secp521r1‘);
const alice_key = alice.generateKeys();

// Generate Bob‘s keys...
const bob = crypto.createECDH(‘secp521r1‘);
const bob_key = bob.generateKeys();

// Exchange and generate the secret...
const alice_secret = alice.computeSecret(bob_key);
const bob_secret = bob.computeSecret(alice_key);

assert(alice_secret, bob_secret);
  // OK

證書

SPKAC:

SPKAC is an acronym that stands for Signed Public Key and Challenge, also known as Netscape SPKI.

SPKI:Simple public-key infrastructure

關鍵點

md5:固定長度(128bit)、不可逆(重要)、不同數據的散列值可能相同(重要)、高度離散型(原文細微的變化,會導致散列值差異巨大)

sha1:固定長度160bit,廣泛使用(如TLS,目前安全性受到密碼學家的質疑)

SHA-256/SHA-384/SHA-512:後面表示摘要的長度。

用途:數字簽名、文件完整性校驗

關系:sha1 基於 MD5,MD5 基於 MD4

md5(1991) -> SHA1

sha家族:由美國國家安全局(NSA)所設計,並由美國國家標準與技術研究院(NIST)發布;是美國的政府標準。

相關術語

SPKAC:Signed Public Key and Challenge

MD5:Message-Digest Algorithm 5,信息-摘要算法。

SHA:Secure Hash Algorithm,安全散列算法。

HMAC:Hash-based Message Authentication Code,密鑰相關的哈希運算消息認證碼。

SPKAC:

對稱加密:比如AES、DES

非對稱加密:比如RSA、DSA

AES:Advanced Encryption Standard(高級加密標準),密鑰長度可以是128、192和256位。

DES:Data Encryption Standard,數據加密標準,對稱密鑰加密算法(現在認為不安全)。https://en.wikipedia.org/wiki/Data_Encryption_Standard

DiffieHellman:Diffie–Hellman key exchange,縮寫為D-H,是一種安全協議,讓通信雙方在預先沒有對方信息的情況下,通過不安全通信信道,創建一個密鑰。這個密鑰可以在後續的通信中,作為對稱加密的密鑰加密傳遞的信息。(備註,使是用協議的發明者命名)

相關鏈接

字符編碼筆記:ASCII,Unicode和UTF-8 - 阮一峰的網絡日誌http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

Unicode與JavaScript詳解 http://www.ruanyifeng.com/blog/2014/12/unicode.html

Base64筆記 http://www.ruanyifeng.com/blog/2008/06/base64.html

MIME筆記 http://www.ruanyifeng.com/blog/2008/06/mime.html

SHA家族 https://zh.wikipedia.org/wiki/SHA%E5%AE%B6%E6%97%8F

加鹽密碼哈希:如何正確使用 http://blog.jobbole.com/61872/

HMAC-MD5算法原理及實現 http://www.jianshu.com/p/067f9eb6b252

Encrypting using AES-256, can I use 256 bits IV? http://security.stackexchange.com/questions/90848/encrypting-using-aes-256-can-i-use-256-bits-iv

分組對稱加密模式:ECB/CBC/CFB/OFB http://blog.csdn.net/aaaaatiger/article/details/2525561

在線生成非對稱加密公鑰私鑰對、在線生成公私鑰對、RSA Key pair create、生成RSA密鑰對http://web.chacuo.net/netrsakeypair

Diffie–Hellman key exchange https://zh.wikipedia.org/wiki/%E8%BF%AA%E8%8F%B2-%E8%B5%AB%E7%88%BE%E6%9B%BC%E5%AF%86%E9%91%B0%E4%BA%A4%E6%8F%9B

理解 Deffie-Hellman 密鑰交換算法 http://wsfdl.com/algorithm/2016/02/04/%E7%90%86%E8%A7%A3Diffie-Hellman%E5%AF%86%E9%92%A5%E4%BA%A4%E6%8D%A2%E7%AE%97%E6%B3%95.html

What is the difference between DHE and ECDH? http://stackoverflow.com/questions/2701294/how-does-the-elliptic-curve-version-of-diffie-hellman-cryptography-work?rq=1

Example application for working with SPKAC (signed public key & challege) data coming from the element.https://github.com/jas-/node-spkac

Using Padding in Encryption http://www.di-mgt.com.au/cryptopad.html#randompadding

對稱加密和分組加密中的四種模式(ECB、CBC、CFB、OFB)http://www.cnblogs.com/happyhippy/archive/2006/12/23/601353.html

分組密碼工作模式https://zh.wikipedia.org/wiki/%E5%88%86%E7%BB%84%E5%AF%86%E7%A0%81%E5%B7%A5%E4%BD%9C%E6%A8%A1%E5%BC%8F#.E5.AF.86.E7.A0.81.E5.9D.97.E9.93.BE.E6.8E.A5.EF.BC.88CBC.EF.BC.89

為什麽說密文鏈接模式已經喪失安全性? https://www.zhihu.com/question/26437065

Elliptic Curve Cryptography: a gentle introduction http://andrea.corbellini.name/2015/05/17/elliptic-curve-cryptography-a-gentle-introduction/

Elliptic Curve Cryptography: ECDH and ECDSA http://andrea.corbellini.name/2015/05/30/elliptic-curve-cryptography-ecdh-and-ecdsa/

為什麽RSA公鑰每次加密得到的結果都不一樣? http://blog.csdn.net/guyongqiangx/article/details/74930951

NodeJS學習筆記 (13)數據加密-crypto(OK)