自分用のメモです。
要件定義
-
~/.ssh/id_rsa
と~/.ssh/id_rsa.pub
を使って暗号化したい -
~/.ssh/id_rsa
はパスワードあり - streamで対応したい
上記3つに対応した方法が見つからなかったので、やってみました。
公開鍵を使って暗号化 -> 秘密鍵を使って復号化
ursaモジュールが良さそうでしたので、これを利用。
var ursa = require('ursa');
var fs = require('fs');
var userHome = process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
var keyPub = fs.readFileSync(userHome + '/.ssh/id_rsa.pub', 'utf8');
var pubKey = ursa.openSshPublicKey(keyPub);
// 公開鍵で暗号化
var encryptedMsg = pubKey.encrypt("Everything is going to be 200 OK", 'utf8', 'base64');
console.log(encryptedMsg); //Pk+wRhxOxjZFQ5cPlssNxqL...jt1Y4xdUQ== となる
// 秘密鍵を使って復号化
var keyPrivate = fs.readFileSync(userHome + '/.ssh/id_rsa', 'utf8');
var privateKey = ursa.createPrivateKey(keyPrivate, '<パスワード>');
var msg = privateKey.decrypt(encryptedMsg, 'base64', 'utf8');
console.log(msg); // Everything is going to be 200 OK となる
秘密鍵を使って暗号化 -> 公開鍵を使って復号化
// 秘密鍵を使って暗号化
var keyPrivate = fs.readFileSync(userHome + '/.ssh/id_rsa', 'utf8');
var privateKey = ursa.createPrivateKey(keyPrivate, '<パスワード>');
var encryptedMsg = privateKey.privateEncrypt("Everything is going to be 200 OK", 'utf8', 'base64');
// 公開鍵を使って復号化
var keyPub = fs.readFileSync(userHome + '/.ssh/id_rsa.pub', 'utf8');
var pubKey = ursa.openSshPublicKey(keyPub);
var msg = pubKey.publicDecrypt(encryptedMsg, 'base64', 'utf8');
console.log(msg); // Everything is going to be 200 OK になる
Stream APIで対応したい
stream-buffersモジュールで200バイトずつ取得して、それをthrough2モジュールで受け取って暗号化。
その後にまた復号化して復号化して、writableStreamに書き込む。
writableStreamに書き込みが終わると、全て複合されているので、結果を表示して終了。
var ursa = require('ursa');
var fs = require('fs');
var userHome = process.env.HOME;
// 公開鍵
var keyPub = fs.readFileSync(userHome + '/.ssh/id_rsa.pub', 'utf8');
var pubKey = ursa.openSshPublicKey(keyPub);
// 秘密鍵
var keyPrivate = fs.readFileSync(userHome + '/.ssh/id_rsa', 'utf8');
var privateKey = ursa.createPrivateKey(keyPrivate);
var streamBuffers = require('stream-buffers'),
through2 = require('through2');
// 平文
var text = ["これは日本語のテキストです。",
"公開鍵で200バイトずつ暗号化して、myWritableStreamBufferに書き込んでいます。",
"パイプ処理が終わるとmyWritableStreamBufferにfinishイベントが発生します。"].join("\n");
// Readstreamで200バイトずつ読み込む
var myReadableStreamBuffer = new streamBuffers.ReadableStreamBuffer({
frequency: 10, // 10msずつ処理.
chunkSize: 200 // 200バイトずつ読み込む
});
myReadableStreamBuffer.put(text);
// stop()メソッドを実行しないと readableStreamがendにならないので、
// writeableStreamもfinishにならない
myReadableStreamBuffer.stop();
// 書き込み先
var myWritableStreamBuffer = new streamBuffers.WritableStreamBuffer({
initialSize: 200, // 200バイトのバッファを確保
incrementAmount: 200 // 200バイトずつバッファを追加
});
myReadableStreamBuffer
.pipe(through2(function(chunk, enc, cb) {
// 公開鍵で暗号化
this.push(pubKey.encrypt(chunk));
cb();
}))
.pipe(through2(function(chunk, enc, cb) {
// 秘密鍵で復号化
this.push(privateKey.decrypt(chunk));
cb();
}))
.pipe(myWritableStreamBuffer);
myWritableStreamBuffer.on('finish', function() {
console.log("---復号化されたテキスト");
console.log(myWritableStreamBuffer.getContentsAsString('utf8'));
});
id_rsa と id_rsa.pubが一致するか比較
id_rsaとid_rsa.pubが一致するかどうかだけをテストしたい場合はこうなります。
var keyPub = fs.readFileSync(userHome + '/.ssh/id_rsa.pub', 'utf8');
var pubKey = ursa.openSshPublicKey(keyPub);
var keyPrivate = fs.readFileSync(userHome + '/.ssh/id_rsa', 'utf8');
var privateKey = ursa.createPrivateKey(keyPrivate, '<パスワード>');
console.log("match:" + ursa.matchingPublicKeys(privateKey, pubKey)); // true or false
共通鍵の作成
公開鍵認証では公開鍵と秘密鍵で認証をしたあと、新しい公開鍵と秘密鍵を作成して相互に持つ必要があるのですが、
この共通鍵を作るのはこうなります。
// Generates a pair key.
// https://github.com/quartzjer/ursa#ursagenerateprivatekeymodulusbits-exponent
var modulusBits = 2048;
var exponent = 65537;
var pairKey = ursa.generatePrivateKey(modulusBits, exponent);
var publicKey = ursa.createPublicKey(pairKey.toPublicPem());
var privateKey = ursa.createPublicKey(pairKey.toPrivatePem());
まとめ
Node.jsっていろいろな人が素晴らしいモジュールを作ってくれているので、便利ですね。
(玉石混淆ですけどね)