/* * eID Applet Project. * Copyright (C) 2008-2013 FedICT. * * 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.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.net.InetAddress; 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.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.crypto.Cipher; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.x509.DigestInfo; import org.bouncycastle.util.encoders.Hex; import be.fedict.eid.applet.service.Address; import be.fedict.eid.applet.service.EIdCertsData; import be.fedict.eid.applet.service.EIdData; import be.fedict.eid.applet.service.Identity; import be.fedict.eid.applet.service.impl.AuthenticationChallenge; import be.fedict.eid.applet.service.impl.AuthenticationSignatureContextImpl; import be.fedict.eid.applet.service.impl.RequestContext; import be.fedict.eid.applet.service.impl.ServiceLocator; import be.fedict.eid.applet.service.impl.UserIdentifierUtil; import be.fedict.eid.applet.service.impl.tlv.TlvParser; import be.fedict.eid.applet.service.spi.AuditService; import be.fedict.eid.applet.service.spi.AuthenticationService; import be.fedict.eid.applet.service.spi.AuthenticationSignatureContext; import be.fedict.eid.applet.service.spi.AuthenticationSignatureService; import be.fedict.eid.applet.service.spi.CertificateSecurityException; import be.fedict.eid.applet.service.spi.ChannelBindingService; import be.fedict.eid.applet.service.spi.ExpiredCertificateSecurityException; import be.fedict.eid.applet.service.spi.IdentityIntegrityService; import be.fedict.eid.applet.service.spi.PreSignResult; import be.fedict.eid.applet.service.spi.RevokedCertificateSecurityException; import be.fedict.eid.applet.service.spi.TrustCertificateSecurityException; import be.fedict.eid.applet.shared.AuthSignRequestMessage; import be.fedict.eid.applet.shared.AuthenticationContract; import be.fedict.eid.applet.shared.AuthenticationDataMessage; import be.fedict.eid.applet.shared.ErrorCode; import be.fedict.eid.applet.shared.FinishedMessage; /** * Authentication data message protocol handler. * * @author Frank Cornelis * */ @HandlesMessage(AuthenticationDataMessage.class) public class AuthenticationDataMessageHandler implements MessageHandler<AuthenticationDataMessage> { public static final String AUTHENTICATED_USER_IDENTIFIER_SESSION_ATTRIBUTE = "eid.identifier"; public static String PLAIN_TEXT_DIGEST_ALGO_OID = "2.16.56.1.2.1.3.1"; private static final Log LOG = LogFactory.getLog(AuthenticationDataMessageHandler.class); @InitParam(AuthenticationDataMessageHandler.AUTHN_SERVICE_INIT_PARAM_NAME) private ServiceLocator<AuthenticationService> authenticationServiceLocator; @InitParam(AuthenticationDataMessageHandler.AUDIT_SERVICE_INIT_PARAM_NAME) private ServiceLocator<AuditService> auditServiceLocator; @InitParam(HelloMessageHandler.CHANNEL_BINDING_SERVICE) private ServiceLocator<ChannelBindingService> channelBindingServiceLocator; @InitParam(HelloMessageHandler.HOSTNAME_INIT_PARAM_NAME) private String hostname; @InitParam(HelloMessageHandler.INET_ADDRESS_INIT_PARAM_NAME) private InetAddress inetAddress; @InitParam(CHALLENGE_MAX_MATURITY_INIT_PARAM_NAME) private Long maxMaturity; private X509Certificate serverCertificate; @InitParam(HelloMessageHandler.SESSION_ID_CHANNEL_BINDING_INIT_PARAM_NAME) private boolean sessionIdChannelBinding; public static final String AUTHN_SERVICE_INIT_PARAM_NAME = "AuthenticationService"; public static final String AUTHN_SIGNATURE_SERVICE_INIT_PARAM_NAME = "AuthenticationSignatureService"; public static final String AUDIT_SERVICE_INIT_PARAM_NAME = "AuditService"; public static final String CHALLENGE_MAX_MATURITY_INIT_PARAM_NAME = "ChallengeMaxMaturity"; public static final String NRCID_SECRET_INIT_PARAM_NAME = "NRCIDSecret"; public static final String NRCID_ORG_ID_INIT_PARAM_NAME = "NRCIDOrgId"; public static final String NRCID_APP_ID_INIT_PARAM_NAME = "NRCIDAppId"; @InitParam(NRCID_SECRET_INIT_PARAM_NAME) private String nrcidSecret; @InitParam(NRCID_ORG_ID_INIT_PARAM_NAME) private String nrcidOrgId; @InitParam(NRCID_APP_ID_INIT_PARAM_NAME) private String nrcidAppId; @InitParam(HelloMessageHandler.IDENTITY_INTEGRITY_SERVICE_INIT_PARAM_NAME) private ServiceLocator<IdentityIntegrityService> identityIntegrityServiceLocator; @InitParam(IdentityDataMessageHandler.INCLUDE_DATA_FILES) private boolean includeDataFiles; @InitParam(AUTHN_SIGNATURE_SERVICE_INIT_PARAM_NAME) private ServiceLocator<AuthenticationSignatureService> authenticationSignatureServiceLocator; public Object handleMessage(AuthenticationDataMessage message, Map<String, String> httpHeaders, HttpServletRequest request, HttpSession session) throws ServletException { LOG.debug("authentication data message received"); if (null == message.authnCert) { /* * Can be the case for future (Kids) eID cards that have some * certificates missing. */ String msg = "authentication certificate not present"; LOG.warn(msg); throw new ServletException(msg); } byte[] signatureValue = message.signatureValue; LOG.debug("authn signing certificate subject: " + message.authnCert.getSubjectX500Principal()); PublicKey signingKey = message.authnCert.getPublicKey(); if (this.sessionIdChannelBinding) { checkSessionIdChannelBinding(message, request); if (null == this.serverCertificate) { LOG.warn("adviced to use in combination with server certificate channel binding"); } } ChannelBindingService channelBindingService = this.channelBindingServiceLocator.locateService(); if (null != this.serverCertificate || null != channelBindingService) { LOG.debug("using server certificate channel binding"); } if (false == this.sessionIdChannelBinding && null == this.serverCertificate && null == channelBindingService) { LOG.warn("not using any secure channel binding"); } byte[] challenge; try { challenge = AuthenticationChallenge.getAuthnChallenge(session, this.maxMaturity); } catch (SecurityException e) { AuditService auditService = this.auditServiceLocator.locateService(); if (null != auditService) { String remoteAddress = request.getRemoteAddr(); auditService.authenticationError(remoteAddress, message.authnCert); } throw new ServletException("security error: " + e.getMessage(), e); } byte[] serverCertificateClientPOV = null; try { if (null != message.serverCertificate) { serverCertificateClientPOV = message.serverCertificate.getEncoded(); } } catch (CertificateEncodingException e) { throw new ServletException("server cert decoding error: " + e.getMessage(), e); } /* * We validate the authentication contract using the client-side * communicated server SSL certificate in case of secure channel * binding. */ AuthenticationContract authenticationContract = new AuthenticationContract(message.saltValue, this.hostname, this.inetAddress, message.sessionId, serverCertificateClientPOV, challenge); byte[] toBeSigned; try { toBeSigned = authenticationContract.calculateToBeSigned(); } catch (IOException e) { throw new ServletException("IO error: " + e.getMessage(), e); } try { Signature signature = Signature.getInstance("SHA1withRSA"); signature.initVerify(signingKey); signature.update(toBeSigned); boolean result = signature.verify(signatureValue); if (false == result) { AuditService auditService = this.auditServiceLocator.locateService(); if (null != auditService) { String remoteAddress = request.getRemoteAddr(); auditService.authenticationError(remoteAddress, message.authnCert); } throw new SecurityException("authn signature incorrect"); } } catch (NoSuchAlgorithmException e) { throw new SecurityException("algo error"); } catch (InvalidKeyException e) { throw new SecurityException("authn key error"); } catch (SignatureException e) { throw new SecurityException("signature error"); } RequestContext requestContext = new RequestContext(session); String transactionMessage = requestContext.getTransactionMessage(); if (null != transactionMessage) { LOG.debug("verifying TransactionMessage signature"); byte[] transactionMessageSignature = message.transactionMessageSignature; if (null == transactionMessageSignature) { throw new SecurityException("missing TransactionMessage signature"); } try { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, signingKey); byte[] signatureDigestInfoValue = cipher.doFinal(transactionMessageSignature); ASN1InputStream aIn = new ASN1InputStream(signatureDigestInfoValue); DigestInfo signatureDigestInfo = new DigestInfo((ASN1Sequence) aIn.readObject()); if (false == PLAIN_TEXT_DIGEST_ALGO_OID .equals(signatureDigestInfo.getAlgorithmId().getObjectId().getId())) { throw new SecurityException("TransactionMessage signature algo OID incorrect"); } if (false == Arrays.equals(transactionMessage.getBytes(), signatureDigestInfo.getDigest())) { throw new SecurityException("signed TransactionMessage incorrect"); } LOG.debug("TransactionMessage signature validated"); } catch (Exception e) { LOG.error("error verifying TransactionMessage signature", e); AuditService auditService = this.auditServiceLocator.locateService(); if (null != auditService) { String remoteAddress = request.getRemoteAddr(); auditService.authenticationError(remoteAddress, message.authnCert); } throw new SecurityException("error verifying TransactionMessage signature: " + e.getMessage()); } } /* * Secure channel binding verification. */ if (null != channelBindingService) { X509Certificate serverCertificate = channelBindingService.getServerCertificate(); if (null == serverCertificate) { LOG.warn("could not verify secure channel binding as the server does not know its identity yet"); } else { if (false == serverCertificate.equals(message.serverCertificate)) { AuditService auditService = this.auditServiceLocator.locateService(); if (null != auditService) { String remoteAddress = request.getRemoteAddr(); auditService.authenticationError(remoteAddress, message.authnCert); } throw new SecurityException("secure channel binding identity mismatch"); } LOG.debug("secure channel binding verified"); } } else { if (null != this.serverCertificate) { if (false == this.serverCertificate.equals(message.serverCertificate)) { AuditService auditService = this.auditServiceLocator.locateService(); if (null != auditService) { String remoteAddress = request.getRemoteAddr(); auditService.authenticationError(remoteAddress, message.authnCert); } throw new SecurityException("secure channel binding identity mismatch"); } LOG.debug("secure channel binding verified"); } } AuthenticationService authenticationService = this.authenticationServiceLocator.locateService(); List<X509Certificate> certificateChain = new LinkedList<X509Certificate>(); certificateChain.add(message.authnCert); certificateChain.add(message.citizenCaCert); certificateChain.add(message.rootCaCert); try { authenticationService.validateCertificateChain(certificateChain); } catch (ExpiredCertificateSecurityException e) { return new FinishedMessage(ErrorCode.CERTIFICATE_EXPIRED); } catch (RevokedCertificateSecurityException e) { return new FinishedMessage(ErrorCode.CERTIFICATE_REVOKED); } catch (TrustCertificateSecurityException e) { return new FinishedMessage(ErrorCode.CERTIFICATE_NOT_TRUSTED); } catch (CertificateSecurityException e) { return new FinishedMessage(ErrorCode.CERTIFICATE); } catch (Exception e) { /* * We don't want to depend on the full JavaEE profile in this * artifact. */ if ("javax.ejb.EJBException".equals(e.getClass().getName())) { Exception exception; try { Method getCausedByExceptionMethod = e.getClass().getMethod("getCausedByException", new Class[] {}); exception = (Exception) getCausedByExceptionMethod.invoke(e, new Object[] {}); } catch (Exception e2) { LOG.debug("error: " + e.getMessage(), e); throw new SecurityException("error retrieving the root cause: " + e2.getMessage()); } if (exception instanceof ExpiredCertificateSecurityException) { return new FinishedMessage(ErrorCode.CERTIFICATE_EXPIRED); } if (exception instanceof RevokedCertificateSecurityException) { return new FinishedMessage(ErrorCode.CERTIFICATE_REVOKED); } if (exception instanceof TrustCertificateSecurityException) { return new FinishedMessage(ErrorCode.CERTIFICATE_NOT_TRUSTED); } if (exception instanceof CertificateSecurityException) { return new FinishedMessage(ErrorCode.CERTIFICATE); } } throw new SecurityException("authn service error: " + e.getMessage()); } String userId = UserIdentifierUtil.getUserId(message.authnCert); LOG.info("authenticated: " + userId + " @ " + request.getRemoteAddr()); if (null != this.nrcidSecret) { userId = UserIdentifierUtil.getNonReversibleCitizenIdentifier(userId, this.nrcidOrgId, this.nrcidAppId, this.nrcidSecret); } /* * Some people state that you cannot use the national register number * without hashing. Problem is that hashing introduces hash collision * problems. The probability is very low, but what if it's your leg * they're cutting of because of a patient mismatch based on the SHA1 of * your national register number? */ /* * Push authenticated used Id into the HTTP session. */ session.setAttribute(AUTHENTICATED_USER_IDENTIFIER_SESSION_ATTRIBUTE, userId); EIdData eidData = (EIdData) session.getAttribute(IdentityDataMessageHandler.EID_SESSION_ATTRIBUTE); if (null == eidData) { eidData = new EIdData(); session.setAttribute(IdentityDataMessageHandler.EID_SESSION_ATTRIBUTE, eidData); } eidData.identifier = userId; AuditService auditService = this.auditServiceLocator.locateService(); if (null != auditService) { auditService.authenticated(userId); } boolean includeIdentity = requestContext.includeIdentity(); boolean includeAddress = requestContext.includeAddress(); boolean includeCertificates = requestContext.includeCertificates(); boolean includePhoto = requestContext.includePhoto(); /* * Also process the identity data in case it was requested. */ if (includeIdentity) { if (null == message.identityData) { throw new ServletException("identity data not included while requested"); } } if (includeAddress) { if (null == message.addressData) { throw new ServletException("address data not included while requested"); } } if (includePhoto) { if (null == message.photoData) { throw new ServletException("photo data not included while requested"); } } IdentityIntegrityService identityIntegrityService = this.identityIntegrityServiceLocator.locateService(); if (null != identityIntegrityService) { if (null == message.rrnCertificate) { throw new ServletException("national registry certificate not included while requested"); } List<X509Certificate> rrnCertificateChain = new LinkedList<X509Certificate>(); rrnCertificateChain.add(message.rrnCertificate); rrnCertificateChain.add(message.rootCaCert); try { identityIntegrityService.checkNationalRegistrationCertificate(rrnCertificateChain); } catch (ExpiredCertificateSecurityException e) { return new FinishedMessage(ErrorCode.CERTIFICATE_EXPIRED); } catch (RevokedCertificateSecurityException e) { return new FinishedMessage(ErrorCode.CERTIFICATE_REVOKED); } catch (TrustCertificateSecurityException e) { return new FinishedMessage(ErrorCode.CERTIFICATE_NOT_TRUSTED); } catch (CertificateSecurityException e) { return new FinishedMessage(ErrorCode.CERTIFICATE); } catch (Exception e) { if ("javax.ejb.EJBException".equals(e.getClass().getName())) { Exception exception; try { Method getCausedByExceptionMethod = e.getClass().getMethod("getCausedByException", new Class[] {}); exception = (Exception) getCausedByExceptionMethod.invoke(e, new Object[] {}); } catch (Exception e2) { LOG.debug("error: " + e.getMessage(), e); throw new SecurityException("error retrieving the root cause: " + e2.getMessage()); } if (exception instanceof ExpiredCertificateSecurityException) { return new FinishedMessage(ErrorCode.CERTIFICATE_EXPIRED); } if (exception instanceof RevokedCertificateSecurityException) { return new FinishedMessage(ErrorCode.CERTIFICATE_REVOKED); } if (exception instanceof TrustCertificateSecurityException) { return new FinishedMessage(ErrorCode.CERTIFICATE_NOT_TRUSTED); } if (exception instanceof CertificateSecurityException) { return new FinishedMessage(ErrorCode.CERTIFICATE); } } throw new SecurityException("error checking the NRN certificate: " + e.getMessage(), e); } PublicKey rrnPublicKey = message.rrnCertificate.getPublicKey(); if (includeIdentity) { if (null == message.identitySignatureData) { throw new ServletException("identity signature data not included while requested"); } verifySignature(message.rrnCertificate.getSigAlgName(), message.identitySignatureData, rrnPublicKey, request, message.identityData); } if (includeAddress) { if (null == message.addressSignatureData) { throw new ServletException("address signature data not included while requested"); } byte[] addressFile = trimRight(message.addressData); verifySignature(message.rrnCertificate.getSigAlgName(), message.addressSignatureData, rrnPublicKey, request, addressFile, message.identitySignatureData); } } if (includeIdentity) { Identity identity = TlvParser.parse(message.identityData, Identity.class); if (false == UserIdentifierUtil.getUserId(message.authnCert).equals(identity.nationalNumber)) { throw new ServletException("national number mismatch"); } session.setAttribute(IdentityDataMessageHandler.IDENTITY_SESSION_ATTRIBUTE, identity); eidData.identity = identity; auditService = this.auditServiceLocator.locateService(); if (null != auditService) { auditService.identified(identity.nationalNumber); } } if (includeAddress) { Address address = TlvParser.parse(message.addressData, Address.class); session.setAttribute(IdentityDataMessageHandler.ADDRESS_SESSION_ATTRIBUTE, address); eidData.address = address; } if (includePhoto) { if (includeIdentity) { byte[] expectedPhotoDigest = eidData.identity.photoDigest; byte[] actualPhotoDigest = digestPhoto(getDigestAlgo(expectedPhotoDigest.length), message.photoData); if (false == Arrays.equals(expectedPhotoDigest, actualPhotoDigest)) { throw new ServletException("photo digest incorrect"); } } session.setAttribute(IdentityDataMessageHandler.PHOTO_SESSION_ATTRIBUTE, message.photoData); eidData.photo = message.photoData; } if (includeCertificates) { if (includeIdentity) { eidData.certs = new EIdCertsData(); eidData.certs.authn = message.authnCert; eidData.certs.ca = message.citizenCaCert; eidData.certs.root = message.rootCaCert; eidData.certs.sign = message.signCert; } session.setAttribute(IdentityDataMessageHandler.AUTHN_CERT_SESSION_ATTRIBUTE, message.authnCert); session.setAttribute(IdentityDataMessageHandler.CA_CERT_SESSION_ATTRIBUTE, message.citizenCaCert); session.setAttribute(IdentityDataMessageHandler.ROOT_CERT_SESSION_ATTRIBTUE, message.rootCaCert); session.setAttribute(IdentityDataMessageHandler.SIGN_CERT_SESSION_ATTRIBUTE, message.signCert); } if (this.includeDataFiles) { session.setAttribute(IdentityDataMessageHandler.EID_DATA_IDENTITY_SESSION_ATTRIBUTE, message.identityData); session.setAttribute(IdentityDataMessageHandler.EID_DATA_ADDRESS_SESSION_ATTRIBUTE, message.addressData); } AuthenticationSignatureService authenticationSignatureService = this.authenticationSignatureServiceLocator .locateService(); if (null != authenticationSignatureService) { List<X509Certificate> authnCertificateChain; if (null != message.authnCert) { authnCertificateChain = new LinkedList<X509Certificate>(); authnCertificateChain.add(message.authnCert); authnCertificateChain.add(message.citizenCaCert); authnCertificateChain.add(message.rootCaCert); } else { authnCertificateChain = null; } AuthenticationSignatureContext authenticationSignatureContext = new AuthenticationSignatureContextImpl( session); PreSignResult preSignResult = authenticationSignatureService.preSign(authnCertificateChain, authenticationSignatureContext); if (null == preSignResult) { return new FinishedMessage(); } boolean logoff = preSignResult.getLogoff(); byte[] computedDigestValue = preSignResult.getDigestInfo().digestValue; String digestAlgo = preSignResult.getDigestInfo().digestAlgo; String authnMessage = preSignResult.getDigestInfo().description; AuthSignRequestMessage authSignRequestMessage = new AuthSignRequestMessage(computedDigestValue, digestAlgo, authnMessage, logoff); return authSignRequestMessage; } return new FinishedMessage(); } 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 byte[] digestPhoto(String digestAlgoName, byte[] photoFile) { MessageDigest messageDigest; try { messageDigest = MessageDigest.getInstance(digestAlgoName); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("SHA error: " + e.getMessage(), e); } byte[] photoDigest = messageDigest.digest(photoFile); return photoDigest; } private String getDigestAlgo(final int hashSize) throws RuntimeException { 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 RuntimeException("Failed to find guess algorithm for hash size of " + hashSize + " bytes"); } private void verifySignature(String signatureAlgo, byte[] signatureData, PublicKey publicKey, HttpServletRequest request, byte[]... data) throws ServletException { Signature signature; try { signature = Signature.getInstance(signatureAlgo); } 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 void checkSessionIdChannelBinding(AuthenticationDataMessage message, HttpServletRequest request) { LOG.debug("using TLS session Id channel binding"); byte[] sessionId = message.sessionId; /* * Next is Tomcat specific. */ String actualSessionId = (String) request.getAttribute("javax.servlet.request.ssl_session"); if (null == actualSessionId) { /* * Servlet specs v3.0 */ actualSessionId = (String) request.getAttribute("javax.servlet.request.ssl_session_id"); } if (null == actualSessionId) { LOG.warn("could not verify the SSL session identifier"); return; } if (false == Arrays.equals(sessionId, Hex.decode(actualSessionId))) { LOG.warn("SSL session Id mismatch"); LOG.debug("signed SSL session Id: " + new String(Hex.encode(sessionId))); LOG.debug("actual SSL session Id: " + actualSessionId); throw new SecurityException("SSL session Id mismatch"); } LOG.debug("SSL session identifier checked"); } public void init(ServletConfig config) throws ServletException { String channelBindingServerCertificate = config .getInitParameter(HelloMessageHandler.CHANNEL_BINDING_SERVER_CERTIFICATE); if (null != channelBindingServerCertificate) { File serverCertificateFile = new File(channelBindingServerCertificate); if (false == serverCertificateFile.exists()) { throw new ServletException("server certificate not found: " + serverCertificateFile); } byte[] encodedServerCertificate; try { encodedServerCertificate = FileUtils.readFileToByteArray(serverCertificateFile); } catch (IOException e) { throw new ServletException("error reading server certificate: " + e.getMessage(), e); } this.serverCertificate = getCertificate(encodedServerCertificate); } } private X509Certificate getCertificate(byte[] certData) { CertificateFactory certificateFactory; try { certificateFactory = CertificateFactory.getInstance("X.509"); } catch (CertificateException e) { throw new RuntimeException("cert factory error: " + e.getMessage(), e); } try { X509Certificate certificate = (X509Certificate) certificateFactory .generateCertificate(new ByteArrayInputStream(certData)); return certificate; } catch (CertificateException e) { throw new RuntimeException("certificate decoding error: " + e.getMessage(), e); } } }