From b9ccb62d35a755efb505d426ce924d5a8fbe937a Mon Sep 17 00:00:00 2001 From: "Bonato, Martin" Date: Thu, 8 Feb 2018 22:19:55 +0100 Subject: BulkSignature implementation --- .../at/gv/egiz/bku/local/stal/LocalBKUWorker.java | 5 +- .../local/stal/LocalBulkSignRequestHandler.java | 82 ++++++ .../gv/egiz/bku/local/stal/LocalSecureViewer.java | 289 +++++++++++++++++---- 3 files changed, 330 insertions(+), 46 deletions(-) create mode 100644 BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalBulkSignRequestHandler.java (limited to 'BKULocal/src/main/java/at/gv/egiz') diff --git a/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalBKUWorker.java b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalBKUWorker.java index afaad92d..c4125944 100644 --- a/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalBKUWorker.java +++ b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalBKUWorker.java @@ -30,6 +30,7 @@ import at.gv.egiz.bku.smccstal.GetCertificateRequestHandler; import at.gv.egiz.bku.smccstal.GetHardwareInfoRequestHandler; import at.gv.egiz.bku.smccstal.PINManagementRequestHandler; import at.gv.egiz.bku.smccstal.IdentityLinkRequestHandler; +import at.gv.egiz.stal.BulkSignRequest; import at.gv.egiz.stal.QuitRequest; import at.gv.egiz.stal.STALRequest; import at.gv.egiz.stal.STALResponse; @@ -53,8 +54,8 @@ public class LocalBKUWorker extends AbstractBKUWorker { public LocalBKUWorker(BKUGUIFacade gui, JFrame container) { super(gui); this.container = container; - addRequestHandler(SignRequest.class, - new LocalSignRequestHandler(new LocalSecureViewer(gui))); + addRequestHandler(SignRequest.class, new LocalSignRequestHandler(new LocalSecureViewer(gui))); + addRequestHandler(BulkSignRequest.class, new LocalBulkSignRequestHandler(new LocalSecureViewer(gui))); addRequestHandler(PINManagementRequest.class, new PINManagementRequestHandler()); addRequestHandler(IdentityLinkRequest.class, new IdentityLinkRequestHandler()); addRequestHandler(GetCertificateRequest.class, new GetCertificateRequestHandler()); diff --git a/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalBulkSignRequestHandler.java b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalBulkSignRequestHandler.java new file mode 100644 index 00000000..6ae4496d --- /dev/null +++ b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalBulkSignRequestHandler.java @@ -0,0 +1,82 @@ +/* + * Copyright 2015 Datentechnik Innovation GmbH and Prime Sign GmbH, Austria + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ + + +package at.gv.egiz.bku.local.stal; + +import java.util.LinkedList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.bku.smccstal.BulkSignRequestHandler; +import at.gv.egiz.stal.BulkSignRequest; +import at.gv.egiz.stal.ErrorResponse; +import at.gv.egiz.stal.HashDataInput; +import at.gv.egiz.stal.STALRequest; +import at.gv.egiz.stal.STALResponse; +import at.gv.egiz.stal.SignRequest; + +/** + * + * @author szoescher + */ +public class LocalBulkSignRequestHandler extends BulkSignRequestHandler { + + private final Logger log = LoggerFactory.getLogger(LocalBulkSignRequestHandler.class); + + public LocalBulkSignRequestHandler(LocalSecureViewer secureViewer) { + super(secureViewer); + } + + /** + * If the request is a SIGN request, it contains a list of DataObjectHashDataInput + * providing the pre-digested input stream (that can be obtained repeatedly) if + * reference caching is enabled (or null otherwise). + * @param request + * @return + */ + @Override + public STALResponse handleRequest(STALRequest request) throws InterruptedException { + + if (request instanceof BulkSignRequest) { + + BulkSignRequest bulkSignRequest = (BulkSignRequest) request; + + List hashDataInputs = new LinkedList(); + + for(SignRequest signRequest : bulkSignRequest.getSignRequests()){ + + hashDataInputs.addAll(signRequest.getHashDataInput()); + } + + ((LocalSecureViewer) secureViewer).setDataToBeSigned(hashDataInputs); + + return super.handleRequest(request); + } else { + log.error("Got unexpected STAL request: {}.", request); + ErrorResponse err = new ErrorResponse(1000); + err.setErrorMessage("Got unexpected STAL request: " + request); + return err; + } + } +} diff --git a/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalSecureViewer.java b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalSecureViewer.java index 5e346aba..25f30463 100644 --- a/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalSecureViewer.java +++ b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalSecureViewer.java @@ -24,31 +24,43 @@ package at.gv.egiz.bku.local.stal; -import at.gv.egiz.bku.slcommands.impl.DataObjectHashDataInput; -import at.gv.egiz.bku.smccstal.SecureViewer; -import java.io.IOException; -import java.util.ArrayList; +import iaik.me.security.CryptoException; +import iaik.me.security.MessageDigest; -import at.gv.egiz.bku.gui.BKUGUIFacade; -import at.gv.egiz.stal.HashDataInput; -import at.gv.egiz.stal.impl.ByteArrayHashDataInput; -import at.gv.egiz.stal.signedinfo.ReferenceType; -import at.gv.egiz.stal.signedinfo.SignedInfoType; import java.awt.event.ActionListener; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; +import java.security.DigestException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; + +import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.bku.gui.hashdata.HashDataInputLoader; +import at.gv.egiz.bku.gui.viewer.SecureViewer; +import at.gv.egiz.bku.slcommands.impl.DataObjectHashDataInput; +import at.gv.egiz.bku.slcommands.impl.cms.ReferencedHashDataInput; +import at.gv.egiz.stal.HashDataInput; +import at.gv.egiz.stal.SignatureInfo; +import at.gv.egiz.stal.hashdata.StubHashDataInput; +import at.gv.egiz.stal.impl.ByteArrayHashDataInput; +import at.gv.egiz.stal.signedinfo.ReferenceType; + /** * * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> */ -public class LocalSecureViewer implements SecureViewer { +public class LocalSecureViewer implements SecureViewer, HashDataInputLoader { - private final Logger log = LoggerFactory.getLogger(LocalSignRequestHandler.class); + private final Logger log = LoggerFactory.getLogger(LocalSecureViewer.class); private List hashDataInputs = Collections.emptyList(); protected BKUGUIFacade gui; @@ -69,48 +81,23 @@ public class LocalSecureViewer implements SecureViewer { * @throws java.lang.Exception */ @Override - public void displayDataToBeSigned(SignedInfoType signedInfo, + public void displayDataToBeSigned(SignatureInfo signedInfo, ActionListener okListener, String okCommand) throws Exception { - if (signedInfo.getReference().size() == 0) { - log.error("No hashdata input selected to be displayed: null."); - throw new Exception("No HashData Input selected to be displayed."); - } + + log.info("Retrieve data to be signed for dsig:SignedInfo {}.", signedInfo.getId()); + List hdi = getHashDataInputs(signedInfo); + List verifiedDataToBeSigned = verifyHashDataInput(signedInfo.getReference(), hdi.get(0)); ArrayList selectedHashDataInputs = new ArrayList(); - for (ReferenceType dsigRef : signedInfo.getReference()) { - // don't get Manifest, QualifyingProperties, ... - if (dsigRef.getType() == null) { - String dsigRefId = dsigRef.getId(); - if (dsigRefId != null) { - boolean hdiAvailable = false; - for (HashDataInput hashDataInput : hashDataInputs) { - if (dsigRefId.equals(hashDataInput.getReferenceId())) { - log.debug("Display hashdata input for dsig:SignedReference {}.", - dsigRefId); - selectedHashDataInputs.add( - ensureCachedHashDataInput(hashDataInput)); - hdiAvailable = true; - break; - } - } - if (!hdiAvailable) { - log.error("No hashdata input for dsig:SignedReference {}.", dsigRefId); - throw new Exception( - "No HashDataInput available for dsig:SignedReference " + dsigRefId); - } - } else { - throw new Exception( - "Cannot get HashDataInput for dsig:Reference without Id attribute"); - } - } - } + selectedHashDataInputs.addAll(verifiedDataToBeSigned); + if (selectedHashDataInputs.size() < 1) { log.error("dsig:SignedInfo does not contain a data reference."); throw new Exception("dsig:SignedInfo does not contain a data reference."); } - gui.showSecureViewer(selectedHashDataInputs, okListener, okCommand); + gui.showSecureViewer(selectedHashDataInputs, okListener, okCommand, this); } @@ -136,4 +123,218 @@ public class LocalSecureViewer implements SecureViewer { return hashDataInput; } + @Override + public void displayDataToBeSigned(List signedInfo, ActionListener okListener, String okCommand) + throws DigestException, Exception { + log.warn("Called displayDataToBeSigned"); + ArrayList selectedHashDataInputs = new ArrayList(); + + + for (SignatureInfo nextSignedInfo : signedInfo) { + selectedHashDataInputs.addAll(addEmptyHashDataInputs(nextSignedInfo)); + } + + gui.showSecureViewer(selectedHashDataInputs, okListener, okCommand, this); + + } + + + + private Collection addEmptyHashDataInputs(SignatureInfo signedInfo) throws Exception { + if (signedInfo.getReference().size() == 0) { + log.error("No hashdata input selected to be displayed: null."); + throw new Exception("No HashData Input selected to be displayed."); + } + + ArrayList selectedHashDataInputs = new ArrayList(); + for (ReferenceType dsigRef : signedInfo.getReference()) { + + if (dsigRef.getType() == null) { + selectedHashDataInputs.add(new StubHashDataInput(dsigRef, signedInfo.getDisplayName(), signedInfo.getMimeType())); + } + } + return selectedHashDataInputs; + } + + @Override + public HashDataInput getHashDataInput(HashDataInput hashDataInput) throws Exception { + + if (hashDataInput instanceof StubHashDataInput) { + String referenceId = hashDataInput.getReferenceId(); + byte[] digest = hashDataInput.getDigest(); + if (referenceId != null || digest != null) { + boolean hdiAvailable = false; + + for (HashDataInput currentHashDataInput : hashDataInputs) { + + if (Arrays.equals(digest, currentHashDataInput.getDigest())) { + log.debug("Display hashdata input for dsig:SignedReference {}.", referenceId); + + if (currentHashDataInput instanceof ReferencedHashDataInput) { + + ReferenceType reference = ((StubHashDataInput) hashDataInput).getReference(); + return verifyHashDataInput(Arrays.asList(reference), currentHashDataInput).get(0); + + } else { + return (ensureCachedHashDataInput(currentHashDataInput)); + } + + } + } + + if (!hdiAvailable) { + for (HashDataInput currentHashDataInput : hashDataInputs) { + if (referenceId.equals(hashDataInput.getReferenceId())) { + log.debug("Display hashdata input for dsig:SignedReference {}.", referenceId); + if (currentHashDataInput instanceof ReferencedHashDataInput) { + + ReferenceType reference = ((StubHashDataInput) hashDataInput).getReference(); + return verifyHashDataInput(Arrays.asList(reference), currentHashDataInput).get(0); + + } else { + return (ensureCachedHashDataInput(currentHashDataInput)); + } + } + } + } + + if (!hdiAvailable) { + log.error("No hashdata input for dsig:SignedReference {}.", referenceId); + throw new Exception("No HashDataInput available for dsig:SignedReference " + referenceId); + } + } else { + throw new Exception("Cannot get HashDataInput for dsig:Reference without Id or digest attribute"); + } + } + return hashDataInput; + } + + + public List getHashDataInputs(SignatureInfo signedInfo) throws Exception { + + ArrayList selectedHashDataInputs = new ArrayList(); + + if (signedInfo.getReference().size() == 0) { + log.error("No hashdata input selected to be displayed: null."); + throw new Exception("No HashData Input selected to be displayed."); + } + + for (ReferenceType dsigRef : signedInfo.getReference()) { + // don't get Manifest, QualifyingProperties, ... + if (dsigRef.getType() == null) { + HashDataInput emptyHashDataInput = new StubHashDataInput(dsigRef, signedInfo.getDisplayName(), + signedInfo.getMimeType()); + + + selectedHashDataInputs.add(getHashDataInput(emptyHashDataInput)); + + } + } + return selectedHashDataInputs; + } + + private List verifyHashDataInput(List signedReferences, HashDataInput hashDataInput) + throws DigestException, NoSuchAlgorithmException, Exception { + + ArrayList verifiedHashDataInputs = new ArrayList(); + + for (ReferenceType signedRef : signedReferences) { + if (signedRef.getType() == null) { + log.info("Verifying digest for signed reference {}.", signedRef.getId()); + + String signedRefId = signedRef.getId(); + byte[] signedDigest = signedRef.getDigestValue(); + String signedDigestAlg = null; + if (signedRef.getDigestMethod() != null) { + signedDigestAlg = signedRef.getDigestMethod().getAlgorithm(); + } else { + throw new NoSuchAlgorithmException("Failed to verify digest value for reference " + signedRefId + ": no digest algorithm"); + } + + + if (hashDataInput == null) { + throw new Exception("No hashdata input for reference " + signedRefId + " returned by service"); + } + + byte[] hdi = null; + + try { + hdi = IOUtils.toByteArray(hashDataInput.getHashDataInput()); + } catch (IOException e) { + throw new Exception("No hashdata input for reference " + signedRefId + " provided by service.", e); + } + + String mimeType = hashDataInput.getMimeType(); + String encoding = hashDataInput.getEncoding(); + String filename = hashDataInput.getFilename(); + + if (log.isDebugEnabled()) { + log.debug("Digesting reference " + signedRefId + " (" + mimeType + ";" + encoding + ")"); + } + + byte[] hashDataInputDigest; + if ((signedRef.getURI() != null) && signedRef.getURI().startsWith("CMSExcludedByteRange:")) { + String range = signedRef.getURI().substring(21); + int sep = range.indexOf('-'); + int from = Integer.parseInt(range.substring(0, sep)); + int to = Integer.parseInt(range.substring(sep+1)); + + Arrays.fill(hdi, from, to+1, (byte)0); + + + byte[] hashData = new byte[hdi.length - ((to+1) - from)]; + if (from > 0) + System.arraycopy(hdi, 0, hashData, 0, from); + if ((to+1) < hdi.length) + System.arraycopy(hdi, to+1, hashData, from, hdi.length - (to+1)); + hashDataInputDigest = digest(hashData, signedDigestAlg); + } else { + hashDataInputDigest = digest(hdi, signedDigestAlg); + } + + log.debug("Comparing digest to claimed digest value for reference {}.", signedRefId); + if (!Arrays.equals(hashDataInputDigest, signedDigest)) { + log.error("Bad digest value for reference {}.", signedRefId); + throw new DigestException("Bad digest value for reference " + signedRefId); + } + + verifiedHashDataInputs.add(new ByteArrayHashDataInput(hdi, signedRefId, mimeType, encoding, filename)); + } + } + + return verifiedHashDataInputs; +} + + private byte[] digest(byte[] hashDataInput, String mdAlg) throws NoSuchAlgorithmException { + if ("http://www.w3.org/2000/09/xmldsig#sha1".equals(mdAlg)) { + mdAlg = "SHA-1"; + } else if ("http://www.w3.org/2001/04/xmlenc#sha256".equals(mdAlg)) { + mdAlg = "SHA-256"; + } else if ("http://www.w3.org/2001/04/xmlenc#sha224".equals(mdAlg)) { + mdAlg = "SHA-224"; + } else if ("http://www.w3.org/2001/04/xmldsig-more#sha224".equals(mdAlg)) { + mdAlg = "SHA-224"; + } else if ("http://www.w3.org/2001/04/xmldsig-more#sha384".equals(mdAlg)) { + mdAlg = "SHA-384"; + } else if ("http://www.w3.org/2001/04/xmlenc#sha512".equals(mdAlg)) { + mdAlg = "SHA-512"; + } else if ("http://www.w3.org/2001/04/xmldsig-more#md2".equals(mdAlg)) { + mdAlg = "MD2"; + } else if ("http://www.w3.org/2001/04/xmldsig-more#md5".equals(mdAlg)) { + mdAlg = "MD5"; + } else if ("http://www.w3.org/2001/04/xmlenc#ripemd160".equals(mdAlg)) { + mdAlg = "RIPEMD160"; + } else { + throw new NoSuchAlgorithmException("Failed to verify digest value: unsupported digest algorithm " + mdAlg); + } + + MessageDigest md; + try { + md = MessageDigest.getInstance(mdAlg); + } catch (CryptoException e) { + throw new NoSuchAlgorithmException(e); + } + return md.digest(hashDataInput); + } + } -- cgit v1.2.3