Java за цифрово подписване на документи в уеб


Java аплет за подписване със смарт карта



страница11/14
Дата18.09.2016
Размер1.95 Mb.
#10052
1   ...   6   7   8   9   10   11   12   13   14

3.3.Java аплет за подписване със смарт карта


В предната част видяхме как можем да реализираме аплет, който подписва документи със сертификат, намиращ се в PKCS#12 хранилище (PFX файл). Нека сега разгледаме имплементацията на аплета SmartCardSignerApplet за подписване на документи със смарт карта.

Системни изисквания за Java аплета за подписване със смарт карта


Java аплетът за подписване със смарт карта изисква инсталиран Java Plug-In
версия 1.5 или по-нова на машината на клиента. Това е необходимо, защото аплетът използва Sun PKCS#11 Provider, който се появява стан­дартно в Java от версия 1.5 нататък.

Имплементация на аплета за подписване със смарт карта


Подписването със смарт карта не се различава много от подписването с PFX файл. Всъщност, разликата е само в начина на инстанциране на хранилището за ключове и сертификати. При работа с PKCS#12 хранили­щето се зарежда от PFX файл, а при работа със смарт карта, хранилището се зарежда от картата чрез интерфейса PKCS#11. Другата разлика е, че вместо парола за достъп се изисква PIN кода за картата. Всичко останало е еднакво – от зареждането на серти­фиката, до подписването на файла.

Да разгледаме сорс кода на аплета. Класът SmartCardSignerApplet реали­зира основната му функционалност:



SmartCardSignerApplet.java

import java.applet.Applet;

import java.awt.*;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.*;

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.ByteArrayInputStream;

import java.util.Arrays;

import java.util.Enumeration;

import java.util.List;

import java.security.*;

import java.security.cert.CertPath;

import java.security.cert.Certificate;

import java.security.cert.CertificateException;

import java.security.cert.CertificateFactory;

import java.lang.reflect.Constructor;

import netscape.javascript.JSException;

import netscape.javascript.JSObject;

/**

* Applet for digital signing documents with a smart card. The applet is intended to

* be placed in a HTML document containing a single HTML form that is used for applet

* input/output. The applet accepts several parameters - the name of the field in the

* HTML form that contains the file name to be signed and the names of the fields in

* the HTML form, where the certification chain and signature should be stored.

*

* If the signing process is sucecssfull, the signature and certification chain

* fields in the HTML form are filled. Otherwise an error message explaining the

* failure reason is shown.

*

* The applet asks the user to locate in his local file system the PKCS#11

* implementation library that is part of software that come with the smart card

* and the smart card reader. Usually this is a Windows .DLL file located in Windows

* system32 directory or .so library (e.g. C:\windows\system32\pkcs201n.dll).

*

* The applet also asks the user to enter his PIN code for accessing the smart card.

* If the smart card contains a certificate and a corresponding private key, the

* signature of the file is calculated and is placed in the HTML form. In addition

* to the calculated signature the certificate with its full certification chain is

* extracted from the smart card and is placed in the HTML form too. The digital

* signature is placed as Base64-encoded sequence of bytes. The certification chain

* is placed as ASN.1 DER-encoded sequence of bytes, additionally encoded in Base64.

* In case the smart card contains only one certificate without its full

* certification chain, a chain consisting of this single certificate is extracted

* and stored in the HTML form instead of a full certification chain.

*

* Digital singature algorithm used is SHA1withRSA. The length of the calculated

* singature depends on the length of the private key on the smart card.

*

* The applet should be able to access the local machine's file system for reading

* and writing. Reading the local file system is required for the applet to access

* the file that should be signed. Writing the local file system is required for

* the applet to save its settings in the user's home directory. Accessing the local

* file system is not possible by default, but if the applet is digitally signed

* (with jarsigner), it runs with no security restrictions and can do anything.

* This applet should be signed in order to run.

*

* Java Plug-In version 1.5 or hihger is required for accessing the PKCS#11 smart

* card functionality, so the applet will not run in any other Java runtime

* environment.

*

* This file is part of NakovDocumentSigner digital document

* signing framework for Java-based Web applications:

* http://www.nakov.com/documents-signing/

*

* Copyright (c) 2005 by Svetlin Nakov - http://www.nakov.com

* All rights reserved. This code is freeware. It can be used

* for any purpose as long as this copyright statement is not

* removed or modified.

*/

public class SmartCardSignerApplet extends 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;

/**

* Initializes the applet - creates and initializes its graphical user interface.

* Actually the applet consists of a single button, that fills its all surface.

* The button's caption is taken from the applet param SIGN_BUTTON_CAPTION_PARAM.

*/

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);

}

/**



* Signs the selected file. The file name comes from a field in the HTML

* document. The result consists of the calculated digital signature and

* certification chain, both placed in fields in the HTML document, encoded

* in Base64 format. The HTML document should contain only one HTML form.

* The name of the field, that contains the name of the file to be signed

* is obtained from FILE_NAME_FIELD_PARAM applet parameter. The names of the

* output fields for the signature and the certification chain are obtained

* from the parameters CERT_CHAIN_FIELD_PARAM and SIGNATURE_FIELD_PARAM. The

* user is asket to choose a PKCS#11 implementation library and a PIN code

* for accessing the smart card.

*/

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.\n" +

"This applet should be started with full security permissions.\n" +

"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\n" +

"HTML form. Please check the applet parameters.");

}

catch (Exception e) {

e.printStackTrace();

JOptionPane.showMessageDialog(this, "Unexpected error: "+e.getMessage());

}

}

/**



* Signs given local file. The certificate and private key to be used for signing

* come from the locally attached smart card. The user is requested to provide a

* PKCS#11 implementation library and the PIN code for accessing the smart card.

* @param aFileName the name of the file to be signed.

* @return the digital signature of the given file and the certification chain of

* the certificatie used for signing the file, both Base64-encoded or null if the

* signing process is canceled by the user.

* @throws DocumentSignException when a problem arised during the singing process

* (e.g. smart card access problem, invalid certificate, invalid PIN code, etc.)

*/

private CertificationChainAndSignatureBase64 signFile(String aFileName)

throws DocumentSignException {

// Load the file for signing

byte[] documentToSign = null;

try {

documentToSign = readFileInByteArray(aFileName);

} catch (IOException ioex) {

String errorMsg = "Can not read the file for signing " + aFileName + ".";



throw new DocumentSignException(errorMsg, ioex);

}

// Show a dialog for choosing PKCS#11 library and smart card PIN code

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." +

"\nPossible reasons:\n" +

" - The smart card reader in not connected.\n" +

" - The smart card is not inserted.\n" +

" - The PKCS#11 implementation library is invalid.\n" +

" - The PIN for the smart card is incorrect.\n" +

"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.\n" +



"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\n" + "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 pkcs11Con = sunPkcs11Class.getConstructor(

java.io.InputStream.class);

Provider pkcs11Provider = (Provider) pkcs11Con.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);

}

}



}

Основният клас на аплета използва още един допълнителен клас PKCS11LibraryFileAndPINCodeDialog, който дава възможност на потреби­теля да избере библиотека-имплементация на PKCS#11 и PIN код за достъп до смарт картата:

PKCS11LibraryFileAndPINCodeDialog.java

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import javax.swing.filechooser.FileFilter;

import java.io.*;

import java.util.Properties;

/**

* Dialog for choosing PKCS#11 implementation library file and PIN code for accessing

* the smart card. Allows the user to choose a PKCS#11 library file (.dll / .so) and

* enter a PIN code for the smart card. The last used library file name is remembered

* in the config file called ".smart_card_signer_applet.config" located in the user's

* home directory in order to be automatically shown the next time when the same user

* accesses this dialog.

*

* This file is part of NakovDocumentSigner digital document

* signing framework for Java-based Web applications:

* http://www.nakov.com/documents-signing/

*

* Copyright (c) 2005 by Svetlin Nakov - http://www.nakov.com

* All rights reserved. This code is freeware. It can be used

* for any purpose as long as this copyright statement is not

* removed or modified.

*/

public class PKCS11LibraryFileAndPINCodeDialog extends JDialog {

private static final String CONFIG_FILE_NAME=".smart_card_signer_applet.config";

private static final String PKCS11_LIBRARY_FILE_NAME_KEY="last-PKCS11-file-name";

private JButton mBrowseForLibraryFileButton = new JButton();

private JTextField mLibraryFileNameTextField = new JTextField();

private JLabel mChooseLibraryFileLabel = new JLabel();

private JTextField mPINCodeTextField = new JPasswordField();

private JLabel mEnterPINCodeLabel = new JLabel();

private JButton mSignButton = new JButton();

private JButton mCancelButton = new JButton();

private boolean mResult = false;

/**

* Initializes the dialog - creates and initializes its GUI controls.

*/

public PKCS11LibraryFileAndPINCodeDialog() {

// Initialize the dialog

this.getContentPane().setLayout(null);

this.setSize(new Dimension(426, 165));

this.setBackground(SystemColor.control);

this.setTitle("Select PKCS#11 library file and smart card PIN code");

this.setResizable(false);

// Center the dialog in the screen

Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

Dimension dialogSize = this.getSize();

int centerPosX = (screenSize.width - dialogSize.width) / 2;

int centerPosY = (screenSize.height - dialogSize.height) / 2;

setLocation(centerPosX, centerPosY);



// Initialize certificate keystore file label

mChooseLibraryFileLabel.setText(



"Please select your PKCS#11 implementation library file (.dll / .so) :");

mChooseLibraryFileLabel.setBounds(new Rectangle(10, 5, 400, 15));

mChooseLibraryFileLabel.setFont(new Font("Dialog", 0, 12));

// Initialize certificate keystore file name text field

mLibraryFileNameTextField.setBounds(new Rectangle(10, 25, 315, 20));

mLibraryFileNameTextField.setFont(new Font("DialogInput", 0, 12));

mLibraryFileNameTextField.setEditable(false);

mLibraryFileNameTextField.setBackground(SystemColor.control);

// Initialize browse button

mBrowseForLibraryFileButton.setText("Browse");

mBrowseForLibraryFileButton.setBounds(new Rectangle(330, 25, 80, 20));

mBrowseForLibraryFileButton.addActionListener(new ActionListener() {



public void actionPerformed(ActionEvent e) {

browseForLibraryButton_actionPerformed();

}

});


// Initialize PIN code label

mEnterPINCodeLabel.setText("Enter the PIN code to access your smart card:");

mEnterPINCodeLabel.setBounds(new Rectangle(10, 55, 350, 15));

mEnterPINCodeLabel.setFont(new Font("Dialog", 0, 12));



// Initialize PIN code text field

mPINCodeTextField.setBounds(new Rectangle(10, 75, 400, 20));

mPINCodeTextField.setFont(new Font("DialogInput", 0, 12));

// Initialize sign button

mSignButton.setText("Sign");

mSignButton.setBounds(new Rectangle(110, 105, 75, 25));

mSignButton.addActionListener(new ActionListener() {



public void actionPerformed(ActionEvent e) {

signButton_actionPerformed();

}

});


// Initialize cancel button

mCancelButton.setText("Cancel");

mCancelButton.setBounds(new Rectangle(220, 105, 75, 25));

mCancelButton.addActionListener(new ActionListener() {



public void actionPerformed(ActionEvent e) {

cancelButton_actionPerformed();

}

});


// Add the initialized components into the dialog's content pane

this.getContentPane().add(mChooseLibraryFileLabel, null);

this.getContentPane().add(mLibraryFileNameTextField, null);

this.getContentPane().add(mBrowseForLibraryFileButton, null);

this.getContentPane().add(mEnterPINCodeLabel, null);

this.getContentPane().add(mPINCodeTextField, null);

this.getContentPane().add(mSignButton, null);

this.getContentPane().add(mCancelButton, null);

this.getRootPane().setDefaultButton(mSignButton);

// Add some functionality for focusing the most appropriate

// control when the dialog is shown

this.addWindowListener(new WindowAdapter() {

public void windowOpened(WindowEvent windowEvent) {

String libraryFileName = mLibraryFileNameTextField.getText();



if (libraryFileName != null && libraryFileName.length() != 0)

mPINCodeTextField.requestFocus();



else

mBrowseForLibraryFileButton.requestFocus();

}

});


}

/**

* Called when the "Browse" button is pressed.

* Shows file choose dialog and allows the user to locate a library file.

*/

private void browseForLibraryButton_actionPerformed() {

JFileChooser fileChooser = new JFileChooser();

LibraryFileFilter libraryFileFilter = new LibraryFileFilter();

fileChooser.addChoosableFileFilter(libraryFileFilter);

String libraryFileName = mLibraryFileNameTextField.getText();

File directory = new File(libraryFileName).getParentFile();

fileChooser.setCurrentDirectory(directory);

if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {

String selectedLibFile = fileChooser.getSelectedFile().getAbsolutePath();

mLibraryFileNameTextField.setText(selectedLibFile);

}

}



/**

* Called when the sign button is pressed. Closses the dialog and sets the result

* flag to true to indicate that the user is confirmed the information entered in

* the dialog.

*/

private void signButton_actionPerformed() {

mResult = true;



this.setVisible(false);

}

/**



* Called when the cancel button is pressed. Closses the dialog and sets the

* result flag to false that indicates that the dialog is canceled.

*/

private void cancelButton_actionPerformed() {

mResult = false;



this.setVisible(false);

}

/**



* @return the file name with full path to it where the dialog settings are

* stored.

*/

private String getConfigFileName() {

String configFileName = System.getProperty("user.home") +

System.getProperty("file.separator") + CONFIG_FILE_NAME;

return configFileName;

}

/**



* Loads the dialog settings from the dialog configuration file. These settings

* consist of a single value - the last used library file name with its path.

*/

private void loadSettings()

throws IOException {

String configFileName = getConfigFileName();

FileInputStream configFileStream = new FileInputStream(configFileName);

try {

Properties configProps = new Properties();

configProps.load(configFileStream);

// Apply setings from the config file

String lastLibraryFileName =

configProps.getProperty(PKCS11_LIBRARY_FILE_NAME_KEY);

if (lastLibraryFileName != null)

mLibraryFileNameTextField.setText(lastLibraryFileName);



else

mLibraryFileNameTextField.setText("");

} finally {

configFileStream.close();

}

}

/**



* Saves the dialog settings to the dialog configuration file. These settings

* consist of a single value - the last used library file name with its path.

*/

private void saveSettings()

throws IOException {

// Create a list of settings to store in the config file

Properties configProps = new Properties();

String currentLibraryFileName = mLibraryFileNameTextField.getText();

configProps.setProperty(PKCS11_LIBRARY_FILE_NAME_KEY,currentLibraryFileName);



// Save the settings in the config file

String configFileName = getConfigFileName();

FileOutputStream configFileStream = new FileOutputStream(configFileName);

try {

configProps.store(configFileStream, "");

} finally {

configFileStream.close();

}

}

/**



* @return the library file selected by the user.

*/

public String getLibraryFileName() {

String libraryFileName = mLibraryFileNameTextField.getText();



return libraryFileName;

}

/**



* @return the PIN code entered by the user.

*/

public String getSmartCardPINCode() {

String pinCode = mPINCodeTextField.getText();



return pinCode;

}

/**



* Shows the dialog and allow the user to choose library file and enter a PIN.

* @return true if the user click sign button or false if the user cancel the

* dialog.

*/

public boolean run() {

try {

loadSettings();

} catch (IOException ioex) {

// Loading settings failed. Default settings will be used.

}

setModal(true);



this.setVisible(true);

try {

if (mResult) {

saveSettings();

}

} catch (IOException ioex) {



// Saving settings failed. Can not handle this problem.

}

return mResult;

}

/**

* File filter class, intended to accept only .dll and .so files.

*/

private static class LibraryFileFilter extends FileFilter {

public boolean accept(File aFile) {

if (aFile.isDirectory()) {

return true;

}

String fileName = aFile.getName().toLowerCase();



boolean accepted =

(fileName.endsWith(".dll") || fileName.endsWith(".so"));



return accepted;

}

public String getDescription() {



return "PKCS#11 v2.0 ot later implementation library (.dll, .so)";

}

}



}

Аплетът използва и класа Base64Utils, който е същият като в аплета за подписване с PFX файл.

Как работи аплетът за подписване със смарт карта?


Аплетът за подписване на документи със смарт карта в уеб браузъра на потребителя работи по абсолютно същия начин като аплета за подписване с PFX файл, който вече разгледахме в детайли.

Първоначално чрез класа netscape.javascript.JSObject от HTML формата се извлича името на файла за подписване и този файл се зарежда в паметта. Това е възможно, защото аплетът е подписан и има достъп до локалната файлова система.

След това на потребителя се показва диалогът за избор на PKCS#11 биб­лиотека и PIN код за достъп до смарт картата. След като той посочи библиотеката с PKCS#11 имплементацията и си въведе PIN кода, от смарт картата се извличат сертификатът заедно със сертификационната му верига (ако е начина) и интерфейс за достъп до личния ключ от картата.

Сертифика­ционната верига се кодира в ASN.1 DER формат и се записва в текстов вид (с BASE64 кодиране) в съответното текстово поле на HTML формата. За достъп до уеб браузъра отново се използва класа JSObject.

Прочетеният в паметта файл се подписва с личния ключ от смарт картата и получената цифрова сигнатура се кодира в текстов вид с Base64 кодиране и се записва в поле от HTML формата.

Ако възникване грешка на някоя от описаните стъпки, на потребителя се показва подходящо съобщение за грешка. Грешка може да възникне при много ситуации – при невъзможност да бъде прочетен файлът за подпис­ване, при невалидна библиотека с PKCS#11 имплементация, при невали­ден PIN код, при липса на сертификат или личен ключ в смарт картата, поради невъзмож­ност за достъп до HTML формата и в още много други ситуации.

Графичният потребителски интерфейс на аплета е базиран на библиоте­ките AWT и JFC/Swing, които се доставят стандартно с JDK 1.5.

Диалогът за избор на PKCS#11 имплементация и PIN код дава възможност за избор само измежду .dll и .so файлове (Windows и Linux библиотеки). Използваната за последен път PKCS#11 библиотека се запомня заедно с пълния път до нея в конфигурационния файл на аплета, намиращ се личната директо­рия на потребителя (user home directory), за да бъде използван при след­ващо показване на същия диалог.

За достъп до смарт картата се използва Sun PKCS#11 Provider, който се инстанцира с Java reflection и се конфигурира динамично. Класът sun. security.pkcs11.SunPKCS11 се инстанцира с reflection (отражение на типовете), за да не се свързва статично с аплета. Ако класът е статично свързан с аплета и този клас липсва (например при версии на Java Plug-In по-малки от 1.5), аплетът изобщо няма да се зареди. Инстанцирането с reflection има предимството, че аплетът ще се зареди и ще хвърли изключение едва при опит за инстанциране на липсващия клас. Тогава на потребителя ще бъде показано подходящо съобщение за грешка.

След като е регистриран PKCS#11 доставчикът, достъпът до хранилището на смарт картата става чрез класа java.security.KeyStore. Аплетът очаква хранилището върху смарт картата да съдържа само един запис (alias), в който са записани сертификатът на потребителя (евентуално заедно със сертификационната му верига) и съответния му личен ключ.

Реализацията на всички използвани криптографски алгоритми, се осигу­рява от доставчика на криптографски услуги по подразбиране SunJSSE, който също е част от JDK 1.5.

За подписването на файлове се използва алгоритъмът за цифрови подписи SHA1withRSA, който е достъпен стандартно от Java посредством класа java.security.Signature и се поддържа от повечето смарт карти.


Компилиране и подписване на аплета


За да работи правилно аплетът, е необходимо той да бъде подписан. Можем да използваме следния скрипт за да си генерираме саморъчно-подписан сертификат, с който да подпишем след това аплета:

generate-certificate.bat

del SmartCardSignerApplet.jks

keytool -genkey -alias signFiles -keystore SmartCardSignerApplet.jks -keypass !secret -dname "CN=Your Company" -storepass !secret

pause


Резултатът от изпълнението на скрипта е хранилището за ключове и сертификати SmartCardSignerApplet.jks, съдържащо генерирания серти­фикат и съответния му личен ключ, записани под име “signFiles”, достъпни с парола “!secret”. Форматът на изходния файл е JKS (Java KeyStore), който се използва по подразбиране от инструмента keytool.

За компилирането на сорс-кода на аплета, получаването на JAR архив и подписването на този архив можем да използваме следния скрипт:



build-script.bat

set JAVA5_HOME=C:\Progra~1\Java\jdk1.5.0_04
del *.class

%JAVA5_HOME%\bin\javac -classpath .;"%JAVA5_HOME%\jre\lib\plugin.jar" *.java


del *.jar

%JAVA5_HOME%\bin\jar -cvf SmartCardSignerApplet.jar *.class


%JAVA5_HOME%\bin\jarsigner -keystore SmartCardSignerApplet.jks -storepass !secret -keypass !secret SmartCardSignerApplet.jar signFiles
pause

Посочената последователност от команди изтрива всички компилирани .class файлове, компилира всички .java файлове, които съставят аплета, пакетира получените .class файлове в архив SmartCardSignerApplet.jar и подписва този архив с генерирания преди това саморъчно-подписан сертификат (намиращ се в хранилището SmartCardSignerApplet.jks).

За компилацията е необходим JDK 1.5 или по-нова версия.


Тестване на аплета с примерна HTML форма


Подписаният аплет можем да тестваме с примерен HTML документ, който съдържа подходяща HTML форма:

TestSmartCardSignerApplet.html

<html>

<head>

<title>Test Smart Card Signer Applettitle>

head>

<body>

<form name="mainForm" method="post" action="FileUploadServlet">

Choose file to upload and sign:



<input type="file" name="fileToBeSigned">

<br>

Certification chain:



<input type="text" name="certificationChain">

<br>

Signature:



<input type="text" name="signature">

form>

<object

classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"

codebase="http://java.sun.com/update/1.5.0/jinstall-1_5-windows-i586.cab#Version=5,0,0,5"

width="130" height="25" name="SmartCardSignerApplet">

<param name="code" value="SmartCardSignerApplet">

<param name="archive" value="SmartCardSignerApplet.jar">

<param name="mayscript" value="true">

<param name="type" value="application/x-java-applet;version=1.5">

<param name="scriptable" value="false">

<param name="fileNameField" value="fileToBeSigned">

<param name="certificationChainField" value="certificationChain">

<param name="signatureField" value="signature">

<param name="signButtonCaption" value="Sign selected file">

<comment>

<embed

type="application/x-java-applet;version=1.5"

code="SmartCardSignerApplet" archive="SmartCardSignerApplet.jar"

width="130" height="25" scriptable="true"

pluginspage="http://java.sun.com/products/plugin/index.html#download"

fileNameField="fileToBeSigned"

certificationChainField="certificationChain"

signatureField="signature"

signButtonCaption="Sign selected file">

embed>

<noembed>

Smart card signing applet can not be started because

Java Plugin 1.5 or newer is not installed.

noembed>

comment>

object>

body>

html>


Важно е да използваме таговете <object> и <embed> вместо остарелия таг , защото при него няма начин да укажем на уеб браузъра, че аплетът изисква JDK версия 1.5 или по-висока.

Аплетът за подписване в уеб среда в действие


При отваряне на тестовият HTML документ първо се проверява дали на машината има инсталиран Java Plug-In
1.5 или по-висока версия и ако няма потребителят се препраща автоматично към сайта, от който тя може да бъде изтеглена.

Ако на машината е инсталиран Java Plug-In 1.5, при зареждане на тестовия HTML документ се появява диалог, който иска съгласие от потребителя за да бъде изпълнен аплетът с пълни права (фигура 4-3). Ако потреби­телят се съгласи, аплетът стартира нормално.





Фигура 4 20. Java Plug-In 1.5 иска съгласие за изпълнение на подписан аплет

Примерната HTML страница съдържа HTML форма с три полета, аплет за подписване и бутон за активиране на подписването, който всъщност се намира вътре в аплета. Трите полета във формата служат съответно за избор на файл за изпра­щане, за записване на сертифика­цион­ната верига, използвана при подпис­ването и за записване на получената сигнатура.

При натискане на бутона за подписване на потребителя се дава възмож­ност да избере библиотека-имплементация на PKCS#11 и PIN код за достъп до смарт картата (фигура 4-4):



Фигура 4 21. Подписване със смарт карта в уеб среда

При успешно подписване изчислената сигнатура и сертификатът, извле­чени от смарт картата, се записват в HTML формата (фигура 4-5):





Фигура 4 22. HTML форма с подписан файл в нея

Каталог: books -> signatures
books -> В обятията на шамбала
books -> Книга се посвещава с благодарност на децата ми. Майка ми и жена ми ме научиха да бъда мъж
books -> Николай Слатински “Надеждата като лабиринт” София, Издателство “виденов & син”, 1993 год
books -> София, Издателство “Българска книжница”, 2004 год. Рецензенти доц д. ик н. Димитър Йончев, проф д-р Нина Дюлгерова Научен редактор проф д-р Петър Иванов
books -> Николай Слатински “Измерения на сигурността” София, Издателство “Парадигма”, 2000 год
books -> Книга 2 щастие и успех предисловие
books -> Превръщане на числа от една бройна система в друга
books -> Тантриското преобразяване
signatures -> Java за цифрово подписване на документи в уеб


Сподели с приятели:
1   ...   6   7   8   9   10   11   12   13   14




©obuch.info 2024
отнасят до администрацията

    Начална страница