在服务器客户端条件下使用 USB 驱动程序签署 PDF
Sigining a PDF using USB driver in server client condition
我正在做一个项目,我需要使用基于 usb 的数字签名来签署 pdf。我在本地尝试了以下代码并能够签署 pdf。我的问题是天气以下代码将在基于 senerio 的客户端服务器中工作。
我的代码是:
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfSignatureAppearance;
import com.lowagie.text.pdf.PdfStamper;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.CRL;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import javax.servlet.RequestDispatcher;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.security.mscapi.SunMSCAPI;
public class Testing {
private static boolean resFlag;
public static void main (String args[])
{
try {
BouncyCastleProvider providerBC = new BouncyCastleProvider();
Security.addProvider(providerBC);
SunMSCAPI providerMSCAPI = new SunMSCAPI();
Security.addProvider(providerMSCAPI);
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null);
String alias = (String)ks.aliases().nextElement();
PrivateKey pk = (PrivateKey)ks.getKey(alias, null);
Certificate[] chain = ks.getCertificateChain(alias);
// //String e = request.getParameter("digiFile");
// KeyStore ks = KeyStore.getInstance("pkcs12");
// String f10 = CommonUtil.getRealPath();
// String str8 = f10 + "/DigiFiles/";
// //System.out.println("str8-->>>>>>>>" + str8 + e);
// ks.load(new FileInputStream("F:/DigiFiles/Anurag Goel.pfx"), "123".toCharArray());
//
//
// System.out.println("The actual path is " + str8);
// String alias = (String)ks.aliases().nextElement();
// PrivateKey key = (PrivateKey)ks.getKey(alias, "123".toCharArray());
// Certificate[] chain = ks.getCertificateChain(alias);
PdfReader reader = new PdfReader("F:/test.pdf");
FileOutputStream os = new FileOutputStream("F:/SampleOutPut61.pdf");
PdfStamper stamper = PdfStamper.createSignature(reader, os, '[=10=]',null,true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setCrypto(pk, chain, (CRL[])null, PdfSignatureAppearance.VERISIGN_SIGNED);
appearance.setReason("elicense project");
appearance.setLocation("Assam");
appearance.setVisibleSignature("hi");
stamper.close();
} catch (KeyStoreException var27) {
var27.printStackTrace();
resFlag = false;
} catch (NoSuchAlgorithmException var28) {
var28.printStackTrace();
resFlag = false;
} catch (CertificateException var29) {
var29.printStackTrace();
resFlag = false;
} catch (FileNotFoundException var30) {
var30.printStackTrace();
resFlag = false;
} catch (IOException var31) {
var31.printStackTrace();
resFlag = false;
} catch (UnrecoverableKeyException var32) {
var32.printStackTrace();
resFlag = false;
} catch (DocumentException var33) {
var33.printStackTrace();
resFlag = false;
} catch (Exception var34) {
var34.printStackTrace();
resFlag = false;
} finally {
RequestDispatcher rd;
}
}
}
请给我建议。谢谢大家
- 您使用了错误的 iText 版本,因此您创建的签名不是面向未来的(请阅读 this book 以找出您的代码有什么问题)。
- 你依赖的操作系统是Windows。你的服务器也是Windows服务器吗?如果它是 Linux 服务器,您的代码将无法工作。请咨询您的托管服务提供商,并询问您的托管服务提供商是否允许您在该服务器上拥有 USB 令牌(如果它不是专用服务器,他们很可能会拒绝)。
- 您正在使用 Windows-MY,这意味着您将身份验证委托给了操作系统。如果 USB 需要密码(通常需要),Windows 会打开一个对话框让您填写该密码。如果您将其部署在服务器上:每次有人请求签名时,您是否会安排人坐在该服务器旁边填写该密码?
- USB 令牌专为人们手动签署文档而设计。他们通常有特定的限制。例如:通常情况下,您每秒不能申请超过 1 个签名。这在网络环境中通常是不够的。在 Web 上下文中,您需要在服务器上安装硬件安全模块 (HSM)。
虽然理论上您的代码可以在服务器上运行,但我看到了很多原因,为什么在 client/server 环境中使用可在独立计算机上运行的代码并不是一个明智的决定。有太多的实际问题(例如身份验证、速度、错误的 iText 版本……)会使您的项目出错。我会回答 "no" 你的问题,该代码是否可以在 client/server 场景中工作。
更新:
在您对我的回答的评论中,您指出您的服务器是 Linux 服务器。很明显,使用 "Windows-MY" 永远不会在 Linux 服务器上工作。您必须使用 PKCS#11 而不是 Windows-MY 来与存储令牌的硬件设备通信。这是一个代码示例,适用于 SafeNet 的 Luna SA。如您所见,它使用 PKCS#11:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.security.pkcs11.SunPKCS11;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.log.LoggerFactory;
import com.itextpdf.text.log.SysoLogger;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.CertificateUtil;
import com.itextpdf.text.pdf.security.CrlClient;
import com.itextpdf.text.pdf.security.CrlClientOnline;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.OcspClient;
import com.itextpdf.text.pdf.security.OcspClientBouncyCastle;
import com.itextpdf.text.pdf.security.PrivateKeySignature;
import com.itextpdf.text.pdf.security.TSAClient;
import com.itextpdf.text.pdf.security.TSAClientBouncyCastle;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
public class C4_01_SignWithPKCS11HSM {
public static final String SRC = "/home/itext/hello.pdf";
public static final String PROPS = "/home/itext/key.properties";
public static final String DEST = "/home/itext/hello_hsm.pdf";
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, '[=10=]');
// 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);
}
public static void main(String[] args) throws IOException, GeneralSecurityException, DocumentException {
LoggerFactory.getInstance().setLogger(new SysoLogger());
Properties properties = new Properties();
properties.load(new FileInputStream(PROPS));
char[] pass = properties.getProperty("PASSWORD").toCharArray();
String pkcs11cfg = properties.getProperty("PKCS11CFG");
BouncyCastleProvider providerBC = new BouncyCastleProvider();
Security.addProvider(providerBC);
FileInputStream fis = new FileInputStream(pkcs11cfg);
Provider providerPKCS11 = new SunPKCS11(fis);
Security.addProvider(providerPKCS11);
KeyStore ks = KeyStore.getInstance("PKCS11");
ks.load(null, pass);
String alias = (String)ks.aliases().nextElement();
PrivateKey pk = (PrivateKey)ks.getKey(alias, pass);
Certificate[] chain = ks.getCertificateChain(alias);
OcspClient ocspClient = new OcspClientBouncyCastle();
TSAClient tsaClient = null;
for (int i = 0; i < chain.length; i++) {
X509Certificate cert = (X509Certificate)chain[i];
String tsaUrl = CertificateUtil.getTSAURL(cert);
if (tsaUrl != null) {
tsaClient = new TSAClientBouncyCastle(tsaUrl);
break;
}
}
List<CrlClient> crlList = new ArrayList<CrlClient>();
crlList.add(new CrlClientOnline(chain));
C4_01_SignWithPKCS11HSM app = new C4_01_SignWithPKCS11HSM();
app.sign(SRC, DEST, chain, pk, DigestAlgorithms.SHA256, providerPKCS11.getName(), CryptoStandard.CMS,
"HSM test", "Ghent", crlList, ocspClient, tsaClient, 0);
}
}
使用的配置文件的内容如下所示:
Name = Luna
library = /usr/lunasa/lib/libCryptoki2_64.so
slot = 1
请注意,so
可能在您的情况下位于另一个目录中,并且您的证书可能位于另一个插槽中。我还使用一个属性文件来存储证书的密码。显然我不会分享我的密码 ;-)
此示例使用 GlobalSign 证书在 GlobalSign 拥有的服务器上进行了测试。
我正在做一个项目,我需要使用基于 usb 的数字签名来签署 pdf。我在本地尝试了以下代码并能够签署 pdf。我的问题是天气以下代码将在基于 senerio 的客户端服务器中工作。
我的代码是:
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfSignatureAppearance;
import com.lowagie.text.pdf.PdfStamper;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.CRL;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import javax.servlet.RequestDispatcher;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.security.mscapi.SunMSCAPI;
public class Testing {
private static boolean resFlag;
public static void main (String args[])
{
try {
BouncyCastleProvider providerBC = new BouncyCastleProvider();
Security.addProvider(providerBC);
SunMSCAPI providerMSCAPI = new SunMSCAPI();
Security.addProvider(providerMSCAPI);
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null);
String alias = (String)ks.aliases().nextElement();
PrivateKey pk = (PrivateKey)ks.getKey(alias, null);
Certificate[] chain = ks.getCertificateChain(alias);
// //String e = request.getParameter("digiFile");
// KeyStore ks = KeyStore.getInstance("pkcs12");
// String f10 = CommonUtil.getRealPath();
// String str8 = f10 + "/DigiFiles/";
// //System.out.println("str8-->>>>>>>>" + str8 + e);
// ks.load(new FileInputStream("F:/DigiFiles/Anurag Goel.pfx"), "123".toCharArray());
//
//
// System.out.println("The actual path is " + str8);
// String alias = (String)ks.aliases().nextElement();
// PrivateKey key = (PrivateKey)ks.getKey(alias, "123".toCharArray());
// Certificate[] chain = ks.getCertificateChain(alias);
PdfReader reader = new PdfReader("F:/test.pdf");
FileOutputStream os = new FileOutputStream("F:/SampleOutPut61.pdf");
PdfStamper stamper = PdfStamper.createSignature(reader, os, '[=10=]',null,true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setCrypto(pk, chain, (CRL[])null, PdfSignatureAppearance.VERISIGN_SIGNED);
appearance.setReason("elicense project");
appearance.setLocation("Assam");
appearance.setVisibleSignature("hi");
stamper.close();
} catch (KeyStoreException var27) {
var27.printStackTrace();
resFlag = false;
} catch (NoSuchAlgorithmException var28) {
var28.printStackTrace();
resFlag = false;
} catch (CertificateException var29) {
var29.printStackTrace();
resFlag = false;
} catch (FileNotFoundException var30) {
var30.printStackTrace();
resFlag = false;
} catch (IOException var31) {
var31.printStackTrace();
resFlag = false;
} catch (UnrecoverableKeyException var32) {
var32.printStackTrace();
resFlag = false;
} catch (DocumentException var33) {
var33.printStackTrace();
resFlag = false;
} catch (Exception var34) {
var34.printStackTrace();
resFlag = false;
} finally {
RequestDispatcher rd;
}
}
}
请给我建议。谢谢大家
- 您使用了错误的 iText 版本,因此您创建的签名不是面向未来的(请阅读 this book 以找出您的代码有什么问题)。
- 你依赖的操作系统是Windows。你的服务器也是Windows服务器吗?如果它是 Linux 服务器,您的代码将无法工作。请咨询您的托管服务提供商,并询问您的托管服务提供商是否允许您在该服务器上拥有 USB 令牌(如果它不是专用服务器,他们很可能会拒绝)。
- 您正在使用 Windows-MY,这意味着您将身份验证委托给了操作系统。如果 USB 需要密码(通常需要),Windows 会打开一个对话框让您填写该密码。如果您将其部署在服务器上:每次有人请求签名时,您是否会安排人坐在该服务器旁边填写该密码?
- USB 令牌专为人们手动签署文档而设计。他们通常有特定的限制。例如:通常情况下,您每秒不能申请超过 1 个签名。这在网络环境中通常是不够的。在 Web 上下文中,您需要在服务器上安装硬件安全模块 (HSM)。
虽然理论上您的代码可以在服务器上运行,但我看到了很多原因,为什么在 client/server 环境中使用可在独立计算机上运行的代码并不是一个明智的决定。有太多的实际问题(例如身份验证、速度、错误的 iText 版本……)会使您的项目出错。我会回答 "no" 你的问题,该代码是否可以在 client/server 场景中工作。
更新:
在您对我的回答的评论中,您指出您的服务器是 Linux 服务器。很明显,使用 "Windows-MY" 永远不会在 Linux 服务器上工作。您必须使用 PKCS#11 而不是 Windows-MY 来与存储令牌的硬件设备通信。这是一个代码示例,适用于 SafeNet 的 Luna SA。如您所见,它使用 PKCS#11:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.security.pkcs11.SunPKCS11;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.log.LoggerFactory;
import com.itextpdf.text.log.SysoLogger;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.CertificateUtil;
import com.itextpdf.text.pdf.security.CrlClient;
import com.itextpdf.text.pdf.security.CrlClientOnline;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.OcspClient;
import com.itextpdf.text.pdf.security.OcspClientBouncyCastle;
import com.itextpdf.text.pdf.security.PrivateKeySignature;
import com.itextpdf.text.pdf.security.TSAClient;
import com.itextpdf.text.pdf.security.TSAClientBouncyCastle;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
public class C4_01_SignWithPKCS11HSM {
public static final String SRC = "/home/itext/hello.pdf";
public static final String PROPS = "/home/itext/key.properties";
public static final String DEST = "/home/itext/hello_hsm.pdf";
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, '[=10=]');
// 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);
}
public static void main(String[] args) throws IOException, GeneralSecurityException, DocumentException {
LoggerFactory.getInstance().setLogger(new SysoLogger());
Properties properties = new Properties();
properties.load(new FileInputStream(PROPS));
char[] pass = properties.getProperty("PASSWORD").toCharArray();
String pkcs11cfg = properties.getProperty("PKCS11CFG");
BouncyCastleProvider providerBC = new BouncyCastleProvider();
Security.addProvider(providerBC);
FileInputStream fis = new FileInputStream(pkcs11cfg);
Provider providerPKCS11 = new SunPKCS11(fis);
Security.addProvider(providerPKCS11);
KeyStore ks = KeyStore.getInstance("PKCS11");
ks.load(null, pass);
String alias = (String)ks.aliases().nextElement();
PrivateKey pk = (PrivateKey)ks.getKey(alias, pass);
Certificate[] chain = ks.getCertificateChain(alias);
OcspClient ocspClient = new OcspClientBouncyCastle();
TSAClient tsaClient = null;
for (int i = 0; i < chain.length; i++) {
X509Certificate cert = (X509Certificate)chain[i];
String tsaUrl = CertificateUtil.getTSAURL(cert);
if (tsaUrl != null) {
tsaClient = new TSAClientBouncyCastle(tsaUrl);
break;
}
}
List<CrlClient> crlList = new ArrayList<CrlClient>();
crlList.add(new CrlClientOnline(chain));
C4_01_SignWithPKCS11HSM app = new C4_01_SignWithPKCS11HSM();
app.sign(SRC, DEST, chain, pk, DigestAlgorithms.SHA256, providerPKCS11.getName(), CryptoStandard.CMS,
"HSM test", "Ghent", crlList, ocspClient, tsaClient, 0);
}
}
使用的配置文件的内容如下所示:
Name = Luna
library = /usr/lunasa/lib/libCryptoki2_64.so
slot = 1
请注意,so
可能在您的情况下位于另一个目录中,并且您的证书可能位于另一个插槽中。我还使用一个属性文件来存储证书的密码。显然我不会分享我的密码 ;-)
此示例使用 GlobalSign 证书在 GlobalSign 拥有的服务器上进行了测试。