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


Уеб форма за подписване със смарт карта



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

Уеб форма за подписване със смарт карта


Уеб формата за подписване със смарт карта (SignedFileUploadForm-SmartCard.jsp) е почти напълно еднаква с уеб формата за подписване с PKCS#12 хранилище. Единствената разлика е в името на използвания аплет и във версията на JDK, която е указана за аплета:

SignedFileUploadForm-SmartCard.jsp

<%

/**

* A JSP that contains the form for signing and uploading a file. The form

* contains 3 fields - the file to be uploaded, the certification chain and

* the digital signature. It also contains the SmartCardSignerApplet that

* signs the selected file on the client's machine.

*

* This file is part of NakovDocumentSigner digital document

* signing framework for Java-based Web applications:

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

*

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

* National Academy for Software Development - http://academy.devbg.org

* 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.

*/

%>

<%@ taglib uri="/WEB-INF/taglibs/struts-html.tld" prefix="html" %>

<html>

<body>

<html:form type="demo.SignedFileUploadActionForm" action="/SignedFileUpload"

method="post" enctype="multipart/form-data">

Please choose file to sign and upload: <html:file property="uploadFile"/> <br>

Certification chain (Base64-encoded): <html:text property="certChain"/> <br>

Digital signature (Base64-encoded): <html:text property="signature"/> <br>

<br>

<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="type" value="application/x-java-applet;version=1.5">

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

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

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

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

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

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

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

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

<comment>

<embed

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

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

code="SmartCardSignerApplet"

archive="SmartCardSignerApplet.jar"

width="130"

height="25"

mayscript="true"

scriptable="true"

scriptable="true"

fileNameField="uploadFile"

certificationChainField="certChain"

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>

<br>

<br>

<html:submit property="submit" value="Upload file"/>

html:form>

body>

html>

В страницата е указано, че трябва да има инсталиран Java Plug-In версия 1.5 или по-нова. Ако това не е така, браузърът на потребителя автома­тично се препраща към страницата, от която може да се изтегли послед­ната версия на Java Plug-In.

Клас за съхранение на данните от двете уеб форми


Зад Struts-формата за изпращане на подписан файл стои съответен Struts action form клас, който се използва за съхранение на данните от нея:

SignedFileUploadActionForm.java

package demo;

import org.apache.struts.action.ActionForm;

import org.apache.struts.upload.FormFile;

/**

* Struts action form class that maps to the form for uploading signed files

* (SignedFileUploadForm-PFX.jsp or SignedFileUploadForm-SmartCard.jsp). It is

* actually a data structure that consist of the uploaded file, the sender's

* certification chain and the digital signature of the uploaded file.

*

* This file is part of NakovDocumentSigner digital document

* signing framework for Java-based Web applications:

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

*

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

* National Academy for Software Development - http://academy.devbg.org

* 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 SignedFileUploadActionForm extends ActionForm {

private FormFile mUploadFile;

private String mCertChain;

private String mSignature;

public FormFile getUploadFile() {

return mUploadFile;

}

public void setUploadFile(FormFile aUploadFile) {

mUploadFile = aUploadFile;

}

public String getCertChain() {



return mCertChain;

}

public void setCertChain(String aCertChain) {

mCertChain = aCertChain;

}

public String getSignature() {



return mSignature;

}

public void setSignature(String aSignature) {

mSignature = aSignature;

}

}



Този клас не представлява нищо повече от обикновен Java bean, в който са дефинирани свойства, точно съответстващи на полетата от HTML формата. Когато потребителят попълни полетата на формата и я изпрати, Struts framework
автоматично създава обект от този клас и прехвърля получените от формата данни в свойствата на този обект.

Действието, което посреща изпратената форма


Получените данни, записани в action form обекта се обработват от действието /SignedFileUpload, което съответства на Struts action класа SignedFileUploadAction:

SignedFileUploadAction.java

package demo;

import javax.servlet.http.*;

import org.apache.struts.action.*;

/**

* Struts action class for handling the results of submitting the forms

* SignedFileUploadForm-PFX.jsp and SignedFileUploadForm-SmartCard.jsp.

*

* It gets the data from the form as SignedFileUploadActionForm object and puts

* this object in the current user's session with key "signedFileUploadActionForm".

* After that this action redirects the user's Web browser to

* ShowSignedFileUploadResults.jsp that is used to display the received file,

* certificate, certification chain and digital signature and their validity.

*

* This file is part of NakovDocumentSigner digital document

* signing framework for Java-based Web applications:

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

*

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

* National Academy for Software Development - http://academy.devbg.org

* 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 SignedFileUploadAction extends Action {

public ActionForward perform(ActionMapping aActionMapping, ActionForm

aActionForm, HttpServletRequest aRequest, HttpServletResponse aResponse) {

SignedFileUploadActionForm signedFileUploadActionForm =

(SignedFileUploadActionForm) aActionForm;

HttpSession session = aRequest.getSession();

session.setAttribute(



"signedFileUploadActionForm", signedFileUploadActionForm);

return aActionMapping.findForward("ShowSignedFileUploadResults");

}

}



Всичко, което това събитие прави, е да запише получения action form обект с данните от формата в потребителската сесия под ключ “signedFileUploadActionForm” и след това да пренасочи изпълнението на уеб приложението към страни­цата за анализ на получения подписан файл ShowSignedFileUploadResults.jsp, която е описана в конфигура­ционния файл на Struts.

Страница за анализ на получените данни


Страницата за анализ на получения подписан файл е малко по-сложна от останалите. Тя извлича action form обекта от сесията на потребителя и след това анализира получените данни и показва информация за тях. Извършва се верификация на цифровия подпис на получения файл и верификация на получения цифров сертификат, използван за подписва­нето. Ако е приложена сертификационна верига, и тя се верифицира. Ето сорс кода на страницата ShowSignedFileUploadResults.jsp:

ShowSignedFileUploadResults.jsp

<%

/**

* A JSP for verifying digital signature, certificate and certification chain of the

* received signed file. It assumes that the data, received by submitting some of

* the forms SignedFileUploadForm-PFX.jsp or SignedFileUploadForm-SmartCard.jsp

* stays in the user's session in SignedFileUploadActionForm object stored with the

* key "signedFileUploadActionForm".

*

* The trusted certificates used for direct certificate verification should be

* located in a directory whose name stays in the CERTS_FOR_DIRECT_VALIDATION_DIR

* constant (see the code below).

*

* The trusted root CA certificates used for certification chain verification should

* be located in a directory whose name stays in the TRUSTED_CA_ROOT_CERTS_DIR

* constant (see the code below).

*

* This file is part of NakovDocumentSigner digital document

* signing framework for Java-based Web applications:

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

*

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

* National Academy for Software Development - http://academy.devbg.org

* 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.

*/

%>

<%@page import="demo.*,

java.io.*,

java.util.*,

java.security.*,

java.security.cert.*,

org.apache.struts.upload.FormFile,

javax.servlet.jsp.JspWriter" %>

<%!

public static final String CERTS_FOR_DIRECT_VALIDATION_DIR =

"/WEB-INF/certs-for-direct-validation";

public static final String TRUSTED_CA_ROOT_CERTS_DIR =

"/WEB-INF/trusted-CA-root-certs";

private static final int KEY_USAGE_DIGITAL_SIGNATURE = 0;

private static final int KEY_USAGE_NON_REPUDIATION = 1;

private static final int KEY_USAGE_KEY_ENCIPHERMENT = 2;

private static final int KEY_USAGE_DATA_ENCIPHERMENT = 3;

private static final int KEY_USAGE_KEY_AGREEMENT = 4;

private static final int KEY_USAGE_CERT_SIGN = 5;

private static final int KEY_USAGE_CRL_SIGN = 6;

private static final int KEY_USAGE_ENCIPHER_ONLY = 7;

private static final int KEY_USAGE_DECIPHER_ONLY = 8;

private ServletContext mApplicationContext = null;

private JspWriter mOut = null;

private SignedFileUploadActionForm mActionForm = null;

private FormFile mReceivedFile = null;

private byte[] mReceivedFileData = null;

private CertPath mCertPath = null;

private X509Certificate[] mCertChain = null;

private X509Certificate mCertificate = null;

private byte[] mSignature = null;

private String mSignatureBase64Encoded;

%>

<html>

<body>

<%

mApplicationContext = application;

mOut = out;

mActionForm = (SignedFileUploadActionForm)

session.getAttribute("signedFileUploadActionForm");

if (mActionForm == null) {

// User session does not contain the SignedFileUploadActionForm object

%>

Please choose file for signing first.

<%

} else {



try {

// Analyse received signed file and display information about it

processReceivedFile();

displayFileInfo(mReceivedFile, mReceivedFileData);

mOut.println("


");



// Analyse received certification chain

processReceivedCertificationChain();



// Analyse received digital signature

processReceivedSignature();



// Display signature, verify it and display the verification results

displaySignature(mSignatureBase64Encoded);

verifyReceivedSignature();

mOut.println("


");



// Display certificate, verify it and display the results

displayCertificate(mCertificate);

verifyReceivedCertificate();

mOut.println("


");



// Display certification chain, verify it and display the results

displayCertificationChain(mCertChain);

verifyReceivedCertificationChain();

} catch (Exception e) {



// Error occurred. Display the exception with its full stack trace

out.println("


Error: "
);

e.printStackTrace(new PrintWriter(out));

out.println("
"
);

}

}



%>

<br/>

Go to the <a href="index.html">start pagea>.

body>

html>

<%!

/**

* Extracts the received file and its data from the received HTML form data.

* The extracted file and its content are stored in the member variables

* mReceivedFile and mReceivedFileData.

* @throws Exception if no file is received.

*/

private void processReceivedFile()

throws Exception {

mReceivedFile = mActionForm.getUploadFile();



if (mReceivedFile == null) {

throw new Exception("No file received. Please upload some file.");

}

mReceivedFileData = mReceivedFile.getFileData();



}

/**

* Displays information about give file. Displays its file name and file size.

*/

private void displayFileInfo(FormFile aFile, byte[] aFileData)

throws Exception {

String fileName = aFile.getFileName();

mOut.println("Signed file successfully uploaded.
"
);

mOut.println("File name: " + fileName + "


"
);

mOut.println("File size: " + aFileData.length + " bytes.


"
);

}

/**



* Analyses received certification chain and extracts the certificates that it

* consist of. The certification chain should be PkiPath-encoded (ASN.1 DER

* formatted), stored as Base64-string. The extracted chain is stored in the

* member variables mCertPath as a CertPath object and in mCertChain as array

* of X.509 certificates. Also the certificate used for signing the received

* file is extracted in the member variable mCertificate.

* @throws Exception if the received certification chain can not be decoded

* (i.e. its encoding or internal format is invalid).

*/

private void processReceivedCertificationChain()

throws Exception {

String certChainBase64Encoded = mActionForm.getCertChain();



try {

mCertPath = DigitalSignatureUtils.loadCertPathFromBase64String(

certChainBase64Encoded);

List certsInChain = mCertPath.getCertificates();

mCertChain = (X509Certificate[])

certsInChain.toArray(new X509Certificate[0]);

mCertificate = mCertChain[0];

}

catch (Exception e) {



throw new Exception("Invalid certification chain received.", e);

}

}



/**

* Displays given certification chain. Displays the length of the chain and the

* subject distinguished names of all certificates in the chain, starting from

* the first and finishing to the last.

*/

private void displayCertificationChain(X509Certificate[] aCertChain)

throws IOException {

mOut.println("Certification chain length: " + aCertChain.length + "


"
);

for (int i=0; iPrincipal certPrincipal = aCertChain[i].getSubjectDN();

mOut.println("certChain[" + i + "] = " + certPrincipal + "
"
);

}

}



/**

* Analyses received Base64-encoded digital signature, decodes it and stores it

* in the member variable mSignature.

* @throws Exception if the received signature can not be decoded.

*/

private void processReceivedSignature()

throws Exception {

mSignatureBase64Encoded = mActionForm.getSignature();



try {

mSignature = Base64Utils.base64Decode(mSignatureBase64Encoded);

} catch (Exception e) {

throw new Exception("Invalid signature received.", e);

}

}



/**

* Displays given Base64-encoded digital signature.

*/

private void displaySignature(String aSignatureBase64Encoded)

throws IOException {

mOut.println("Digital signature (Base64-encoded): " +

aSignatureBase64Encoded);

}

/**



* Verifies the received signature using the received file data and certificate

* and displays the verification results. The received document, certificate and

* signature are taken from the member variables mReceivedFileData, mCertificate

* and mSignature respectively.

*/

private void verifyReceivedSignature()

throws IOException {

mOut.println("Digital signature status: ");



try {

boolean signatureValid = DigitalSignatureUtils.verifyDocumentSignature(

mReceivedFileData, mCertificate, mSignature);



if (signatureValid)

mOut.println("Signature is verified to be VALID.");



else

mOut.println("Signature is INVALID!");

} catch (Exception e) {

e.printStackTrace();

mOut.println("Signature verification failed due to exception: " +

e.toString());

}

mOut.println("");



}

/**

* Displays information about given certificate. This information includes the

* certificate subject distinguished name and its purposes (public key usages).

*/

private void displayCertificate(X509Certificate aCertificate)

throws IOException {

String certificateSubject = aCertificate.getSubjectDN().toString();

mOut.println("Certificate subject: " + certificateSubject + "
"
);

boolean[] certKeyUsage = aCertificate.getKeyUsage();

mOut.println("Certificate purposes (public key usages):


"
);

if (certKeyUsage != null) {

if (certKeyUsage[KEY_USAGE_DIGITAL_SIGNATURE])

mOut.println("[digitalSignature] - verify digital signatures


"
);

if (certKeyUsage[KEY_USAGE_NON_REPUDIATION])

mOut.println("[nonRepudiation] - verify non-repudiation


"
);

if (certKeyUsage[KEY_USAGE_KEY_ENCIPHERMENT])

mOut.println("[keyEncipherment] - encipher keys for transport


"
);

if (certKeyUsage[KEY_USAGE_DATA_ENCIPHERMENT])

mOut.println("[dataEncipherment] - encipher user data


"
);

if (certKeyUsage[KEY_USAGE_KEY_AGREEMENT])

mOut.println("[keyAgreement] - use for key agreement


"
);

if (certKeyUsage[KEY_USAGE_CERT_SIGN])

mOut.println("[keyCertSign] - verify signatures on certs


"
);

if (certKeyUsage[KEY_USAGE_CRL_SIGN])

mOut.println("[cRLSign] - verify signatures on CRLs


"
);

if (certKeyUsage[KEY_USAGE_ENCIPHER_ONLY])

mOut.println("[encipherOnly] - encipher during key agreement


"
);

if (certKeyUsage[KEY_USAGE_DECIPHER_ONLY])

mOut.println("[decipherOnly] - decipher during key agreement


"
);

} else {

mOut.println("[No purposes defined]
"
);

}

}



/**

* Verifies received certificate directly and displays the verification results.

* The certificate for verification is taken form mCertificate member variable.

* Trusted certificates are taken from the CERTS_FOR_DIRECT_VALIDATION_DIR

* directory. This directory should be relative to the Web application root

* directory and should contain only .CER files (DER-encoded X.509 cert.).

*/

private void verifyReceivedCertificate()

throws IOException, GeneralSecurityException {

// Create the list of the trusted certificates for direct validation

X509Certificate[] trustedCertificates =

getCertificateList(mApplicationContext,CERTS_FOR_DIRECT_VALIDATION_DIR);

// Verify the certificate and display the verification results

mOut.println("Certificate direct verification status: ");



try {

DigitalSignatureUtils.verifyCertificate(

mCertificate, trustedCertificates);

mOut.println("Certificate is verified to be VALID.");

} catch (CertificateExpiredException cee) {

mOut.println("Certificate is INVALID (validity period expired)!");

} catch (CertificateNotYetValidException cnyve) {

mOut.println("Certificate is INVALID (validity period not started)!");

} catch (DigitalSignatureUtils.CertificateValidationException cve) {

mOut.println("Certificate is INVALID! " + cve.getMessage());

}

mOut.println("");



}

/**

* Verifies received certification chain and displays the verification results.

* The chain for verification is taken form mCertPath member variable. Trusted

* CA root certificates are taken from the TRUSTED_CA_ROOT_CERTS_DIR directory.

* This directory should be relative to the Web application root directory and

* should contain only .CER files (DER-encoded X.509 certificates).

*/

private void verifyReceivedCertificationChain()

throws IOException, GeneralSecurityException {

// Create the most trusted CA set of trust anchors

X509Certificate[] trustedCACerts =

getCertificateList(mApplicationContext, TRUSTED_CA_ROOT_CERTS_DIR);

// Verify the certification chain and display the verification results

mOut.println("Certification chain verification: ");



try {

DigitalSignatureUtils.verifyCertificationChain(

mCertPath, trustedCACerts);

mOut.println("Certification chain verified to be VALID.");

} catch (CertPathValidatorException cpve) {

mOut.println("Certification chain is INVALID! Validation failed on " +



"cert [" + cpve.getIndex() + "] from the chain: "+cpve.toString());

}

mOut.println("


"
);

}

/**



* @return a list of X509 certificates, obtained by reading all files from the

* given directory. The supplied directory should be a given as a relative path

* from the Web appication root (e.g. "/WEB-INF/test") and should contain only

* .CER files (DER-encoded X.509 certificates).

*/

private X509Certificate[] getCertificateList(ServletContext aServletContext,

String aCertificatesDirectory)



throws IOException, GeneralSecurityException {

// Get a list of all files in the given directory

Set trustedCertsResNames =

aServletContext.getResourcePaths(aCertificatesDirectory);

// Allocate an array for storing the certificates

int count = trustedCertsResNames.size();

X509Certificate[] trustedCertificates = new X509Certificate[count];



// Read all X.509 certificate files one by one into an array

int index = 0;

Iterator trustedCertsResourceNamesIter = trustedCertsResNames.iterator();



while (trustedCertsResourceNamesIter.hasNext()) {

String certResName = (String) trustedCertsResourceNamesIter.next();

InputStream certStream =

aServletContext.getResourceAsStream(certResName);



try {

X509Certificate trustedCertificate =

DigitalSignatureUtils.loadX509CertificateFromStream(certStream);

trustedCertificates[index] = trustedCertificate;

index++;

} finally {

certStream.close();

}

}



return trustedCertificates;

}

%>


Как се обработва полученият подписан файл?


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

След това се декодира получената от клиента сертификационна верига. Понеже тя е била кодирана в текстов вид за да може да бъде пренесена през поле от HTML формата, първо се получава оригиналният й бинарен вид чрез Base64 декодиране. След това от този бинарен вид се възстано­вява оригиналната последователност от X.509 сертификати като се има предвид, че кодирането е било извършено във формат PkiPath. От декоди­раната сертификационна верига се изважда сертификатът на потребителя, изпол­зван при подписването. Този сертификат е винаги първият сертифи­кат от тази верига.

Следва декодиране на получената цифрова сигнатура за да се възстанови оригиналният й бинарен вид, след което се проверява дали тя е валидна. От сертификата на клиента се извлича публичният му ключ и с него се проверява дали получената сигнатура съответства на получения документ. На потребителя се отпечатва сигнатурата, в текстов вид, както е получена, заедно с резултата от нейната проверка.

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

Директната проверка използва съвкупност от доверени сертификати, която се прочита от специална директория, името на която се взима от константа в страницата. Тази директория трябва да е поддиректория на уеб приложе­нието и трябва да съдържа само файлове със сертификати (.CER файлове). Ако се установи, че сертификатът на потребителя е директно подписан от някой от тези сертификати, той се счита за валиден. На потребителя се отпечатва резултатът от директната верификация.

Следва проверка на получената сертификационна верига. Преди да започ­не самата проверка веригата се отпечатва на потребителя във вид на пос­ледователност от сертификати, всеки на отделен ред. За всеки сертификат се отпечатва поредният му номер във веригата и информация за собстве­ника му.

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

Проверката използва средствата на Java Certification Path API и установява дали подадената верига е валидна. Ако веригата се състои от само един сертификат (т. е. верига реално няма), тя се счита за невалидна. Ако веригата се състои от повече от един сертификат, от нея се премахва последният сертификат и се изпълнява PKIX алгоритъма.

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

За четенето на всички файлове от дадена директория се използват методите getResourcePaths() и getResourceAsStream() на класа ServletContext.

На всяка една стъпка от обработката на получения подписан файл, ако възникне проблемна ситуация, при която приложението не може да продължи работата си, на потребителя се отпечатва съобщение за грешка, придружено от пълния вид на полученото изключение.

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


Верификацията на цифрови подписи и сертификати в действие


Резултатът от всички описани проверки, които се извършват при получа­ване на подписан файл, изглежда по следния начин (фигура 4-7):



Фигура 4 24. Резултатът от верификация на цифров подпис и сертификат

Основната криптографска функционалност на приложението


Основната функционалност по верификацията на сигнатури, сертификати и сертификационни вериги се намира в класа DigitalSignatureUtils. Класът е базиран на средствата от Java Cryptography Architecture (JCA)
, Java Cryptography Extension
и Java Certification Path API
. Както и при апле­та, реализацията на всички криптографски алгоритми се осигурява от доставчика на криптографски услуги по подразбиране SunJSSE, който е част от JDK 1.4
. Ето и сорс кода на класа:

DigitalSignatureUtils.java

package demo;

import java.security.PublicKey;

import java.security.Signature;

import java.security.GeneralSecurityException;

import java.security.cert.*;

import java.io.*;

import java.util.List;

import java.util.HashSet;

/**

* Utility class for digital signatures and certificates verification.

*

* Verification of digital signature aims to confirm or deny that given signature is

* created by signing given document with the private key corresponding to given

* certificate. Verification of signatures is done with the standard digital

* signature verification algorithm, provided by Java Cryptography API:

* 1. The message digest is calculated from given document.

* 2. The original message digest is obtained by decrypting the signature with

* the public key of the signer (this public key is taken from the signer's

* certificate).

* 3. Values calculated in step 1. and step 2. are compared.

*

* Verification of a certificate aims to check if the certificate is valid wihtout

* inspecting its certification chain (sometimes it is unavailable). The certificate

* verification is done in two steps:

* 1. The certificate validity period is checked against current date.

* 2. The certificate is checked if it is directly signed by some of the trusted

* certificates that we have. A list of trusted certificates is supported for this

* direct certificate verification process. If we want to successfully validate the

* certificates issued by some certification authority (CA), we need to add the

* certificate of this CA in our trusted list. Note that some CA have several

* certificates and we should add only that of them, which the CA directly uses for

* issuing certificates to its clients.

*

* Verification of a certification chains aims to check if given certificate is

* valid by analysing its certification chain. A certification chain always starts

* with the user certificate that should be verified, then several intermediate CA

* certificates follow and at the end of the chain stays some root CA certificate.

* The verification process includes following steps (according to PKIX algorithm):

* 1. Check the certificate validity period against current date.

* 2. Check if each certificate in the chain is signed by the previous.

* 3. Check if all the certificates in the chain, except the first, belong to

* some CA, i.e. if they are authorized to be used for signing other certificates.

* 4. Check if the root CA certificate in the end of the chain is trusted, i.e.

* if is it in the list of trusted root CA certificates.

* The verification process uses PKIX algorithm, defined in RFC-3280, but don't use

* CRL lists.

*

* This file is part of NakovDocumentSigner digital document

* signing framework for Java-based Web applications:

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

*

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

* National Academy for Software Development - http://academy.devbg.org

* 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 DigitalSignatureUtils {

private static final String X509_CERTIFICATE_TYPE = "X.509";

private static final String CERT_CHAIN_ENCODING = "PkiPath";

private static final String DIGITAL_SIGNATURE_ALGORITHM_NAME = "SHA1withRSA";

private static final String CERT_CHAIN_VALIDATION_ALGORITHM = "PKIX";

/**

* Loads X.509 certificate from DER-encoded binary stream.

*/

public static X509Certificate loadX509CertificateFromStream(

InputStream aCertStream)



throws GeneralSecurityException {

CertificateFactory cf=CertificateFactory.getInstance(X509_CERTIFICATE_TYPE);

X509Certificate cert = (X509Certificate)cf.generateCertificate(aCertStream);

return cert;

}

/**



* Loads X.509 certificate from DER-encoded binary file (.CER file).

*/

public static X509Certificate loadX509CertificateFromCERFile(String aFileName)

throws GeneralSecurityException, IOException {

FileInputStream fis = new FileInputStream(aFileName);

X509Certificate cert = null;

try {

cert = loadX509CertificateFromStream(fis);

} finally {

fis.close();

}

return cert;

}

/**



* Loads a certification chain from given Base64-encoded string, containing

* ASN.1 DER formatted chain, stored with PkiPath encoding.

*/

public static CertPath loadCertPathFromBase64String(

String aCertChainBase64Encoded)



throws CertificateException, IOException {

byte[] certChainEncoded = Base64Utils.base64Decode(aCertChainBase64Encoded);

CertificateFactory cf=CertificateFactory.getInstance(X509_CERTIFICATE_TYPE);

InputStream certChainStream = new ByteArrayInputStream(certChainEncoded);

CertPath certPath;



try {

certPath = cf.generateCertPath(certChainStream, CERT_CHAIN_ENCODING);

} finally {

certChainStream.close();

}

return certPath;

}

/**



* Verifies given digital singature. Checks if given signature is obtained by

* signing given document with the private key corresponing to given public key.

*/

public static boolean verifyDocumentSignature(byte[] aDocument,

PublicKey aPublicKey, byte[] aSignature)



throws GeneralSecurityException {

Signature signatureAlgorithm =

Signature.getInstance(DIGITAL_SIGNATURE_ALGORITHM_NAME);

signatureAlgorithm.initVerify(aPublicKey);

signatureAlgorithm.update(aDocument);

boolean valid = signatureAlgorithm.verify(aSignature);

return valid;

}

/**



* Verifies given digital singature. Checks if given signature is obtained

* by signing given document with the private key corresponing to given

* certificate.

*/

public static boolean verifyDocumentSignature(byte[] aDocument,

X509Certificate aCertificate, byte[] aSignature)



throws GeneralSecurityException {

PublicKey publicKey = aCertificate.getPublicKey();



boolean valid = verifyDocumentSignature(aDocument, publicKey, aSignature);

return valid;

}

/**



* Verifies a certificate. Checks its validity period and tries to find a

* trusted certificate from given list of trusted certificates that is directly

* signed given certificate. The certificate is valid if no exception is thrown.

*

* @param aCertificate the certificate to be verified.

* @param aTrustedCertificates a list of trusted certificates to be used in

* the verification process.

*

* @throws CertificateExpiredException if the certificate validity period is

* expired.

* @throws CertificateNotYetValidException if the certificate validity period is

* not yet started.

* @throws CertificateValidationException if the certificate is invalid (can not

* be validated using the given set of trusted certificates.

*/

public static void verifyCertificate(X509Certificate aCertificate,

X509Certificate[] aTrustedCertificates)



throws GeneralSecurityException {

// First check certificate validity period

aCertificate.checkValidity();



// Check if the certificate is signed by some of the given trusted certs

for (int i=0; iX509Certificate trustedCert = aTrustedCertificates[i];



try {

aCertificate.verify(trustedCert.getPublicKey());



// Found parent certificate. Certificate is verified to be valid

return;

}

catch (GeneralSecurityException ex) {



// Certificate is not signed by current trustedCert. Try the next

}

}



// Certificate is not signed by any of the trusted certs --> it is invalid

throw new CertificateValidationException(

"Can not find trusted parent certificate.");

}

/**



* Verifies certification chain using "PKIX" algorithm, defined in RFC-3280.

* It is considered that the given certification chain start with the target

* certificate and finish with some root CA certificate. The certification

* chain is valid if no exception is thrown.

*

* @param aCertChain the certification chain to be verified.

* @param aTrustedCACertificates a list of most trusted root CA certificates.

* @throws CertPathValidatorException if the certification chain is invalid.

*/

public static void verifyCertificationChain(CertPath aCertChain,

X509Certificate[] aTrustedCACertificates)



throws GeneralSecurityException {

int chainLength = aCertChain.getCertificates().size();

if (chainLength < 2) {

throw new CertPathValidatorException("The certification chain is too " +

"short. It should consist of at least 2 certiicates.");

}

// Create a set of trust anchors from given trusted root CA certificates

HashSet trustAnchors = new HashSet();

for (int i = 0; i < aTrustedCACertificates.length; i++) {

TrustAnchor trustAnchor =



new TrustAnchor(aTrustedCACertificates[i], null);

trustAnchors.add(trustAnchor);

}

// Create a certification chain validator and a set of parameters for it

PKIXParameters certPathValidatorParams = new PKIXParameters(trustAnchors);

certPathValidatorParams.setRevocationEnabled(false);

CertPathValidator chainValidator =

CertPathValidator.getInstance(CERT_CHAIN_VALIDATION_ALGORITHM);

// Remove the root CA certificate from the end of the chain. It is required

// by the validation algorithm because by convention the trust anchor

// certificates should not be a part of the chain that is validated

CertPath certChainForValidation = removeLastCertFromCertChain(aCertChain);



// Execute the certification chain validation

chainValidator.validate(certChainForValidation, certPathValidatorParams);

}

/**

* Removes the last certificate from given certification chain.

* @return given cert chain without the last certificate in it.

*/

private static CertPath removeLastCertFromCertChain(CertPath aCertChain)

throws CertificateException {

List certs = aCertChain.getCertificates();



int certsCount = certs.size();

List certsWithoutLast = certs.subList(0, certsCount-1);

CertificateFactory cf=CertificateFactory.getInstance(X509_CERTIFICATE_TYPE);

CertPath certChainWithoutLastCert = cf.generateCertPath(certsWithoutLast);



return certChainWithoutLastCert;

}

/**



* Exception class for certificate validation errors.

*/

public static class CertificateValidationException

extends GeneralSecurityException {

public CertificateValidationException(String aMessage) {

super(aMessage);

}

}



}

Как работи основната криптографска функционалност?


Класът започва с методи за зареждане на сертификат от поток и от файл, с които се прочитат файловете с доверените сертификати, използвани при проверката на сертификати и сертификационни вериги. Очаква се тези файлове да бъдат в стандартния .CER формат (ASN.1 DER-кодирани).

Следва метод за зареждане на сертификационна верига, представена във формат PkiPath и кодирана в текстов вид с кодиране Base64.

Класът предлага още функционалност за проверка на цифрови сигнатури, която използва алгоритъма SHA1withRSA – същият, който се използва от аплета за подписване.

Предоставят се още методи за директна верификация на сертификат и верификация на сертификационни вериги.

Методът за директна верификация на сертификат като параметър очаква сертификата и множество от доверени сертификати, сред които да търси издателя на проверявания сертификат. Верификацията е успешна, ако методът завърши изпълнението си без да генерира изключение.

Методът за проверката на сертификационни вериги е малко по-сложен. Той приема като вход сертификационна верига и множество от доверени Root-сертификати.

При проверката на подадената верига първоначално се проверява дали тя се състои от поне 2 сертификата. Верига от 1 сертификат не може да бъде валидна, защото тя трябва да завършва с Root-сертификата на някой сертификационен орган от първо ниво, а в същото време тя започва с потребителския сертификат.

Проверката започва с построяване на множество от крайни точки на доверие (TrustAnchor обекти) от зададените доверени Root-сертификати. След това се създава обект, съдържащ множеството от параметри на алгоритъма за проверка. От тези параметри се указва на алгоритъма да не използва списъци от анулирани сертификати (CRLs). Използването на такива CRL списъци не се прилага, защото е сложно за реализация и изисква допълнителни усилия за извличане на тези списъци от сървърите на сертификационните органи, които ги издават и разпространяват.

След създаването на крайните точки на доверите и инициализира­нето на параметрите на алгоритъма за верификация има още една важна стъпка преди самата верификация. Алгоритъмът PKIX, който се използва за вери­фикацията има една особеност. Той очаква да му се подаде сертификаци­онна верига, която не завършва с Root-сертификат на някой сертифика­ционен орган, а със сертификата, който стои непосредствено преди него, т. е. очаква от сертификационната верига да бъде отстранен последният сертификат. Ако последният сертификат от веригата не бъде отстранен преди проверката, верификацията няма да е успешна.

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



За работа с Base64-кодирана информация в уеб приложението се използва същият клас Base64Utils, който се използва и при аплета за подписване на файлове.

Конфигурационен файл на уеб приложението


Съгласно стандартите на платформата J2EE
за работата на демонстраци­онното уеб приложение е необходим още конфи­гура­ционният файл:

web.xml

xml version="1.0" encoding="UTF-8"?>



2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

<servlet>

<servlet-name>actionservlet-name
>

<servlet-class>org.apache.struts.action.ActionServletservlet-class>

<init-param>

<param-name>configparam-name>

<param-value>/WEB-INF/struts-config.xmlparam-value>

init-param>

<load-on-startup>2load-on-startup>

servlet>

<servlet-mapping>

<servlet-name>actionservlet-name>

<url-pattern>*.dourl-pattern>

servlet-mapping>

<welcome-file-list>

<welcome-file>/index.htmlwelcome-file>

welcome-file-list>

web-app>

Този файл съдържа настройките на приложението и указва, да бъде заре­ден и инициализиран Struts framework и неговият ActionServlet. Като стартова страница по подразбиране е указан файлът index.html от глав­ната директория на приложението.

Стартова страница на приложението


Стартовата страница index.html е изключително проста. Тя съдържа просто две препратки – към страницата за подписване с PKCS#12 храни­лище и към страницата за подписване със смарт карта. Ето нейният код:

index.html



* This file is part of NakovDocumentSigner digital document

* signing framework for Java-based Web applications:

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

*

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

* National Academy for Software Development - http://academy.devbg.org

* 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.

-->

<html>

<body>

<a href="SignedFileUploadForm-PFX.jsp">Digital document signing with .PFX file

(PKCS#12 keystore) demo.a>



<br>

<a href="SignedFileUploadForm-SmartCard.jsp">Digital document signing with

smart card (PKCS#11 token) demo.a>



body>

html>

Когато демонстрационното уеб приложение се стартира първоначално, то зарежда именно тази страница и дава възможност на потребителя да тест­ва съответно двата аплета за подписване на документи.

Конфигурационен файл на Struts framework


За да се използва Struts framework
е необходим и неговият конфигурацио­нен файл struts-config.xml:

struts-config.xml

xml version="1.0" encoding="UTF-8"?>



<struts-config>

<form-beans>

<form-bean name="SignedFileUploadActionForm"

type="demo.SignedFileUploadActionForm"/>

form-beans>

<action-mappings>

<action name="SignedFileUploadActionForm"

type="demo.SignedFileUploadAction"

scope="request" path="/SignedFileUpload">

<forward name="ShowSignedFileUploadResults"

path="/ShowSignedFileUploadResults.jsp" redirect="true"/>

action>

action-mappings>

struts-config>

Този файл конфигурира Struts формите и Struts action класовете, които се използват от уеб приложението.

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


Съвкупността от всички описани файлове съставя демонстрационното уеб приложение. За да го компилираме и подготвим за изпълнение можем да използваме следния Apache Ant
скрипт:

build.xml

xml version="1.0" encoding="iso-8859-1"?>

<project name="DocumentSigningDemoWebApp" default="build" basedir=".">

<target name="init">

<property name="app-name" value="DocumentSigningDemoWebApp"/>

<property name="webapp-name" value="${app-name}.war"/>

<property name="src-dir" value="src"/>

<property name="www-dir" value="wwwroot"/>

<property name="classes-dir" value="${www-dir}/WEB-INF/classes"/>

<property name="web-xml" value="${www-dir}/WEB-INF/web.xml"/>

<property name="lib-dir" value="${www-dir}/WEB-INF/lib"/>

<property name="deploy-dir" value="deploy"/>

target>

<target name="clean" depends="init">

<delete dir="${classes-dir}"/>

<mkdir dir="${classes-dir}"/>

<delete dir="${deploy-dir}"/>

<mkdir dir="${deploy-dir}"/>

target>

<target name="compile" depends="init">

<javac srcdir="src"

destdir="wwwroot/WEB-INF/classes"

debug="on">

<classpath>

<fileset dir="${lib-dir}">

<include name="**/*.jar"/>

<include name="**/*.zip"/>

fileset>

classpath>

javac>

target>

<target name="war" depends="init">

<war compress="true" destfile="${deploy-dir}/${webapp-name}"

webxml="${web-xml}">

<fileset dir="${www-dir}">

<include name="**/*.*"/>

fileset>

war>

target>

<target name="build">

<antcall target="clean"/>

<antcall target="compile"/>

<antcall target="war"/>

target>

project>

Скриптът компилира файловете на приложението и пакетира всички негови части (JSP файлове, ресурси, библиотеки, компилирани класове и др.) в архив като подрежда файловете и директориите по специален начин, съгласно J2EE
специфика­циите за уеб приложения.

Резултатът от изпълнението с инструмента ant на този скрипт е файлът DocumentSigningDemoWebApp.war, който съдържа компилираното уеб при­ложение във вид готов за изпълнение върху стандартен уеб контейнер.


Инсталиране (deployment) на приложението


За да изпълним приложението DocumentSigningDemoWebApp.war е необхо­димо да го инсталираме (deployment) на някой J2EE
сървър или сървър за Java уеб приложения (Servlet container).

Ако използваме сървъра за J2EE уеб приложения Apache Tomcat, е достатъчно до копираме файла DocumentSigningDemoWebApp.war в директо­рия %TOMCAT_HOME%/webapps и да стартираме Tomcat. След това (при стандартна инсталация и конфигурация на Tomcat) приложението е дос­тъпно от адрес: http://localhost:8080/DocumentSigningDemoWebApp/ (фи­гура 4-8):





Фигура 4 25. Уеб приложението DocumentSigningDemoWebApp



Национална академия по разработка на софтуер

Лекторите
» Светлин Наков е преподавател по съвременни софтуерни технологии в СУ “Св. Климент Охридски”.

Той е автор на десетки на­учни и технически публи­ка­ции и ня­колко книги, свър­­зани с раз­работката на соф­ту­ер, заради което е търсен лектор и консултант.

През 2004 г. получава наг­ра­дата "Джон Атанасов" от прези­дента на България Ге­орги Пър­ва­нов за приноса му към разви­тието на инфор­ма­ци­он­ните технологии и ин­формаци­он­ното общество.
» Мартин Кулов е изпълнителен директор във фирма “Код Атест”, където раз­работва проекти за пови­ша­ване качеството на соф­ту­ер­ните продукти в Бъл­гария чрез автоматизация на про­цесите и внедряване на сис­теми за управление на ка­чеството.

Мартин е опитен лектор и сертифициран от Майкрософт разработчик по програмите MCSD и MCSD.NET.


» Други инструк­тори с опит като преподаватели и програмисти.

Академията
» Национална академия по раз­ра­ботка на софтуер (НАРС) е център за професионално обу­чение на соф­ту­ерни специалисти.
» НАРС провежда задълбочени кур­сове по разработка на софтуер и съв­ременни софтуерни тех­нологии.
» Предлагани специалности:

.NET Enterprise Developer

Java Enterprise Developer
» Качествено обу­чение с много практически упраж­нения
» Завършвате само за 3 месеца.
» Гарантирана работа след успеш­но завършване!
» Професионална сертификация!
» БЕЗПЛАТНО!

Учите безплатно, плащате след като завършите и започнете работа.

Стипендии от софтуерни фирми.


http://academy.devbg.org


Каталог: 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 2019
отнасят до администрацията

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