我正在尝试使用智能卡和iText对PDF进行数字签名。我通过documentation阅读了如何使用iText签署文档并尝试自己使用其中的一些代码。以下是我正在使用的代码:
String pkcs11ConfigSettings =
"name = SmartCard\nlibrary = C:\\Program Files\\ActivIdentity\\ActivClient\\acpkcs201-ns.dll";
AuthProvider p =
new SunPKCS11(new ByteArrayInputStream(pkcs11ConfigSettings.getBytes()));
Security.addProvider(p);
KeyStore.PasswordProtection pp =
new KeyStore.PasswordProtection("012345".toCharArray());
KeyStore.Builder builder =
KeyStore.Builder.newInstance("PKCS11",p ,pp);
KeyStore ks = builder.getKeyStore();
Certificate[] cc = ks.getCertificateChain("Digital Signature Key");
PrivateKey pk = (PrivateKey)ks.getKey("Digital Signature Key", null);
OutputStream fos = new FileOutputStream("c:\\2.pdf");
PdfReader reader = new PdfReader(new FileInputStream(new File("C:\\1.pdf")));
PdfStamper stamper = PdfStamper.createSignature(reader, fos, '\0');
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setCrypto(pk, cc, null,PdfSignatureAppearance.SELF_SIGNED);
appearance.setVisibleSignature(new Rectangle(0, 0, 100, 100), 1,null);
stamper.close();
此方法的问题是当iText关闭PDFStamper
时,它会调用C_Sign()
来调用驱动程序提示输入PIN。
因此,如果这是一个应用程序,则需要我在签名之前输入我的个人识别码,以获得KeyStore
和PrivateKey
,以及何时出现驱动程序的PIN输入提示。反正要求两次PIN码吗?我对这些东西不熟悉,我是以错误的方式解决这个问题吗?
答案 0 :(得分:2)
如果我按照authenticated attributes示例,我可以将PIN对话框仅在每次签名文档时弹出一次。这是我最终使用的最终代码,希望它对其他人有用。
for(int i=0;i<2;i++) {
String pkcs11ConfigSettings =
"name = AuthProvider\nlibrary = C:\\Program Files\\ActivIdentity\\ActivClient\\acpkcs201-ns.dll";
AuthProvider p = (SunPKCS11)Security.getProvider("SunPKCS11-AuthProvider");
if(p==null) {
p = new SunPKCS11(new ByteArrayInputStream(pkcs11ConfigSettings.getBytes()));
p.setCallbackHandler(new CallbackHandler() {
@Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for(Callback c : callbacks)
if(c instanceof PasswordCallback) {
//HACK. if we set password to null it will bring up the drivers PIN dialog.
((PasswordCallback) c).setPassword(null);
}
}
});
Security.addProvider(p);
}
KeyStore ks = KeyStore.getInstance("PKCS11",p);
ks.load(null, null);
Certificate[] cc = ks.getCertificateChain("Digital Signature Key");
PrivateKey pk = (PrivateKey)ks.getKey("Digital Signature Key", null);
OutputStream fos = new FileOutputStream("c:\\doc" + i + ".pdf"); ;
PdfReader reader = new PdfReader(new FileInputStream(new File("C:\\1.pdf")));
PdfStamper stamper = PdfStamper.createSignature(reader, fos, '\0');
PdfSignatureAppearance sap = stamper.getSignatureAppearance();
sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
Calendar cal = Calendar.getInstance();
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.setDate(new PdfDate(cal));
dic.setName(PdfPKCS7.getSubjectFields((X509Certificate)cc[0]).getField("CN"));
sap.setCryptoDictionary(dic);
sap.setLayer2Text("Digitally signed by "+ dic.get(PdfName.NAME) +"\n\nDate: " + cal.getTime().toString());
HashMap<PdfName,Object> exc = new HashMap<PdfName,Object>();
exc.put(PdfName.CONTENTS, new Integer(0x2502));
sap.preClose(exc);
PdfPKCS7 pk7 = new PdfPKCS7(pk, cc, null, "SHA1", "SunPKCS11-AuthProvider", false);
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
byte buf[] = new byte[8192];
int n;
InputStream inp = sap.getRangeStream();
while ((n = inp.read(buf)) > 0) {
messageDigest.update(buf, 0, n);
}
byte hash[] = messageDigest.digest();
byte sh[] = pk7.getAuthenticatedAttributeBytes(hash, cal, null);
pk7.update(sh, 0, sh.length);
PdfDictionary dic2 = new PdfDictionary();
byte sg[] = pk7.getEncodedPKCS7(hash, cal);
byte out[] = new byte[0x2500 / 2];
System.arraycopy(sg, 0, out, 0, sg.length);
dic2.put(PdfName.CONTENTS, new PdfString(out).setHexWriting(true));
sap.close(dic2);
}