ネイティブイメージを使用したJipher JCE

Jipher JCEは、Oracleが開発したJava暗号化アーキテクチャ(JCA)プロバイダーであり、事前設定済みでFIPS検証済みのOpenSSL 3.0をパッケージ化しています。Jipherプロバイダーは、OpenSSL 3.0のFIPSプロバイダーを含む、FIPSで許可されているアルゴリズムをサポートしています。Jipherは、Bouncy CastleやデフォルトのJDKプロバイダーと比較して、競争力のあるパフォーマンスを提供します。FIPSで許可されたアルゴリズムのみを使用する必要があるコンテキストでは、ネイティブイメージでJipherを有効にすることをお勧めします。一部のアルゴリズムは、特定のユースケースでのみFIPSで許可されていることに注意してください。そのため、Jipherによって提供される一部のアルゴリズムは、すべての目的でFIPSによって許可されない場合があります。

注:JipherはGraalVM Community Editionでは使用できません。AMD64およびAArch64アーキテクチャのLinuxおよびmacOS(macOS 10.15以降)でサポートされています。

Jipher JARファイルは、Oracle GraalVMコアパッケージの *lib/jipher/jipher-jce.jar* および *lib/jipher/jipher-pki.jar* に含まれています。Jipherを有効にするには、これらのJARファイルをアプリケーションクラスパスに渡します。

このページでは、GraalVMネイティブイメージでJipherを使用する方法について説明します。

Jipherを使用してネイティブ実行ファイルを作成する #

JCAアルゴリズムはリフレクションに依存しています。Ahead-of-Timeコンパイル中に必要なすべてのコードパスをネイティブ実行ファイルに含めるには、実行時にリフレクションを介して動的にアクセスされるJavaコードと、呼び出される可能性のあるネイティブコードを、`native-image`ツールが認識する必要があります。(詳細はこちらこちら)。これは、エージェントによって収集されたJSONベースのメタデータを提供することで実現できます。Jipherを介して動的にアクセスされるJCAサービスも、エージェントによって自動的に登録されます。

以下の手順は、RSAベースの署名作成と検証を行う簡単なJavaアプリケーションを使用して、Jipherをネイティブ実行ファイルに埋め込む方法を示しています。

  1. 次のコードを *JipherExample.java* という名前のファイルに保存します

     import java.security.*;
     import java.util.*;
     import com.oracle.jipher.provider.*;
    
     class JipherExample {
         public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
             Provider jipher = new JipherJCE();
             Security.insertProviderAt(jipher, 1);
    
             byte[] data = new byte[1024];
             new Random().nextBytes(data);
    
             KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", jipher);
             keyGen.initialize(4096);
             KeyPair keypair = keyGen.generateKeyPair();
                
             Signature signer = Signature.getInstance("SHA512withRSA", jipher);
             signer.initSign(keypair.getPrivate());
             signer.update(data);
             byte[] signature = signer.sign();
                
             Signature verifier = Signature.getInstance("SHA512withRSA", jipher);
             verifier.initVerify(keypair.getPublic());
             verifier.update(data);
             boolean isValid = verifier.verify(signature);
             assert(isValid);
         }
     }
    
  2. クラスパスにJipher JARファイルを使用してアプリケーションをコンパイルします

     javac -cp $GRAALVM_HOME/lib/jipher/jipher-jce.jar:$GRAALVM_HOME/lib/jipher/jipher-pki.jar JipherExample.java
    
  3. エージェントを有効にしてJVMでアプリケーションを実行します。トレースエージェントは、テスト実行中に検出されたすべての動的機能をキャプチャし、複数の *-config.json* ファイルに書き込みます。

     java -cp $GRAALVM_HOME/lib/jipher/jipher-jce.jar:$GRAALVM_HOME/lib/jipher/jipher-pki.jar:. -agentlib:native-image-agent=config-output-dir=<path> JipherExample
    

    ここで、`<path>` は設定ファイルを保存するディレクトリを指す必要があります。出力ディレクトリは `/META-INF/native-image/`(MavenまたはGradleでビルドする場合は `/resources/META-INF/native-image/`)にすることをお勧めします。後で、ネイティブ実行ファイルをビルドするときに、`native-image`ビルダーはその場所からファイルを自動的に取得します。

    このJavaアプリケーションの場合、エージェントは次の内容の *reachability-metadata.json* ファイルを作成します

     {
       "reflection":[
         {
           "type":"com.oracle.jipher.internal.spi.KeyPairGen$Rsa",
           "methods":[{"name":"<init>","parameterTypes":[] }]
         },
         {
           "type":"com.oracle.jipher.internal.spi.RsaDigestSig$Sha512WithRsa",
           "methods":[{"name":"<init>","parameterTypes":[] }]
         }
       ],
       "resources":[
         {"glob":"libs/linux_x64/fips.so.crc32"},
         {"glob":"libs/linux_x64/fips.so"},
         {"glob":"libs/linux_x64/libjipher.so.crc32"},
         {"glob":"libs/linux_x64/libjipher.so"},
         {"glob":"libs/linux_x64/openssl.cnf.crc32"},
         {"glob":"libs/linux_x64/openssl.cnf"},
         {"glob":"libs"}
       ],
       "jni":[
         {"type":"[B"},
         {"type":"[[B"},
         {"type":"com.oracle.jipher.internal.openssl.JniOpenSsl"},
         {"type":"java.lang.Boolean","methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]}
       ]
     }
    
  4. エージェントがJipherへのすべての呼び出しを検出するために、JVMでエージェントを使用してアプリケーションを再実行します(エージェントは必要な回数だけ再実行できます)。これにより、負のテストケース(例外クラスをキャプチャできるようにするため)を含む設定スイート全体が再生成されます。後続の実行では、次のコマンドを使用します

     java -agentlib:native-image-agent=config-merge-dir=<path> JipherExample
    

    `config-merge-dir`コマンドは、新しい設定を以前のテスト実行の設定とマージします。

  5. 提供された設定でネイティブ実行ファイルを作成します

     native-image JipherExample
    

    設定ファイルが `/META-INF/native-image/` 以外のディレクトリに配置されている場合は、ビルド時に `-H:ConfigurationFileDirectories=<path>` フラグを渡して、`native-image` ツールに新しい場所を通知します

     native-image -H:ConfigurationFileDirectories=<path> JipherExample
    
  6. ネイティブ実行ファイルを実行します

     ./jipherexample
    

Jipherがネイティブ実行ファイルに埋め込まれて**いない**場合、JVMによってロードされると、JARに埋め込まれたネイティブライブラリと *openssl.cnf* ファイルをファイルシステムに抽出し、JVMプロセスに動的にロードします。Jipherがネイティブ実行ファイルに埋め込まれて**いる**場合、ネイティブライブラリと *openssl.cnf* ファイルをファイルシステムに抽出し、ネイティブプロセスに動的にロードし続けます。FIPSで許可されたアルゴリズムのみを使用する必要がある場合は、GraalVMネイティブイメージにJipherを使用することをお勧めします。ネイティブイメージでのJCAサービスのサポートの詳細については、こちらをご覧ください。

お問い合わせ