JavaからGnuPG(PGP)の鍵を利用する
鍵生成のついでにJavaから利用する方法のメモ
自力で読み込みプログラムを作成すると大変なので、Bouncy Castleのライブラリを利用します。必要なjarファイルはbcprovとbcpgの二つになります。
最初にGnuPGの鍵が保存されているsecring.gpgを読み込みます。読み込まれた結果はPGPSecretKeyRingに格納されます。
FileInputStream is = new FileInputStream("secring.gpg"); PGPSecretKeyRing keyRing = new PGPSecretKeyRing(is); is.close();
PGPSecretKeyRingは主鍵と副鍵からなる複数のPGPSecretKeyの集合になっています。getSecretKeys()で全ての鍵を取り出すことができます。またgetSecretKey()を使うと、ドキュメントに明記はされていませんが、主鍵を取り出すことができるようです。
secring.gpgに関連付けられたユーザIDは主鍵のPGPSecretKeyに格納されているようです。以下のようなコードでユーザIDを表示することができます。
public void printUserIDs(PGPSecretKeyRing keyRing) { Iterator it = keyRing.getSecretKey().getUserIDs(); while(it.hasNext()) { System.out.println(it.next()); } }
secring.gpgには用途ごとに異なる鍵が含まれていますので、利用する際にはPGPSecretKeyRingから用途別に抜き出してやる必要があります。用途を調べるには署名用はPGPSecretKeyのisSigningKey()、暗号用はPGPPublicKeyのisEncryptionKey()を利用します。
/** * PGPSecretKeyRingから署名用の鍵を抜き出す */ public List<PGPSecretKey> getSigningKeyList(PGPSecretKeyRing keyRing) { List<PGPSecretKey> keyList = new ArrayList<PGPSecretKey>(); Iterator it = keyRing.getSecretKeys(); while(it.hasNext()) { PGPSecretKey secretKey = (PGPSecretKey) it.next(); if (secretKey.isSigningKey()) { keyList.add(secretKey); } } return keyList; } /** * PGPSecretKeyRingから暗号用の鍵を抜き出す */ public static List<PGPSecretKey> getEncryptionKeyList(PGPSecretKeyRing keyRing) { List<PGPSecretKey> keyList = new ArrayList<PGPSecretKey>(); Iterator it = keyRing.getSecretKeys(); while(it.hasNext()) { PGPSecretKey secretKey = (PGPSecretKey) it.next(); PGPPublicKey publicKey = secretKey.getPublicKey(); if (publicKey.isEncryptionKey()) { keyList.add(secretKey); } } return keyList; }
後はPGPSecretKeyからjava.securityのPrivateKeyやPublicKeyを取り出せば、JCEのインターフェイスを利用して署名等に利用できるようになります。
List<PGPSecretKey> signingKeyList = getSigningKeyList(keyRing); PGPSecretKey secretKey = signingKeyList.get(0); // 例として最初に見つかった署名用鍵を利用 printFingerprint(secretKey); if (Security.getProvider("BC") == null) { Security.addProvider(new BouncyCastleProvider()); } char[] passphrase = "passphrase".toCharArray(); // 実際は何らかの方法でユーザからの入力などを行う // JDK1.6以降ではコンソールからのパスワード入力が可能になったが、 // Eclipseのコンソールでは動作しないので注意 // char[] passphrase = System.console().readPassword(); // パスフレーズを用いて暗号化された鍵を取り出す。 // 取り出した後は、パスフレーズをメモリ上に残さないためにクリアする。 PGPPrivateKey pgpPrivateKey = secretKey.extractPrivateKey(passphrase, "BC"); Arrays.fill(passphrase, '0'); // PGPPrivateKey ==> PrivateKey PrivateKey privateKey = pgpPrivateKey.getKey(); // PGPPublicKey ==> PublicKey PGPPublicKey pgpPublicKeyKey = secretKey.getPublicKey(); PublicKey publicKey = pgpPublicKeyKey.getKey("BC");
public void printFingerprint(PGPSecretKey secretKey) { byte[] fingerprint = secretKey.getPublicKey().getFingerprint(); for(int i = 0; i < fingerprint.length; i++) { System.out.print(String.format("%02X", fingerprint[i])); if ((i % 2) != 0) { System.out.print(" "); } } System.out.println(""); }
これでGnuPGの鍵を利用することが出来るようになりました。以下は署名の生成と検証に利用する例です。
// 署名生成 Signature signature = Signature.getInstance("SHA1withRSA"); signature.initSign(privateKey); signature.update("hello".getBytes()); byte[] sign = signature.sign(); // 署名検証 signature.initVerify(publicKey); signature.update("hello".getBytes()); boolean result = signature.verify(sign); System.out.println("verify: " + (result ? "ok" : "bad"));