iText 的新用户试图在https://itextpdf.com/book/digitalsignatures中获取从示例 3.1 和代码示例 3.2 改编的示例代码
我在 FYI 下面包含了代码片段。
我让托管服务提供商.pfx
从 GlobalSign 证书创建一个文件(他们不提供.p12
文件)。他们发出了以下linux命令:
# openssl pkcs12 -export -out cert.pfx -inkey www.mydomain.com.key
-in ../certs/www.mydomain.com.crt -certfile ../certs/ca-bundle.crt
ca-bundle.crt
GlobalSign 来自哪里。为了得到一个.p12
,我只是将文件复制.pfx
到一个带有.p12
扩展名的新文件中。从我在网上看到的情况来看,人们之前已经这样做了(与 iText 无关)并且成功了,因为.pfx
文件和.p12
文件是二进制等价物。
要验证,可以在 linux 提示符下键入:
openssl pkcs12 -info -in /path/to/file/cert.pfx
然后它会要求输入密码,但没有,所以只需按 Enter,然后它会要求输入我输入的私人密码短语(例如myPrivateCertPassword
),然后它会显示我的私人证书、GlobalSign 的公共证书和我的私钥。
现在是我感到困惑的地方。没有导入密码,所以我在 iText 中尝试了以下尝试(如下面的片段所示):
ks.load(new FileInputStream(CONSTANTS.CERTIFICATE_PATH), null);
ks.load(new FileInputStream(CONSTANTS.CERTIFICATE_PATH), "".toCharArray());
没有一个工作。第一个给出运行时错误:
java.io.IOException: PKCS12 key store mac invalid - wrong password or corrupted file.
第二个给出运行时错误:
java.lang.NoClassDefFoundError : org/bouncycastle/cert/X509CertificateHolder
有什么想法可能是错的吗?
确切地说,ks.load()
代码行中使用的密码来自哪里(GlobalSign?服务器托管公司?我?)。export key
当我发出上述导出命令时,这是否称为?
提前感谢您的任何评论。
-------- 代码片段如下 ---------
public static final char[] PASS = "myPrivateCertPassword".toCharArray();
public static final String CERTIFICATE_PATH = "/path/to/file/cert.p12";
在 main() 中:
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
KeyStore ks = KeyStore.getInstance("pkcs12", provider.getName());
ks.load(new FileInputStream(CONSTANTS.CERTIFICATE_PATH), null);
// ks.load(new FileInputStream(CONSTANTS.CERTIFICATE_PATH), "".toCharArray()); // this also fails
String alias = (String)ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias, CONSTANTS.PASS);
Certificate[] chain = ks.getCertificateChain(alias);
sign(userFile, userFile_signed, chain, pk, DigestAlgorithms.SHA256,
provider.getName(),CryptoStandard.CMS, "Test", "Ghent", null, null, null, 0);
外部主要():
public void sign(
String src,
String dest,
Certificate[] chain,
PrivateKey pk,
String digestAlgorithm,
String provider,
CryptoStandard subfilter,
String reason,
String location,
Collection<CrlClient> crlList,
OcspClient ocspClient, TSAClient tsaClient,
int estimatedSize)
throws GeneralSecurityException, IOException, DocumentException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
// Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(reason);
appearance.setLocation(location);
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, pks, chain,
crlList, ocspClient, tsaClient, estimatedSize, subfilter);
}
更新 1:
我通过如上所述导出创建了一个新的 cert.pfx 文件,但这次我输入了一个“导出密码”,as exportPassword
,我将其插入为:
ks.load(new FileInputStream(CONSTANTS.CERTIFICATE_PATH), "exportPassword".toCharArray());
但这会产生一个新的运行时错误:
java.io.IOException: exception decrypting data - java.security.InvalidKeyException: Illegal key size
我越来越近了吗?
更新 2:
我根据布鲁诺的评论安装了 JCE,现在我收到了这个错误:
java.lang.NoClassDefFoundError : org/bouncycastle/tsp/TimeStampTokenInfo
更新 3:
我还可以通过将 www.bouncycastle.org 中的 bcpkix-jdk15on-149.jar 添加到 /lib 目录来清除上述错误,并将以下内容添加到 java 程序中:
import org.bouncycastle.jce.provider.BouncyCastleProvider; // this was already there
import org.bouncycastle.tsp.TimeStampTokenInfo; // this is new and fixed the above error
现在我可以看到数字签名了!
更新 4:
对于那些感兴趣的人,请参阅我的后续帖子: iText:人们使用哪种类型的证书来在 Linux 上自动进行 PDF 签名?