在服务器客户端条件下使用USB驱动程序签名PDF

时间:2015-02-07 11:16:57

标签: java pdf ssl itext

我正在做一个项目,我需要使用基于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, '\0',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;


      }

   }
}

请给我一些建议。谢谢所有

1 个答案:

答案 0 :(得分:2)

  1. 您使用的是错误的iText版本,因此您创建的签名不是未来证明(请阅读this book以了解您的代码有什么问题。)
  2. 您取决于操作系统是Windows的事实。您的服务器也是Windows服务器吗?如果它是Linux服务器,您的代码将无法运行。请咨询您的托管服务提供商,并询问您的托管服务提供商是否允许您在该服务器上安装USB令牌(如果它不是专用服务器,他们可能会拒绝该服务器)。
  3. 您正在使用Windows-MY,这意味着您将身份验证委派给操作系统。如果USB需要密码(他们通常这样做),Windows将打开一个对话框供您填写该密码。如果您在服务器上部署它:每次有人请求签名时,您是否会有人坐在该服务器旁边填写该密码?
  4. USB令牌专为人们手动签署文档而设计。他们通常有特定的限制。例如:通常,您每秒不能应用超过1个签名。这在网络环境中通常是不够的。在Web上下文中,您需要在服务器上安装硬件安全模块(HSM)。
  5. 虽然理论上代码可能在服务器上运行,但我发现很多理由说明在客户端/服务器环境中使用适用于独立计算机的代码并不是明智的决定。有太多实际问题(例如身份验证,速度,iText的错误版本......)会导致您的项目出错。我会回答你的问题是“否”,该代码是否适用于客户端/服务器场景。

    <强>更新

    在您对我的回答的评论中,您表明您的服务器是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, '\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);
        }
    
        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证书进行了测试。