好的,我正在尝试执行针对 iText (7) 和 BouncyCastle (154) jar 文件编译的测试 Java 程序。一切正常运行,直到我在调用 signDetached 函数时尝试添加 OCSP 响应。
我的代码基本上是来自 iText 网站的这段代码:
在必要时替换我的证书和密码详细信息。一切都可以编译,但是当我运行它时,Java代码会出现以下错误:
线程“主”java.lang.NoClassDefFoundError 中的异常:com.itextpdf.signatures.OcspClientBouncyCastle.getEncoded(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 在 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) 在 java.lang.ClassLoader.loadClass(ClassLoader.java:731) ... 4 更多
当我深入挖掘时,我发现 iText7 正在使用一个名为的 java 类OcspClientBouncyCastle
,它引用了一个来自更老的 BouncyCastle 实现的类文件(151 之前的东西 - 尚未确定包含此类的确切版本)。它引用的类是org.bouncycastle.ocsp.RevokedStatus
. 导致问题的代码是这样的:
status instanceof org.bouncycastle.ocsp.RevokedStatus
在第 148 行OcspClientBouncyCastle.java
。它不存在于 151 及以上版本的任何 BouncyCastle jar 文件中......
同一个 iText 类OcspClientBouncyCastle.java
文件似乎也使用在路径org.bouncycastle.cert.ocsp.*
中找到的较新的 BouncyCastle 类来处理与 BouncyCastle 相关的所有其他内容,因此看起来好像它使用了两种不同的 BouncyCastle 实现。至少在我看来是这样的。
这导致我的 OCSP 测试失败,因为当我从我的服务提供商处获得无效的 OCSP 响应时(这是预期的,因为我正在使用已撤销的证书进行测试)OcspClientBouncyCastle.java
源代码引用的类在类路径中找不到 - 所以这一切都因无法捕获的错误而崩溃。
现在,我可以通过使用 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 shell脚本执行我的代码。
所以,毕竟,我想我的问题是:谁能想到 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));
}
}
}