From def78ba300a5ca23db35ac51df7ef815038df5be Mon Sep 17 00:00:00 2001 From: shubhamCS03 Date: Thu, 23 Mar 2023 13:03:42 +0530 Subject: [PATCH] ZCS-13176:Mail recall zimlet message verfication header --- .../common/account/ZAttrProvisioning.java | 8 ++ .../zimbra/common/soap/AdminConstants.java | 6 ++ soap/src/java/com/zimbra/soap/JaxbUtil.java | 7 +- .../message/GenerateSecretKeyRequest.java | 39 ++++++++ .../message/GenerateSecretKeyResponse.java | 34 +++++++ store/conf/attrs/zimbra-attrs.xml | 5 +- .../com/zimbra/cs/account/ZAttrConfig.java | 72 ++++++++++++++ .../callback/GenerateSecretKeyCallback.java | 53 ++++++++++ .../com/zimbra/cs/mailbox/MailSender.java | 33 +++---- .../zimbra/cs/service/admin/AdminService.java | 3 + .../cs/service/admin/GenerateSecretKey.java | 56 +++++++++++ .../com/zimbra/cs/service/util/SecretKey.java | 96 +++++++++++++++++++ 12 files changed, 391 insertions(+), 21 deletions(-) create mode 100644 soap/src/java/com/zimbra/soap/admin/message/GenerateSecretKeyRequest.java create mode 100644 soap/src/java/com/zimbra/soap/admin/message/GenerateSecretKeyResponse.java create mode 100644 store/src/java/com/zimbra/cs/account/callback/GenerateSecretKeyCallback.java create mode 100644 store/src/java/com/zimbra/cs/service/admin/GenerateSecretKey.java create mode 100644 store/src/java/com/zimbra/cs/service/util/SecretKey.java diff --git a/common/src/java/com/zimbra/common/account/ZAttrProvisioning.java b/common/src/java/com/zimbra/common/account/ZAttrProvisioning.java index 946ae8b4f2c..8f159f5b163 100644 --- a/common/src/java/com/zimbra/common/account/ZAttrProvisioning.java +++ b/common/src/java/com/zimbra/common/account/ZAttrProvisioning.java @@ -7149,6 +7149,14 @@ public static TwoFactorAuthSecretEncoding fromString(String s) throws ServiceExc @ZAttr(id=4094) public static final String A_zimbraFeatureMailRecallEnabled = "zimbraFeatureMailRecallEnabled"; + /** + * Secret key used in Mail Recall to make it more secure from spoof. + * + * @since ZCS 10.0.0 + */ + @ZAttr(id=4097) + public static final String A_zimbraFeatureMailRecallSecretKey = "zimbraFeatureMailRecallSecretKey"; + /** * Time(in minutes) within which a message can be recalled. The default * time is 30 minutes and accepts value from 1 to 30. diff --git a/common/src/java/com/zimbra/common/soap/AdminConstants.java b/common/src/java/com/zimbra/common/soap/AdminConstants.java index 37d62669ff3..f98c93add60 100644 --- a/common/src/java/com/zimbra/common/soap/AdminConstants.java +++ b/common/src/java/com/zimbra/common/soap/AdminConstants.java @@ -1610,6 +1610,12 @@ public final class AdminConstants { public static final QName VALIDATE_S3_BUCKET_REACHABLE_REQUEST = QName.get(E_VALIDATE_S3_BUCKET_REACHABLE_REQUEST, NAMESPACE); public static final QName VALIDATE_S3_BUCKET_REACHABLE_RESPONSE = QName.get(E_VALIDATE_S3_BUCKET_REACHABLE_RESPONSE, NAMESPACE); + // Secret key for mail recall + public static final String E_GENERATE_SECRET_KEY_REQUEST = "GenerateSecretKeyRequest"; + public static final String E_GENERATE_SECRET_KEY_RESPONSE = "GenerateSecretKeyResponse"; + public static final QName GENERATE_SECRET_KEY_REQUEST = QName.get(E_GENERATE_SECRET_KEY_REQUEST, NAMESPACE); + public static final QName GENERATE_SECRET_KEY_RESPONSE = QName.get(E_GENERATE_SECRET_KEY_RESPONSE, NAMESPACE); + // Removed Zetras zimlet package list public static final List ZEXTRAS_PACKAGES_LIST = Arrays.asList("com_ng_auth", "com_zextras_zextras", "com_zextras_client", "com_zimbra_connect_classic", "com_zimbra_connect_modern", "com_zextras_docs", diff --git a/soap/src/java/com/zimbra/soap/JaxbUtil.java b/soap/src/java/com/zimbra/soap/JaxbUtil.java index 12f3b435bfd..76aa2269fcf 100644 --- a/soap/src/java/com/zimbra/soap/JaxbUtil.java +++ b/soap/src/java/com/zimbra/soap/JaxbUtil.java @@ -30,6 +30,8 @@ import javax.xml.bind.Unmarshaller; import javax.xml.namespace.QName; +import com.zimbra.soap.admin.message.GenerateSecretKeyRequest; +import com.zimbra.soap.admin.message.GenerateSecretKeyResponse; import org.dom4j.Document; import org.dom4j.Namespace; import org.dom4j.io.DocumentResult; @@ -1167,7 +1169,10 @@ public final class JaxbUtil { com.zimbra.soap.admin.message.ValidateS3BucketReachableRequest.class, com.zimbra.soap.admin.message.ValidateS3BucketReachableResponse.class, com.zimbra.soap.admin.message.EditS3BucketConfigRequest.class, - com.zimbra.soap.admin.message.EditS3BucketConfigResponse.class + com.zimbra.soap.admin.message.EditS3BucketConfigResponse.class, + com.zimbra.soap.admin.message.GenerateSecretKeyRequest.class, + com.zimbra.soap.admin.message.GenerateSecretKeyResponse.class + }; try { diff --git a/soap/src/java/com/zimbra/soap/admin/message/GenerateSecretKeyRequest.java b/soap/src/java/com/zimbra/soap/admin/message/GenerateSecretKeyRequest.java new file mode 100644 index 00000000000..fb4d47fa420 --- /dev/null +++ b/soap/src/java/com/zimbra/soap/admin/message/GenerateSecretKeyRequest.java @@ -0,0 +1,39 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Zimbra Collaboration Suite Server + * Copyright (C) 2023 Synacor, Inc. + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software Foundation, + * version 2 of the License. + * + * This program 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 General Public License for more details. + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + * ***** END LICENSE BLOCK ***** + */ + +package com.zimbra.soap.admin.message; + +import com.zimbra.common.soap.AdminConstants; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +/** + * @zm-api-command-auth-required true + * @zm-api-command-admin-auth-required true + * @zm-api-command-description Create random secret key and update it to LDAP attribute. + */ +@XmlAccessorType(XmlAccessType.NONE) +@XmlRootElement(name = AdminConstants.E_GENERATE_SECRET_KEY_REQUEST) +@XmlType(propOrder = {}) +public class GenerateSecretKeyRequest { + + public GenerateSecretKeyRequest() { + } +} diff --git a/soap/src/java/com/zimbra/soap/admin/message/GenerateSecretKeyResponse.java b/soap/src/java/com/zimbra/soap/admin/message/GenerateSecretKeyResponse.java new file mode 100644 index 00000000000..2275a516a6e --- /dev/null +++ b/soap/src/java/com/zimbra/soap/admin/message/GenerateSecretKeyResponse.java @@ -0,0 +1,34 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Zimbra Collaboration Suite Server + * Copyright (C) 2023 Synacor, Inc. + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software Foundation, + * version 2 of the License. + * + * This program 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 General Public License for more details. + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + * ***** END LICENSE BLOCK ***** + */ + +package com.zimbra.soap.admin.message; + +import com.zimbra.common.soap.AdminConstants; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +@XmlAccessorType(XmlAccessType.NONE) +@XmlRootElement(name = AdminConstants.E_GENERATE_SECRET_KEY_RESPONSE) +@XmlType(propOrder = {}) +public class GenerateSecretKeyResponse { + + public GenerateSecretKeyResponse() { + } +} diff --git a/store/conf/attrs/zimbra-attrs.xml b/store/conf/attrs/zimbra-attrs.xml index 49026c10157..9cdaee722ab 100755 --- a/store/conf/attrs/zimbra-attrs.xml +++ b/store/conf/attrs/zimbra-attrs.xml @@ -10320,7 +10320,7 @@ TODO: delete them permanently from here Can be used by an external service to indicate that a trial is set to convert to active at expiration. - + FALSE FALSE Enables the mail recall functionality @@ -10369,4 +10369,7 @@ TODO: delete them permanently from here TRUE whether to enable CountObjectsRequest with userAccount/account type on Zimbra Admin Console + + Secret key used in Mail Recall to make it more secure from spoof. + diff --git a/store/src/java/com/zimbra/cs/account/ZAttrConfig.java b/store/src/java/com/zimbra/cs/account/ZAttrConfig.java index 5279d14c6c0..5bfc8fd4bdc 100644 --- a/store/src/java/com/zimbra/cs/account/ZAttrConfig.java +++ b/store/src/java/com/zimbra/cs/account/ZAttrConfig.java @@ -18669,6 +18669,78 @@ public Map unsetFeatureMailRecallEnabled(Map attrs return attrs; } + /** + * Secret key used in Mail Recall to make it more secure from spoof. + * + * @return zimbraFeatureMailRecallSecretKey, or null if unset + * + * @since ZCS 10.0.0 + */ + @ZAttr(id=4097) + public String getFeatureMailRecallSecretKey() { + return getAttr(Provisioning.A_zimbraFeatureMailRecallSecretKey, null, true); + } + + /** + * Secret key used in Mail Recall to make it more secure from spoof. + * + * @param zimbraFeatureMailRecallSecretKey new value + * @throws com.zimbra.common.service.ServiceException if error during update + * + * @since ZCS 10.0.0 + */ + @ZAttr(id=4097) + public void setFeatureMailRecallSecretKey(String zimbraFeatureMailRecallSecretKey) throws com.zimbra.common.service.ServiceException { + HashMap attrs = new HashMap(); + attrs.put(Provisioning.A_zimbraFeatureMailRecallSecretKey, zimbraFeatureMailRecallSecretKey); + getProvisioning().modifyAttrs(this, attrs); + } + + /** + * Secret key used in Mail Recall to make it more secure from spoof. + * + * @param zimbraFeatureMailRecallSecretKey new value + * @param attrs existing map to populate, or null to create a new map + * @return populated map to pass into Provisioning.modifyAttrs + * + * @since ZCS 10.0.0 + */ + @ZAttr(id=4097) + public Map setFeatureMailRecallSecretKey(String zimbraFeatureMailRecallSecretKey, Map attrs) { + if (attrs == null) attrs = new HashMap(); + attrs.put(Provisioning.A_zimbraFeatureMailRecallSecretKey, zimbraFeatureMailRecallSecretKey); + return attrs; + } + + /** + * Secret key used in Mail Recall to make it more secure from spoof. + * + * @throws com.zimbra.common.service.ServiceException if error during update + * + * @since ZCS 10.0.0 + */ + @ZAttr(id=4097) + public void unsetFeatureMailRecallSecretKey() throws com.zimbra.common.service.ServiceException { + HashMap attrs = new HashMap(); + attrs.put(Provisioning.A_zimbraFeatureMailRecallSecretKey, ""); + getProvisioning().modifyAttrs(this, attrs); + } + + /** + * Secret key used in Mail Recall to make it more secure from spoof. + * + * @param attrs existing map to populate, or null to create a new map + * @return populated map to pass into Provisioning.modifyAttrs + * + * @since ZCS 10.0.0 + */ + @ZAttr(id=4097) + public Map unsetFeatureMailRecallSecretKey(Map attrs) { + if (attrs == null) attrs = new HashMap(); + attrs.put(Provisioning.A_zimbraFeatureMailRecallSecretKey, ""); + return attrs; + } + /** * Time(in minutes) within which a message can be recalled. The default * time is 30 minutes and accepts value from 1 to 30. diff --git a/store/src/java/com/zimbra/cs/account/callback/GenerateSecretKeyCallback.java b/store/src/java/com/zimbra/cs/account/callback/GenerateSecretKeyCallback.java new file mode 100644 index 00000000000..b060c7e7545 --- /dev/null +++ b/store/src/java/com/zimbra/cs/account/callback/GenerateSecretKeyCallback.java @@ -0,0 +1,53 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Zimbra Collaboration Suite Server + * Copyright (C) 2023 Synacor, Inc. + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software Foundation, + * version 2 of the License. + * + * This program 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 General Public License for more details. + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + * ***** END LICENSE BLOCK ***** + */ + +package com.zimbra.cs.account.callback; + +import com.google.common.base.Strings; +import com.zimbra.common.service.ServiceException; +import com.zimbra.cs.account.AttributeCallback; +import com.zimbra.cs.account.Entry; +import com.zimbra.cs.account.Provisioning; +import com.zimbra.cs.account.soap.SoapProvisioning; +import com.zimbra.soap.admin.message.GenerateSecretKeyRequest; + +import java.util.Map; + +public class GenerateSecretKeyCallback extends AttributeCallback { + + @Override + public void preModify(CallbackContext context, String attrName, Object value, + Map attrsToModify, Entry entry) throws ServiceException { + String secretKey = null; + try { + if(value.equals("TRUE")) { + secretKey = Provisioning.getInstance().getConfig().getFeatureMailRecallSecretKey(); + if (Strings.isNullOrEmpty(secretKey)) { + SoapProvisioning sp = SoapProvisioning.getAdminInstance(); + sp.invokeJaxb(new GenerateSecretKeyRequest()); + } + } + } catch (ServiceException e) { + throw ServiceException.FAILURE("Unable to initialize SecureRandom for mail recall", e); + } + } + + @Override + public void postModify(CallbackContext context, String attrName, Entry entry) { + } + +} diff --git a/store/src/java/com/zimbra/cs/mailbox/MailSender.java b/store/src/java/com/zimbra/cs/mailbox/MailSender.java index a2077a23d5a..415210ff921 100644 --- a/store/src/java/com/zimbra/cs/mailbox/MailSender.java +++ b/store/src/java/com/zimbra/cs/mailbox/MailSender.java @@ -22,17 +22,7 @@ import java.io.InputStream; import java.net.SocketException; import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import javax.mail.Address; @@ -41,6 +31,7 @@ import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; +import javax.mail.internet.MailDateFormat; import javax.mail.internet.MimeMessage; import com.google.common.base.Joiner; @@ -61,13 +52,7 @@ import com.zimbra.common.util.StringUtil; import com.zimbra.common.util.SystemUtil; import com.zimbra.common.util.ZimbraLog; -import com.zimbra.cs.account.AccessManager; -import com.zimbra.cs.account.Account; -import com.zimbra.cs.account.AuthToken; -import com.zimbra.cs.account.DataSource; -import com.zimbra.cs.account.Domain; -import com.zimbra.cs.account.Identity; -import com.zimbra.cs.account.Provisioning; +import com.zimbra.cs.account.*; import com.zimbra.cs.extension.ZimbraExtensionNotification; import com.zimbra.cs.filter.RuleManager; import com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException; @@ -81,6 +66,7 @@ import com.zimbra.cs.service.FileUploadServlet.Upload; import com.zimbra.cs.service.UserServlet; import com.zimbra.cs.service.util.ItemId; +import com.zimbra.cs.service.util.SecretKey; import com.zimbra.cs.util.AccountUtil; import com.zimbra.cs.util.AccountUtil.AccountAddressMatcher; import com.zimbra.cs.util.BuildInfo; @@ -91,6 +77,8 @@ public class MailSender { public static final String MSGTYPE_REPLY = String.valueOf(Flag.toChar(Flag.ID_REPLIED)); public static final String MSGTYPE_FORWARD = String.valueOf(Flag.toChar(Flag.ID_FORWARDED)); private static Map mPreSendMailListeners = new ConcurrentHashMap(); + private static final MailDateFormat mailDateFormat = new MailDateFormat(); + private static final String X_MESSAGE_VERIFICATION = "X-Zimbra-Message-Verification"; private Boolean mSaveToSent; private Collection mUploads; @@ -1015,7 +1003,12 @@ void updateHeaders(MimeMessage mm, Account acct, Account authuser, OperationCont mm.setFrom(from); mm.setSender(sender); - mm.setSentDate(new Date()); + Date date = new Date(); + mm.setSentDate(date); + + String value = SecretKey.getMessageVerificationHeaderValue(mm.getMessageID(), mailDateFormat.format(date), from.getAddress()); + mm.addHeader(X_MESSAGE_VERIFICATION, value); + if (sender == null) { Address[] existingReplyTos = mm.getReplyTo(); if (existingReplyTos == null || existingReplyTos.length == 0) { @@ -1478,4 +1471,6 @@ public static void unregisterPreSendMailListener(PreSendMailListener listener) { } } } + + } diff --git a/store/src/java/com/zimbra/cs/service/admin/AdminService.java b/store/src/java/com/zimbra/cs/service/admin/AdminService.java index 756e18d91d8..86a2ae4f1db 100644 --- a/store/src/java/com/zimbra/cs/service/admin/AdminService.java +++ b/store/src/java/com/zimbra/cs/service/admin/AdminService.java @@ -310,6 +310,9 @@ public void registerHandlers(DocumentDispatcher dispatcher) { // ContactBackup API dispatcher.registerHandler(AdminConstants.CONTACT_BACKUP_REQUEST, new ContactBackup()); + + // GetSecretKey API + dispatcher.registerHandler(AdminConstants.GENERATE_SECRET_KEY_REQUEST, new GenerateSecretKey()); } /** diff --git a/store/src/java/com/zimbra/cs/service/admin/GenerateSecretKey.java b/store/src/java/com/zimbra/cs/service/admin/GenerateSecretKey.java new file mode 100644 index 00000000000..7d46c529bb4 --- /dev/null +++ b/store/src/java/com/zimbra/cs/service/admin/GenerateSecretKey.java @@ -0,0 +1,56 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Zimbra Collaboration Suite Server + * Copyright (C) 2023 Synacor, Inc. + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software Foundation, + * version 2 of the License. + * + * This program 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 General Public License for more details. + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + * ***** END LICENSE BLOCK ***** + */ + +package com.zimbra.cs.service.admin; + +import com.zimbra.common.service.ServiceException; +import com.zimbra.common.soap.AdminConstants; +import com.zimbra.common.soap.Element; +import com.zimbra.common.util.ZimbraLog; +import com.zimbra.cs.account.Provisioning; +import com.zimbra.cs.account.soap.SoapProvisioning; +import com.zimbra.cs.service.util.SecretKey; +import com.zimbra.soap.ZimbraSoapContext; +import com.zimbra.soap.admin.type.CacheEntryType; + +import java.util.Map; + +public class GenerateSecretKey extends AdminDocumentHandler { + public Element handle(Element request, Map context) throws ServiceException { + ZimbraSoapContext zsc = getZimbraSoapContext(context); + + String randomString = SecretKey.generateRandomString(); + Provisioning.getInstance().getConfig().setFeatureMailRecallSecretKey(randomString); + flushCache(); + + Element response = zsc.createElement(AdminConstants.GENERATE_SECRET_KEY_RESPONSE); + return response; + } + + private void flushCache() { + try { + SoapProvisioning sp = new SoapProvisioning(); + sp.soapSetURI(SoapProvisioning.getLocalConfigURI()); + sp.soapZimbraAdminAuthenticate(); + sp.flushCache(CacheEntryType.config.toString(), null, true); + } catch (ServiceException e) { + ZimbraLog.misc.warn("Encountered exception during FlushCache after creating Secret Key", e); + } + } + +} + diff --git a/store/src/java/com/zimbra/cs/service/util/SecretKey.java b/store/src/java/com/zimbra/cs/service/util/SecretKey.java new file mode 100644 index 00000000000..7edd81c9b20 --- /dev/null +++ b/store/src/java/com/zimbra/cs/service/util/SecretKey.java @@ -0,0 +1,96 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Zimbra Collaboration Suite Server + * Copyright (C) 2023 Synacor, Inc. + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software Foundation, + * version 2 of the License. + * + * This program 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 General Public License for more details. + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + * ***** END LICENSE BLOCK ***** + */ + +package com.zimbra.cs.service.util; + +import com.zimbra.common.service.ServiceException; +import com.zimbra.cs.account.Provisioning; +import org.apache.commons.codec.binary.Hex; + +import javax.mail.MessagingException; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Base64; + +public class SecretKey { + + public static final int KEY_SIZE_BYTES = 32; + public static final String MSGVRFY_HEADER_PREFIX = "hash=SHA256;guid="; + public static final String MSGVRFY_ALGORITHM_NAME = "SHA-256"; + + /** + * returns the randomly generated String + * + * @return randomly generated String + * @throws ServiceException if an error occurred + */ + public static String generateRandomString() throws ServiceException { + try { + SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); + byte[] key = new byte[KEY_SIZE_BYTES]; + random.nextBytes(key); + return new String(Hex.encodeHex(key)); + } catch (NoSuchAlgorithmException e) { + throw ServiceException.FAILURE("Unable to initialize SecureRandom for mail recall", e); + } + } + + /** + * provide the Hash for Message-Verification header field. + * + * @param id + * @param date + * @param from + * @throws ServiceException + */ + public static String getMessageVerificationHeaderValue(String id, String date, String from) throws MessagingException, ServiceException { + String secretKey = Provisioning.getInstance().getConfig().getFeatureMailRecallSecretKey(); + String guid = (id + date + from + secretKey); + String guidHash = getHashForMessageVerification(guid); + String hash = MSGVRFY_HEADER_PREFIX + guidHash; + return hash; + } + + /** + * Create a digest of the given input with the given algorithm. + * + * @param input + * @throws ServiceException + */ + private static String getHashForMessageVerification(String input) throws ServiceException { + try { + MessageDigest md = MessageDigest.getInstance(MSGVRFY_ALGORITHM_NAME); + byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8)); + BigInteger number = new BigInteger(1, messageDigest); + StringBuilder hexString = new StringBuilder(number.toString(16)); + + while (hexString.length() < 64) { + hexString.insert(0, '0'); + } + + String basicBase64format + = Base64.getEncoder() + .encodeToString(hexString.toString().getBytes()); + return basicBase64format; + } catch (NoSuchAlgorithmException e) { + throw ServiceException.FAILURE("Unable to encrypt", e); + } + } +}