Jipher JCEとネイティブ・イメージ

Jipher JCEはオラクル社が開発した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以上)でサポートされています。

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

Jipherのインストール

Jipherは、個別のコンポーネントとして配布され、GraalVMアップデータ・ツールを使用してGraalVMに追加できます。

次のコマンドを実行して、Jipherをインストールします:

gu install jipher

これにより、GraalVMのlibディレクトリ(lib/jipher/jipher-jce.JARおよびlib/jipher/jipher-pki.JAR)にJipher JARファイルが追加されます。

Jipherを使用したネイティブ実行可能ファイルのビルド

JCAアルゴリズムはリフレクションに依存します。事前コンパイル時に必要なすべてのコード・パスをネイティブ実行可能ファイルに含めるには、native-imageツールで、リフレクションによって実行時に動的にアクセスされるJavaコードと、呼び出される可能性のあるネイティブ・コードを認識する必要があります。(詳細は、こちらを参照してください)。これを行うには、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 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アプリケーションの場合、エージェントは複数の構成ファイルを作成します。確認する内容は次のとおりです:

    • resource-config.json: Jipherは、JAR内にOpenSSLライブラリ(サポートされているすべてのプラットフォーム用)をバンドルします。このファイルには、libjipher.sofips.so、およびopenssl.cnfのエントリが、対応するチェックサム・ファイルとともにリストされます。特定のエントリは、エージェントが実行されるプラットフォームに関連し、ネイティブ実行可能ファイルがビルドされるプラットフォームに対応している必要があります。たとえば、Linux x64では、内容は次のようになります:
        {
        "resources":{
        "includes":[
            {
            "pattern":"\\Qlibs/linux_x64/fips.so.crc32\\E"
            },
            {
            "pattern":"\\Qlibs/linux_x64/fips.so\\E"
            },
            {
            "pattern":"\\Qlibs/linux_x64/libjipher.so.crc32\\E"
            },
            {
            "pattern":"\\Qlibs/linux_x64/libjipher.so\\E"
            },
            {
            "pattern":"\\Qlibs/linux_x64/openssl.cnf.crc32\\E"
            },
            {
            "pattern":"\\Qlibs/linux_x64/openssl.cnf\\E"
            },
            {
            "pattern":"\\Qlibs\\E"
            }
        ]},
        "bundles":[]
        }
      
    • jni-config.json: このファイルには、ネイティブ・メソッド宣言を含むJavaクラスのJipher内部OpenSSLパッケージのエントリがリストされます。内容は次のようになります:
        {
        "name":"[B"}
        ,
        {
        "name":"[[B"}
        ,
        {
        "name":"com.oracle.jipher.internal.openssl.JniOpenSsl"}
        ,
        {
        "name":"java.lang.Boolean",
        "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]
        }
      
    • reflect-config.json: このファイルには、JCE SPIを実装するJavaクラスのJipher内部SPIパッケージのエントリがリストされます。内容は次のようになります:
        [
        {
        "name":"com.oracle.jipher.internal.spi.KeyPairGen$Rsa",
        "methods":[{"name":"<init>","parameterTypes":[] }]}
        ,
        {
        "name":"com.oracle.jipher.internal.spi.RsaDigestSig$Sha512WithRsa",
        "methods":[{"name":"<init>","parameterTypes":[] }]}
        ,
        ...]
      
  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サービスのサポートの詳細は、こちらを参照してください。