LoginSignup
24
28

More than 5 years have passed since last update.

Node.jsで公開鍵暗号方式を使う

Last updated at Posted at 2016-02-26

自分用のメモです。

要件定義

  • ~/.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っていろいろな人が素晴らしいモジュールを作ってくれているので、便利ですね。
(玉石混淆ですけどね)

24
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
28