Java applet使用智能卡实现数字签名

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java applet使用智能卡实现数字签名相关的知识,希望对你有一定的参考价值。

如何使用智能卡在浏览器中签名(任何文档或文本)。到目前为止我调查过的内容:

ActiveX - 仅限IE浏览器Silverlight - 根本无法访问证书,并且插件面临与Java相同的限制

浏览器特定扩展;例如,直到版本33的Firefox曾经有过window.crypto.signText,但现在已经没有了

安装在客户端上的本地应用程序 - 不容易安装,支持,开发和更新多个操作系统及其不同版本。

Web Cryptography - “只有基本的加密功能”,没有证书支持我用完了想法。欢迎并赞赏所有建议。

我尝试了一个java applet代码如下。主类:智能卡applet.java公共类SmartCardSignerApplet扩展Applet {

private static final String FILE_NAME_FIELD_PARAM = "fileNameField";
private static final String CERT_CHAIN_FIELD_PARAM = "certificationChainField";
private static final String SIGNATURE_FIELD_PARAM = "signatureField";
private static final String SIGN_BUTTON_CAPTION_PARAM = "signButtonCaption";

private static final String PKCS11_KEYSTORE_TYPE = "PKCS11";
private static final String X509_CERTIFICATE_TYPE = "X.509";
private static final String CERTIFICATION_CHAIN_ENCODING = "PkiPath";
private static final String DIGITAL_SIGNATURE_ALGORITHM_NAME = "SHA1withRSA";
private static final String SUN_PKCS11_PROVIDER_CLASS = "sun.security.pkcs11.SunPKCS11";

private Button mSignButton;   //initialises applet public void init() {
    String signButtonCaption = this.getParameter(SIGN_BUTTON_CAPTION_PARAM);
    mSignButton = new Button(signButtonCaption);
    mSignButton.setLocation(0, 0);
    Dimension appletSize = this.getSize();
    mSignButton.setSize(appletSize);
    mSignButton.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent e) {
            signSelectedFile();
        }
    });
    this.setLayout(null);
    this.add(mSignButton);
}    \   signing the file private void signSelectedFile() {
    try {
        // Get the file name to be signed from the form in the html document
        JSObject browserWindow = JSObject.getWindow(this);
        JSObject mainForm = (JSObject) browserWindow.eval("document.forms[0]");
        String fileNameFieldName = this.getParameter(FILE_NAME_FIELD_PARAM);
        JSObject fileNameField = (JSObject) mainForm.getMember(fileNameFieldName);
        String fileName = (String) fileNameField.getMember("value");

        // Perform the actual file signing
        CertificationChainAndSignatureBase64 signingResult = signFile(fileName);
        if (signingResult != null) {
            // Document  signed. Fill the certificate and signature fields
            String certChainFieldName = this.getParameter(CERT_CHAIN_FIELD_PARAM);
            JSObject certChainField = (JSObject) mainForm.getMember(certChainFieldName);
            certChainField.setMember("value", signingResult.mCertificationChain);
            String signatureFieldName = this.getParameter(SIGNATURE_FIELD_PARAM);
            JSObject signatureField = (JSObject) mainForm.getMember(signatureFieldName);
            signatureField.setMember("value", signingResult.mSignature);
        } else {
            // User canceled signing
        }
    }
    catch (DocumentSignException dse) {
        // Document signing failed. Display error message
        String errorMessage = dse.getMessage();
        JOptionPane.showMessageDialog(this, errorMessage);
    }
    catch (SecurityException se) {
        se.printStackTrace();
        JOptionPane.showMessageDialog(this,
            "Unable to access the local file system.
" +
            "This applet should be started with full security permissions.
" +
            "Please accept to trust this applet when the Java Plug-In ask you.");
    }
    catch (JSException jse) {
        jse.printStackTrace();
        JOptionPane.showMessageDialog(this,
            "Unable to access some of the fields of the
" +
            "HTML form. Please check the applet parameters.");
    }
    catch (Exception e) {
        e.printStackTrace();
        JOptionPane.showMessageDialog(this, "Unexpected error: " + e.getMessage());
    }
}

privateChainAndSignatureBase64 signFile(String aFileName)抛出DocumentSignException {

    // Load the file for signing
    byte[] documentToSign = null;
    try {
        documentToSign = readFileInByteArray(aFileName);
    } catch (IOException ioex) {
        String errorMessage = "Can not read the file for signing " + aFileName + ".";
        throw new DocumentSignException(errorMessage, ioex);
    }

    // Show a dialog for choosing PKCS#11 implementation library and smart card PIN
    PKCS11LibraryFileAndPINCodeDialog pkcs11Dialog =
        new PKCS11LibraryFileAndPINCodeDialog();
    boolean dialogConfirmed;
    try {
        dialogConfirmed = pkcs11Dialog.run();
    } finally {
        pkcs11Dialog.dispose();
    }

    if (dialogConfirmed) {
        String oldButtonLabel = mSignButton.getLabel();
        mSignButton.setLabel("Working...");
        mSignButton.setEnabled(false);
        try {
            String pkcs11LibraryFileName = pkcs11Dialog.getLibraryFileName();
            String pinCode = pkcs11Dialog.getSmartCardPINCode();

            // Do the actual signing of the document with the smart card
            CertificationChainAndSignatureBase64 signingResult =
                signDocument(documentToSign, pkcs11LibraryFileName, pinCode);
            return signingResult;
        } finally {
            mSignButton.setLabel(oldButtonLabel);
            mSignButton.setEnabled(true);
        }
    }
    else {
        return null;
    }
}

private CertificationChainAndSignatureBase64 signDocument(
    byte[] aDocumentToSign, String aPkcs11LibraryFileName, String aPinCode)
throws DocumentSignException {
    if (aPkcs11LibraryFileName.length() == 0) {
        String errorMessage = "It is mandatory to choose a PCKS#11 native " +
            "implementation library for for smart card (.dll or .so file)!";
        throw new DocumentSignException(errorMessage);
    }

    // Load the keystore from the smart card using the specified PIN code
    KeyStore userKeyStore = null;
    try {
        userKeyStore = loadKeyStoreFromSmartCard(aPkcs11LibraryFileName, aPinCode);
    } catch (Exception ex) {
        String errorMessage = "Can not read the keystore from the smart card.
" +
            "Possible reasons:
" +
            " - The smart card reader in not connected.
" +
            " - The smart card is not inserted.
" +
            " - The PKCS#11 implementation library is invalid.
" +
            " - The PIN for the smart card is incorrect.
" +
            "Problem details: " + ex.getMessage();
        throw new DocumentSignException(errorMessage, ex);
    }

    // Get the private key and its certification chain from the keystore
    PrivateKeyAndCertChain privateKeyAndCertChain = null;
    try {
        privateKeyAndCertChain =
            getPrivateKeyAndCertChain(userKeyStore);
    } catch (GeneralSecurityException gsex) {
        String errorMessage = "Can not extract the private key and " +
            "certificate from the smart card. Reason: " + gsex.getMessage();
        throw new DocumentSignException(errorMessage, gsex);
    }

    // Check if the private key is available
    PrivateKey privateKey = privateKeyAndCertChain.mPrivateKey;
    if (privateKey == null) {
        String errorMessage = "Can not find the private key on the smart card.";
        throw new DocumentSignException(errorMessage);
    }

    // Check if X.509 certification chain is available
    Certificate[] certChain = privateKeyAndCertChain.mCertificationChain;
    if (certChain == null) {
        String errorMessage = "Can not find the certificate on the smart card.";
        throw new DocumentSignException(errorMessage);
    }

    // Create the result object
    CertificationChainAndSignatureBase64 signingResult =
        new CertificationChainAndSignatureBase64();

    // Save X.509 certification chain in the result encoded in Base64
    try {
        signingResult.mCertificationChain = encodeX509CertChainToBase64(certChain);
    }
    catch (CertificateException cee) {
        String errorMessage = "Invalid certificate on the smart card.";
        throw new DocumentSignException(errorMessage);
    }

    // Calculate the digital signature of the file,
    // encode it in Base64 and save it in the result
    try {
        byte[] digitalSignature = signDocument(aDocumentToSign, privateKey);
        signingResult.mSignature = Base64Utils.base64Encode(digitalSignature);
    } catch (GeneralSecurityException gsex) {
        String errorMessage = "File signing failed.
" +
            "Problem details: " + gsex.getMessage();
        throw new DocumentSignException(errorMessage, gsex);
    }

    return signingResult;
}

/**
 * Loads the keystore from the smart card using its PKCS#11 implementation
 * library and the Sun PKCS#11 security provider. The PIN code for accessing
 * the smart card is required.
 */
private KeyStore loadKeyStoreFromSmartCard(String aPKCS11LibraryFileName,
    String aSmartCardPIN)
throws GeneralSecurityException, IOException {
    // First configure the Sun PKCS#11 provider. It requires a stream (or file)
    // containing the configuration parameters - "name" and "library".
    String pkcs11ConfigSettings =
        "name = SmartCard
" + "library = " + aPKCS11LibraryFileName;
    byte[] pkcs11ConfigBytes = pkcs11ConfigSettings.getBytes();
    ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11ConfigBytes);

    // Instantiate the provider dynamically with Java reflection
    try {
        Class sunPkcs11Class = Class.forName(SUN_PKCS11_PROVIDER_CLASS);
        Constructor pkcs11Constr = sunPkcs11Class.getConstructor(
            java.io.InputStream.class);
        Provider pkcs11Provider = (Provider) pkcs11Constr.newInstance(confStream);
        Security.addProvider(pkcs11Provider);
    } catch (Exception e) {
        throw new KeyStoreException("Can initialize Sun PKCS#11 security " +
            "provider. Reason: " + e.getCause().getMessage());
    }

    // Read the keystore form the smart card
    char[] pin = aSmartCardPIN.toCharArray();
    KeyStore keyStore = KeyStore.getInstance(PKCS11_KEYSTORE_TYPE);
    keyStore.load(null, pin);
    return keyStore;
}

/**
 * @return private key and certification chain corresponding to it, extracted from
 * given keystore. The keystore is considered to have only one entry that contains
 * both certification chain and its corresponding private key. If the keystore has
 * no entries, an exception is thrown.
 */
private PrivateKeyAndCertChain getPrivateKeyAndCertChain(
    KeyStore aKeyStore)
throws GeneralSecurityException {
    Enumeration aliasesEnum = aKeyStore.aliases();
    if (aliasesEnum.hasMoreElements()) {
        String alias = (String)aliasesEnum.nextElement();
        Certificate[] certificationChain = aKeyStore.getCertificateChain(alias);
        PrivateKey privateKey = (PrivateKey) aKeyStore.getKey(alias, null);
        PrivateKeyAndCertChain result = new PrivateKeyAndCertChain();
        result.mPrivateKey = privateKey;
        result.mCertificationChain = certificationChain;
        return result;
    } else {
        throw new KeyStoreException("The keystore is empty!");
    }
}

/**
 * @return Base64-encoded ASN.1 DER representation of given X.509 certification
 * chain.
 */
private String encodeX509CertChainToBase64(Certificate[] aCertificationChain)
throws CertificateException {
    List certList = Arrays.asList(aCertificationChain);
    CertificateFactory certFactory =
        CertificateFactory.getInstance(X509_CERTIFICATE_TYPE);
    CertPath certPath = certFactory.generateCertPath(certList);
    byte[] certPathEncoded = certPath.getEncoded(CERTIFICATION_CHAIN_ENCODING);
    String base64encodedCertChain = Base64Utils.base64Encode(certPathEncoded);
    return base64encodedCertChain;
}

/**
 * Reads the specified file into a byte array.
 */
private byte[] readFileInByteArray(String aFileName)
throws IOException {
    File file = new File(aFileName);
    FileInputStream fileStream = new FileInputStream(file);
    try {
        int fileSize = (int) file.length();
        byte[] data = new byte[fileSize];
        int bytesRead = 0;
        while (bytesRead < fileSize) {
            bytesRead += fileStream.read(data, bytesRead, fileSize-bytesRead);
        }
        return data;
    }
    finally {
        fileStream.close();
    }
}

/**
 * Signs given document with a given private key.
 */
private byte[] signDocument(byte[] aDocument, PrivateKey aPrivateKey)
throws GeneralSecurityException {
    Signature signatureAlgorithm =
        Signature.getInstance(DIGITAL_SIGNATURE_ALGORITHM_NAME);
    signatureAlgorithm.initSign(aPrivateKey);
    signatureAlgorithm.update(aDocument);
    byte[] digitalSignature = signatureAlgorithm.sign();
    return digitalSignature;
}

/**
 * Data structure that holds a pair of private key and
 * certification chain corresponding to this private key.
 */
static class PrivateKeyAndCertChain {
    public PrivateKey mPrivateKey;
    public Certificate[] mCertificationChain;
}

/**
 * Data structure that holds a pair of Base64-encoded
 * certification chain and digital signature.
 */
static class CertificationChainAndSignatureBase64 {
    public String mCertificationChain = null;
    public String mSignature = null;
}

/**
 * Exception class used for document signing errors.
 */
static class DocumentSignException extends Exception {
    public DocumentSignException(String aMessage) {
        super(aMessage);
    }

    public DocumentSignException(String aMessage, Throwable aCause) {
        super(aMessage, aCause);
    }
}

当我运行applet时,我收到一条消息,找不到ckr操作。任何帮助?

答案

较旧的方法,如Java applet,Active X等,这些方法将被淘汰或正在逐步淘汰新的Modern Browser产品。最近有很多关于WebCrypto API的讨论,但截至目前,WebCrypto API不提供对(Windows)或任何其他Key存储或本地加密USB /智能卡设备的访问。

请参阅我的回答发布User Authentication from Browser using Digital Signature Certificate on USB Token or Smart Card

以上帖子还有用于签署pdf的javascript代码。

对于文件或返回签署How to Digitally Sign GST Return or eReturn using JavaScript form Browser and USB Token of user? Can I use WebCrypto API?

以上是关于Java applet使用智能卡实现数字签名的主要内容,如果未能解决你的问题,请参考以下文章

Java Applet实现五子棋游戏

java卡的简介

如何在我的 Java Applet 上运行图像而无需签名?

使用智能卡或证书在浏览器中对数据进行数字签名

Java AI 实现人工智能- 我在Github上发现的-基于Java的计算机视觉 Java实现人脸识别(开源代码-(人脸识别-自动驾驶-汽车追踪-手写数字识别器))带你导入代码并测试使用

Java AI 实现人工智能- 我在Github上发现的-基于Java的计算机视觉 Java实现人脸识别(开源代码-(人脸识别-自动驾驶-汽车追踪-手写数字识别器))带你导入代码并测试使用