公開鍵暗号 入門

Takami Torao
  • このエントリーをはてなブックマークに追加

概要

異なる鍵 A と B が存在した場合、A で暗号化したデータが B でしか復号化できないといった特殊な鍵の組み合わせを非対称鍵 (asymmetric key) と言う。公開鍵暗号 (public-key encryption) は非対称鍵の特性を利用して鍵の片方を暗号化用に相手に公開し (公開鍵)、もう片方を復号化用に非公開にしておく (秘密鍵) 方式。

公開鍵暗号化
Fig 1. 公開鍵暗号

公開鍵だけを知りえてもデータの復号化は困難であるため傍受や改ざんなどに耐性を持つが、公開鍵を使用するだけでは悪意のある中継者による鍵のすり替えに対処できない (中間者攻撃)。公開鍵暗号を完全にするには公開鍵証明書や電子署名と組み合わせて PKI を構成する必要がある。

中継者攻撃
Fig 2. 中間者攻撃

また現実的な問題として公開鍵を用いた暗号は共通鍵暗号と比較して計算量が多いため、TLS ではセッションの開始時に鍵共有アルゴリズムで共通鍵を作成するためだけに使用して、それ以降の通信は共有鍵暗号で行う方法を採っている。

アルゴリズム

広く使われている公開鍵アルゴリズムは RSA と DSA だが DSA は電子署名のためのアルゴリズムであるため暗号化には使用できない。

RSA (Rivest Shamir Adleman)
1978 年に開発者の頭文字から名づけられた最初の実用的な公開鍵暗号方式。歴史が長く実効的な鍵の発見方法も見つかっていないため広く使用されている。RSA Data Security 社が保有していた特許が 2000 年に切れてからさらに普及が進んでいる。
DSA (Digital Signature Algorithm)
1991 年に米国家安全保障局で開発、1994 年に米標準技術局で認可された電子署名のための公開鍵アルゴリズム。内部で SHA-1 を使用しているため今後強度的な拡張が加えられる予定。より短い鍵で強い強度を持つ ECDSA の方が良い。

公開鍵の作成

Java 実装は RSA, EC (ECDSA) を使用して公開鍵暗号を使用することができる。J2SE 5.0 まで RSA のデフォルトの強度が 128bit に制限されていたが JSE 6 で解除された。公開鍵と秘密鍵のセット (キーペア) は KeyPairGenerator を使用して作成することができます。キー生成に使用できるアルゴリズムは KeyPairGenerator アルゴリズム、セキュア乱数のアルゴリズムは SecureRandom アルゴリズムを参照してください。SecureRandom のシードにパスワードに基づくハッシュ値などを指定することで同一のキーを何度でも生成することができます。

// キーペアの生成
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
generator.initialize(1024, random);
KeyPair keyPair = generator.generateKeyPair();

// 秘密鍵・公開鍵の取得
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic()

それぞれの鍵から getEncoded() で取得できるバイト配列を保存しておくことで次回以降の実行でも同じ鍵を復元する事ができます (これは汎用的なフォーマットであるため多言語からの利用も可能です)。

// 秘密鍵の保存 (公開鍵も同じ)
byte[] binary = privateKey.getEncoded();
FileOutputStream out = new FileOutputStream();
out.write(binary);
out.close();

復元時はそれぞれのフォーマットに対応する EncodedKeySpec を使用します。

KeyFactory keyFactory = KeyFactory.getInstance("RSA");

// 公開鍵の復元
byte[] binary = ...;	// 保存した公開鍵のバイナリ
EncodedKeySpec keySpec = new X509EncodedKeySpec(binary);
Publickey publicKey = keyFactory.generatePublic(keySpec);

// 秘密鍵の復元
binary = ...;			// 保存した秘密鍵のバイナリ
keySpec = new PKCS8EncodedKeySpec(binary);
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

暗号化と復号化

公開鍵を使用した暗号化/復号化は共通鍵の場合とほとんど同じです。Cipherアルゴリズム から公開鍵方式に対応するアルゴリズムを選択し、暗号化に公開鍵を、復号化に秘密鍵を指定してください。

// 暗号化の実行
binary = "hello, world".getBytes();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encrypted = cipher.doFinal(binary);

// 複合化の実行
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] binary = cipher.doFinal(encrypted);