1. 概要

非対称暗号化とも呼ばれる公開鍵暗号化では、暗号化メカニズムは2つの関連する鍵に依存します。 公開鍵と秘密鍵。 公開鍵はメッセージの暗号化に使用されますが、秘密鍵の所有者のみがメッセージを復号化できます。 

このチュートリアルでは、PEMファイルから公開鍵と秘密鍵を読み取る方法を学習します。

まず、公開鍵暗号に関するいくつかの重要な概念について学習します。 次に、純粋なJavaを使用してPEMファイルを読み取る方法を学習します。

最後に、別のアプローチとしてBouncyCastleライブラリについて説明します。

2. コンセプト

始める前に、いくつかの重要な概念について説明しましょう。

X.509は、公開鍵証明書の形式を定義する標準です。 したがって、この形式は、他の情報の中でも特に公開鍵を記述します。

DER は、X.509証明書やPKCS8秘密鍵などのデータをファイルに保存するための最も一般的なエンコード形式です。これはバイナリエンコードであり、結果のコンテンツはテキストエディターで表示できません。

PKCS8 は、秘密鍵情報を格納するための標準構文です。秘密鍵は、対称アルゴリズムを使用してオプションで暗号化できます。 

RSA秘密鍵は、この標準だけでなく、他のアルゴリズムでも処理できます。 PKCS8秘密鍵は通常、PEMエンコーディング形式で交換されます。

PEM は、DER証明書のbase-64エンコードメカニズムです。 PEMは、公開鍵/秘密鍵や証明書要求など、他の種類のデータもエンコードできます。

PEMファイルには、エンコードされたデータのタイプを説明するヘッダーとフッターも含まれています。

-----BEGIN PUBLIC KEY-----
...Base64 encoding of the DER encoded certificate...
-----END PUBLIC KEY-----

3. 純粋なJavaの使用

3.1. ファイルからPEMデータを読み取る

PEMファイルを読み取り、その内容を文字列に保存することから始めましょう。

String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());

3.2. PEM文字列から公開鍵を取得する

次に、PEMでエンコードされた文字列から公開鍵を取得するユーティリティメソッドを作成します。

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjtGIk8SxD+OEiBpP2/T
JUAF0upwuKGMk6wH8Rwov88VvzJrVm2NCticTk5FUg+UG5r8JArrV4tJPRHQyvqK
wF4NiksuvOjv3HyIf4oaOhZjT8hDne1Bfv+cFqZJ61Gk0MjANh/T5q9vxER/7TdU
NHKpoRV+NVlKN5bEU/NQ5FQjVXicfswxh6Y6fl2PIFqT2CfjD+FkBPU1iT9qyJYH
A38IRvwNtcitFgCeZwdGPoxiPPh1WHY8VxpUVBv/2JsUtrB/rAIbGqZoxAIWvijJ
Pe9o1TY3VlOzk9ASZ1AeatvOir+iDVJ5OpKmLnzc46QgGPUsjIyo6Sje9dxpGtoG
QQIDAQAB
-----END PUBLIC KEY-----

Fileをパラメーターとして受け取ったとしましょう。

public static RSAPublicKey readPublicKey(File file) throws Exception {
    String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());

    String publicKeyPEM = key
      .replace("-----BEGIN PUBLIC KEY-----", "")
      .replaceAll(System.lineSeparator(), "")
      .replace("-----END PUBLIC KEY-----", "");

    byte[] encoded = Base64.decodeBase64(publicKeyPEM);

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
    return (RSAPublicKey) keyFactory.generatePublic(keySpec);
}

ご覧のとおり、最初にヘッダー、フッター、および新しい行も削除する必要があります。 次に、Base64でエンコードされた文字列を対応するバイナリ形式にデコードする必要があります。 

次に、公開鍵材料を処理できる鍵仕様クラスに結果をロードする必要があります。 この場合、 X509EncodedKeySpec クラス。 

最後に、仕様から公開鍵オブジェクトを生成するには、 KeyFactory クラス。 

3.3. PEM文字列から秘密鍵を取得する

公開鍵の読み取り方法がわかったので、秘密鍵を読み取るアルゴリズムは非常に似ています。 

PKCS8形式のPEMエンコードされた秘密鍵を使用します。 ヘッダーとフッターがどのように見えるか見てみましょう。

-----BEGIN PRIVATE KEY-----
...Base64 encoded key...
-----END PRIVATE KEY-----

以前に学んだように、PKCS8キーマテリアルを処理できるクラスが必要です。 The PKCS8EncodedKeySpec クラスはその役割を果たします。

では、アルゴリズムを見てみましょう。

public RSAPrivateKey readPrivateKey(File file) throws Exception {
    String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());

    String privateKeyPEM = key
      .replace("-----BEGIN PRIVATE KEY-----", "")
      .replaceAll(System.lineSeparator(), "")
      .replace("-----END PRIVATE KEY-----", "");

    byte[] encoded = Base64.decodeBase64(privateKeyPEM);

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
    return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}

4. BouncyCastleライブラリの使用

4.1. 公開鍵を読む

BouncyCastleライブラリを調べて、純粋なJava実装の代わりにそれをどのように使用できるかを見ていきます。

公開鍵を取得しましょう:

public RSAPublicKey readPublicKey(File file) throws Exception {
    KeyFactory factory = KeyFactory.getInstance("RSA");

    try (FileReader keyReader = new FileReader(file);
      PemReader pemReader = new PemReader(keyReader)) {

        PemObject pemObject = pemReader.readPemObject();
        byte[] content = pemObject.getContent();
        X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
        return (RSAPublicKey) factory.generatePublic(pubKeySpec);
    }
}

BouncyCastleを使用する際に知っておく必要のある重要なクラスがいくつかあります。

  • PemReader Reader をパラメーターとして受け取り、その内容を解析します。 不要なヘッダーを削除し基になるBase64PEMデータをバイナリ形式にデコードします。
  • PemObjectは、PemReaderによって生成された結果を格納します

Javaのクラス( X509EncodedKeySpec、KeyFactory )をBouncyCastle独自のクラス( JcaPEMKeyConverter )にラップする別のアプローチを見てみましょう。

public RSAPublicKey readPublicKeySecondApproach(File file) throws IOException {
    try (FileReader keyReader = new FileReader(file)) {
        PEMParser pemParser = new PEMParser(keyReader);
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(pemParser.readObject());
        return (RSAPublicKey) converter.getPublicKey(publicKeyInfo);
    }
}

4.2. 秘密鍵を読む

ここで、上記の例と非常によく似た2つの例を示します。

最初の例では、X509EncodedKeySpecクラスをPKCS8EncodedKeySpecクラスに置き換え、RSAPublicKeyの代わりにRSAPrivateKeyオブジェクトを返す必要があります。 ]:

public RSAPrivateKey readPrivateKey(File file) throws Exception {
    KeyFactory factory = KeyFactory.getInstance("RSA");

    try (FileReader keyReader = new FileReader(file);
      PemReader pemReader = new PemReader(keyReader)) {

        PemObject pemObject = pemReader.readPemObject();
        byte[] content = pemObject.getContent();
        PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
        return (RSAPrivateKey) factory.generatePrivate(privKeySpec);
    }
}

次に、秘密鍵を読み取るために、前のセクションの2番目のアプローチを少し作り直してみましょう。

public RSAPrivateKey readPrivateKeySecondApproach(File file) throws IOException {
    try (FileReader keyReader = new FileReader(file)) {

        PEMParser pemParser = new PEMParser(keyReader);
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(pemParser.readObject());

        return (RSAPrivateKey) converter.getPrivateKey(privateKeyInfo);
    }
}

ご覧のとおり、SubjectPublicKeyInfoPrivateKeyInfoに、RSAPublicKeyRSAPrivateKeyに置き換えました。

4.3. 利点

BouncyCastleライブラリによって提供されるいくつかの利点があります。

1つの利点は、ヘッダーとフッターを手動でスキップまたは削除する必要がないことです。もう1つは、Base64デコードの責任を負わないことです。 したがって、BouncyCastleを使用すると、エラーが発生しにくいコードを記述できます。

さらに、BouncyCastleライブラリはPKCS1形式もサポートしています。 PKCS1は暗号化キー(RSAキーのみ)を格納するために使用される一般的な形式でもあるという事実にもかかわらず、Javaはそれ自体をサポートしていません。

5. 結論

この記事では、PEMファイルから公開鍵と秘密鍵を読み取る方法を学びました。

最初に、公開鍵暗号に関するいくつかの重要な概念を研究しました。 次に、純粋なJavaを使用して公開鍵と秘密鍵を読み取る方法を確認しました。

最後に、BouncyCastleライブラリを調べて、純粋なJava実装と比較していくつかの利点があるため、これが優れた代替手段であることを発見しました。

JavaBouncyCastleの両方のアプローチの完全なソースコードは、GitHubで入手できます。