/* * eID Applet Project. * Copyright (C) 2008-2013 FedICT. * Copyright (C) 2014 e-Contract.be BVBA. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version * 3.0 as published by the Free Software Foundation. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, see * http://www.gnu.org/licenses/. */ package be.fedict.eid.applet.service.impl.handler; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import be.fedict.eid.applet.service.Address; import be.fedict.eid.applet.service.Identity; import be.fedict.eid.applet.service.dto.DTOMapper; import be.fedict.eid.applet.service.impl.RequestContext; import be.fedict.eid.applet.service.impl.ServiceLocator; import be.fedict.eid.applet.service.impl.tlv.TlvParser; import be.fedict.eid.applet.service.spi.AddressDTO; import be.fedict.eid.applet.service.spi.AuditService; import be.fedict.eid.applet.service.spi.AuthorizationException; import be.fedict.eid.applet.service.spi.DigestInfo; import be.fedict.eid.applet.service.spi.IdentityDTO; import be.fedict.eid.applet.service.spi.IdentityIntegrityService; import be.fedict.eid.applet.service.spi.IdentityRequest; import be.fedict.eid.applet.service.spi.IdentityService; import be.fedict.eid.applet.service.spi.SignatureService; import be.fedict.eid.applet.shared.ErrorCode; import be.fedict.eid.applet.shared.FinishedMessage; import be.fedict.eid.applet.shared.SignCertificatesDataMessage; import be.fedict.eid.applet.shared.SignRequestMessage; /** * Sign Certificate Data Message Handler. * * @author Frank Cornelis * */ @HandlesMessage(SignCertificatesDataMessage.class) public class SignCertificatesDataMessageHandler implements MessageHandler<SignCertificatesDataMessage> { private static final Log LOG = LogFactory.getLog(SignCertificatesDataMessageHandler.class); @InitParam(HelloMessageHandler.SIGNATURE_SERVICE_INIT_PARAM_NAME) private ServiceLocator<SignatureService> signatureServiceLocator; @InitParam(HelloMessageHandler.REMOVE_CARD_INIT_PARAM_NAME) private boolean removeCard; @InitParam(HelloMessageHandler.LOGOFF_INIT_PARAM_NAME) private boolean logoff; @InitParam(HelloMessageHandler.REQUIRE_SECURE_READER_INIT_PARAM_NAME) private boolean requireSecureReader; @InitParam(HelloMessageHandler.IDENTITY_INTEGRITY_SERVICE_INIT_PARAM_NAME) private ServiceLocator<IdentityIntegrityService> identityIntegrityServiceLocator; @InitParam(AuthenticationDataMessageHandler.AUDIT_SERVICE_INIT_PARAM_NAME) private ServiceLocator<AuditService> auditServiceLocator; @InitParam(HelloMessageHandler.IDENTITY_SERVICE_INIT_PARAM_NAME) private ServiceLocator<IdentityService> identityServiceLocator; public Object handleMessage(SignCertificatesDataMessage message, Map<String, String> httpHeaders, HttpServletRequest request, HttpSession session) throws ServletException { SignatureService signatureService = this.signatureServiceLocator.locateService(); List<X509Certificate> signingCertificateChain = message.certificateChain; X509Certificate signingCertificate = signingCertificateChain.get(0); if (null == signingCertificate) { throw new ServletException("missing non-repudiation certificate"); } LOG.debug("signing certificate: " + signingCertificateChain.get(0).getSubjectX500Principal()); RequestContext requestContext = new RequestContext(session); boolean includeIdentity = requestContext.includeIdentity(); boolean includeAddress = requestContext.includeAddress(); boolean includePhoto = requestContext.includePhoto(); Identity identity = null; Address address = null; if (includeIdentity || includeAddress || includePhoto) { /* * Pre-sign phase including identity data. */ if (includeIdentity) { if (null == message.identityData) { throw new ServletException("identity data missing"); } identity = TlvParser.parse(message.identityData, Identity.class); } if (includeAddress) { if (null == message.addressData) { throw new ServletException("address data missing"); } address = TlvParser.parse(message.addressData, Address.class); } if (includePhoto) { if (null == message.photoData) { throw new ServletException("photo data missing"); } if (null != identity) { byte[] expectedPhotoDigest = identity.photoDigest; byte[] actualPhotoDigest; try { actualPhotoDigest = digestPhoto(getDigestAlgo(expectedPhotoDigest.length), message.photoData); } catch (NoSuchAlgorithmException e) { throw new ServletException("photo signed with unsupported algorithm"); } if (false == Arrays.equals(expectedPhotoDigest, actualPhotoDigest)) { throw new ServletException("photo digest incorrect"); } } } IdentityIntegrityService identityIntegrityService = this.identityIntegrityServiceLocator.locateService(); if (null != identityIntegrityService) { if (null == message.rrnCertificate) { throw new ServletException("national registry certificate not included while requested"); } PublicKey rrnPublicKey = message.rrnCertificate.getPublicKey(); if (null != message.identityData) { if (null == message.identitySignatureData) { throw new ServletException("missing identity data signature"); } verifySignature(message.rrnCertificate.getSigAlgName(), message.identitySignatureData, rrnPublicKey, request, message.identityData); if (null != message.addressData) { if (null == message.addressSignatureData) { throw new ServletException("missing address data signature"); } byte[] addressFile = trimRight(message.addressData); verifySignature(message.rrnCertificate.getSigAlgName(), message.addressSignatureData, rrnPublicKey, request, addressFile, message.identitySignatureData); } } LOG.debug("checking national registration certificate: " + message.rrnCertificate.getSubjectX500Principal()); List<X509Certificate> rrnCertificateChain = new LinkedList<X509Certificate>(); rrnCertificateChain.add(message.rrnCertificate); rrnCertificateChain.add(message.rootCertificate); identityIntegrityService.checkNationalRegistrationCertificate(rrnCertificateChain); } } DigestInfo digestInfo; DTOMapper dtoMapper = new DTOMapper(); IdentityDTO identityDTO = dtoMapper.map(identity, IdentityDTO.class); AddressDTO addressDTO = dtoMapper.map(address, AddressDTO.class); try { digestInfo = signatureService.preSign(null, signingCertificateChain, identityDTO, addressDTO, message.photoData); } catch (NoSuchAlgorithmException e) { throw new ServletException("no such algo: " + e.getMessage(), e); } catch (AuthorizationException e) { return new FinishedMessage(ErrorCode.AUTHORIZATION); } // also save it in the session for later verification SignatureDataMessageHandler.setDigestValue(digestInfo.digestValue, digestInfo.digestAlgo, session); IdentityService identityService = this.identityServiceLocator.locateService(); boolean removeCard; if (null != identityService) { IdentityRequest identityRequest = identityService.getIdentityRequest(); removeCard = identityRequest.removeCard(); } else { removeCard = this.removeCard; } SignRequestMessage signRequestMessage = new SignRequestMessage(digestInfo.digestValue, digestInfo.digestAlgo, digestInfo.description, this.logoff, removeCard, this.requireSecureReader); return signRequestMessage; } public void init(ServletConfig config) throws ServletException { // empty } private byte[] digestPhoto(String digestAlgoName, byte[] photoFile) { MessageDigest messageDigest; try { messageDigest = MessageDigest.getInstance(digestAlgoName); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("digest error: " + e.getMessage(), e); } byte[] photoDigest = messageDigest.digest(photoFile); return photoDigest; } private void verifySignature(String signatureAlgoName, byte[] signatureData, PublicKey publicKey, HttpServletRequest request, byte[]... data) throws ServletException { Signature signature; try { signature = Signature.getInstance(signatureAlgoName); } catch (NoSuchAlgorithmException e) { throw new ServletException("algo error: " + e.getMessage(), e); } try { signature.initVerify(publicKey); } catch (InvalidKeyException e) { throw new ServletException("key error: " + e.getMessage(), e); } try { for (byte[] dataItem : data) { signature.update(dataItem); } boolean result = signature.verify(signatureData); if (false == result) { AuditService auditService = this.auditServiceLocator.locateService(); if (null != auditService) { String remoteAddress = request.getRemoteAddr(); auditService.identityIntegrityError(remoteAddress); } throw new ServletException("signature incorrect"); } } catch (SignatureException e) { throw new ServletException("signature error: " + e.getMessage(), e); } } private byte[] trimRight(byte[] addressFile) { int idx; for (idx = 0; idx < addressFile.length; idx++) { if (0 == addressFile[idx]) { break; } } byte[] result = new byte[idx]; System.arraycopy(addressFile, 0, result, 0, idx); return result; } private String getDigestAlgo(final int hashSize) throws NoSuchAlgorithmException { switch (hashSize) { case 20: return "SHA-1"; case 28: return "SHA-224"; case 32: return "SHA-256"; case 48: return "SHA-384"; case 64: return "SHA-512"; } throw new NoSuchAlgorithmException("Failed to find guess algorithm for hash size of " + hashSize + " bytes"); } }