OK、iText (7) および BouncyCastle (154) jar ファイルに対してコンパイルされたテスト Java プログラムを実行しようとしています。signDetached 関数を呼び出すときに OCSP 応答を追加しようとするまで、すべてが問題なく動作します。
私のコードは基本的に、iText Web サイトの次のコードです。
必要に応じて証明書とパスワードの詳細を置き換えました。すべて正常にコンパイルされますが、実行すると Java コードが次のエラーで失敗します。
スレッド「メイン」の例外 java.lang.NoClassDefFoundError: com.itextpdf.signatures.OcspClientBouncyCastle.getEncoded(OcspClientBouncyCastle.java:148) の org.bouncycastle.ocsp.RevokedStatus com.itextpdf.signatures.PdfSigner.signDetached(PdfSigner.java) :510) DECSignHello.DoSign(DECSignHello.java:314) で DECSignHello.main(DECSignHello.java:125) 原因: java.lang.ClassNotFoundException: org.bouncycastle.ocsp.RevokedStatus at java.net.URLClassLoader.findClass( URLClassLoader.java:600) の java.lang.ClassLoader.loadClassHelper(ClassLoader.java:777) の java.lang.ClassLoader.loadClass(ClassLoader.java:750) の sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java) :326) at java.lang.ClassLoader.loadClass(ClassLoader.java:731) ... 4 つ以上
OcspClientBouncyCastle
もう少し深く掘り下げると、iText7 が、はるかに古い BouncyCastle 実装 (151 より前のもの - このクラスを含むバージョンをまだ正確に特定していない) のクラス ファイルを参照するという Java クラスを使用していることがわかります。参照しているクラスはorg.bouncycastle.ocsp.RevokedStatus
. 問題の原因となっているコードは次のとおりです。
status instanceof org.bouncycastle.ocsp.RevokedStatus
の 148 行目OcspClientBouncyCastle.java
。151以降のBouncyCastle jarファイルには存在しません...
同じ iText クラスファイルは、他のすべての BouncyCastle 関連OcspClientBouncyCastle.java
のパスにある新しい BouncyCastle クラスも使用しているように見えるため、2 つの異なる BouncyCastle 実装を使用しているように見えます。org.bouncycastle.cert.ocsp.*
少なくとも私にはそう見えます。
OcspClientBouncyCastle.java
サービスプロバイダーから無効な OCSP 応答を受け取ったとき (失効した証明書を使用してテストしているため、予期されていた)、ソースコードによって参照されているクラスがクラスパスに見つからないため、OCSP テストが失敗する原因となっています。トラップできないエラーですべてが失敗します。
これで、次のように 154 BouncyCastle 実装の有効なクラスを使用して手動で OCSP 検証を行うことで、いわばこれを回避できます。
BasicOCSPResp l_resp = l_ocsp_client.getBasicOCSPResp(l_other_cert, l_root_cert, l_ocsp_url); SingleResp[] l_response = l_resp.getResponses();
等々...
ただし、iText7関数 ( OCSP クライアント パラメーターのPdfSigner.signDetached
パスとインスタンス)を使用するとすぐに、コードが呼び出され、「古い」クラスが使用されます。OcspClientBouncyCastle
OcspClientBouncyCastle.java
クラスパスに古い(まだ特定されていない)BouncyCastle jarファイルを含めることで、そのエラーを回避できると思いますが、それは非常に雑然としたソリューションのようです。
ファイルを編集しOcspClientBouncyCastle.java
て誤った関数を修正し、BouncyCastle 関数の新しい実装を使用することもできますが、それもやり過ぎのようです。
私はコマンドラインで AIX 環境で作業していることに言及する必要があります。したがって、Java 開発ツール/環境はありませんvi
。クラスパスを (コンパイルおよび実行時に) 手動で構築し、ksh シェル スクリプトを使用してコードを実行しています。
結局のところ、私の質問はこれだと思います: iText がOcspClientBouncyCastle
このようにクラスを実装する合理的な理由を誰か思いつくことができますか?
私はこのフォーラムの長年のフォロワー/ユーザーですが、何かを投稿するのはこれが初めてです。私の「質問」の範囲はかなり広いので、前もってお詫び申し上げます。
サンプルコードは次のとおりです。
/*
* This class is part of the white paper entitled
* "Digital Signatures for PDF documents"
* written by Bruno Lowagie
*
* For more info, go to: http://itextpdf.com/learn
*/
package signatures.chapter3;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.Properties;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.OcspClient;
import com.itextpdf.text.pdf.security.OcspClientBouncyCastle;
public class C3_07_SignWithOCSP extends C3_01_SignWithCAcert {
public static final String SRC = "src/main/resources/hello.pdf";
public static final String DEST = "results/chapter3/hello_cacert_ocsp.pdf";
public static void main(String[] args) throws IOException, GeneralSecurityException, DocumentException {
Properties properties = new Properties();
properties.load(new FileInputStream("c:/home/blowagie/key.properties"));
String path = properties.getProperty("PRIVATE");
char[] pass = properties.getProperty("PASSWORD").toCharArray();
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
KeyStore ks = KeyStore.getInstance("pkcs12", provider.getName());
ks.load(new FileInputStream(path), pass);
String alias = (String)ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias, pass);
Certificate[] chain = ks.getCertificateChain(alias);
OcspClient ocspClient = new OcspClientBouncyCastle();
C3_07_SignWithOCSP app = new C3_07_SignWithOCSP();
app.sign(SRC, DEST, chain, pk, DigestAlgorithms.SHA256, provider.getName(), CryptoStandard.CMS, "Test", "Ghent",
null, ocspClient, null, 0);
}
}
C3_08_GetTsaUrl.java
/*
* This class is part of the white paper entitled
* "Digital Signatures for PDF documents"
* written by Bruno Lowagie
*
* For more info, go to: http://itextpdf.com/learn
*/
package signatures.chapter3;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Properties;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import com.itextpdf.text.pdf.security.CertificateUtil;
public class C3_08_GetTsaUrl {
public static void main(String[] args) throws IOException, GeneralSecurityException {
Properties properties = new Properties();
properties.load(new FileInputStream("c:/home/blowagie/key.properties"));
String path = properties.getProperty("PRIVATE");
char[] pass = properties.getProperty("PASSWORD").toCharArray();
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
KeyStore ks = KeyStore.getInstance("pkcs12", provider.getName());
ks.load(new FileInputStream(path), pass);
String alias = (String)ks.aliases().nextElement();
Certificate[] chain = ks.getCertificateChain(alias);
for (int i = 0; i < chain.length; i++) {
X509Certificate cert = (X509Certificate)chain[i];
System.out.println(String.format("[%s] %s", i, cert.getSubjectDN()));
System.out.println(CertificateUtil.getTSAURL(cert));
}
}
}