View Javadoc
1   /* 
2   Copyright (c) 2010, NHIN Direct Project
3   All rights reserved.
4   
5   Authors:
6      Umesh Madan     umeshma@microsoft.com
7      Greg Meyer      gm2552@cerner.com
8    
9   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
10  
11  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
12  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 
13  in the documentation and/or other materials provided with the distribution.  Neither the name of the The NHIN Direct Project (nhindirect.org). 
14  nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
15  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
16  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 
17  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
18  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
19  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
20  THE POSSIBILITY OF SUCH DAMAGE.
21  */
22  
23  package org.nhindirect.gateway.smtp;
24  
25  import java.io.ByteArrayInputStream;
26  import java.io.File;
27  import java.io.InputStream;
28  import java.security.cert.X509Certificate;
29  import java.util.ArrayList;
30  import java.util.Collection;
31  import java.util.Collections;
32  import java.util.HashMap;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.function.Function;
36  import java.util.stream.Collectors;
37  
38  import org.apache.commons.io.IOUtils;
39  import org.apache.commons.lang3.StringUtils;
40  import org.nhind.config.rest.AnchorService;
41  import org.nhind.config.rest.CertPolicyService;
42  import org.nhind.config.rest.CertificateService;
43  import org.nhind.config.rest.DomainService;
44  import org.nhind.config.rest.SettingService;
45  import org.nhind.config.rest.TrustBundleService;
46  import org.nhindirect.common.audit.Auditor;
47  import org.nhindirect.common.crypto.KeyStoreProtectionManager;
48  import org.nhindirect.config.model.Anchor;
49  import org.nhindirect.config.model.CertPolicy;
50  import org.nhindirect.config.model.CertPolicyGroupDomainReltn;
51  import org.nhindirect.config.model.CertPolicyGroupUse;
52  import org.nhindirect.config.model.CertPolicyUse;
53  import org.nhindirect.config.model.Setting;
54  import org.nhindirect.config.model.TrustBundle;
55  import org.nhindirect.config.model.TrustBundleAnchor;
56  import org.nhindirect.config.model.utils.CertUtils;
57  import org.nhindirect.gateway.smtp.config.cert.impl.ConfigServiceRESTCertificateStore;
58  import org.nhindirect.policy.PolicyExpression;
59  import org.nhindirect.policy.PolicyLexicon;
60  import org.nhindirect.policy.PolicyLexiconParser;
61  import org.nhindirect.policy.PolicyLexiconParserFactory;
62  import org.nhindirect.policy.PolicyParseException;
63  import org.nhindirect.stagent.DefaultNHINDAgent;
64  import org.nhindirect.stagent.MutableAgent;
65  import org.nhindirect.stagent.NHINDAgent;
66  import org.nhindirect.stagent.cert.CertificateResolver;
67  import org.nhindirect.stagent.cert.impl.DNSCertificateStore;
68  import org.nhindirect.stagent.cert.impl.EmployLdapAuthInformation;
69  import org.nhindirect.stagent.cert.impl.KeyStoreCertificateStore;
70  import org.nhindirect.stagent.cert.impl.LDAPCertificateStore;
71  import org.nhindirect.stagent.cert.impl.LdapCertificateStoreFactory;
72  import org.nhindirect.stagent.cert.impl.LdapPublicCertUtilImpl;
73  import org.nhindirect.stagent.cert.impl.LdapStoreConfiguration;
74  import org.nhindirect.stagent.cert.impl.TrustAnchorCertificateStore;
75  import org.nhindirect.stagent.cert.impl.UniformCertificateStore;
76  import org.nhindirect.stagent.cryptography.SMIMECryptographerImpl;
77  import org.nhindirect.stagent.policy.PolicyResolver;
78  import org.nhindirect.stagent.policy.impl.DomainPolicyResolver;
79  import org.nhindirect.stagent.trust.DefaultTrustAnchorResolver;
80  import org.nhindirect.stagent.trust.TrustAnchorResolver;
81  import org.nhindirect.stagent.trust.TrustModel;
82  import org.slf4j.Logger;
83  import org.slf4j.LoggerFactory;
84  
85  /**
86   * The SmtpAgentFactory is a bootstrapper for creating instances of the {@link SmtpAgent) based on configuration information.  Configurations
87   * are loaded from a URL that may take the form of any addressable resource such as a file, HTTP resource, LDAP store, or database.  Based on the
88   * URL protocol, an appropriate configuration loader and parser is instantiated which creates an injector used to provide instance of the SmptAgent.
89   * Optionally specific configuration and security and trust agent providers can be passed for specific object creation.  This is generally useful
90   * for creating mock implementations for testing.
91   * @author Greg Meyer
92   *
93   */
94  public class SmtpAgentFactory 
95  {		
96  	
97  	protected static SmtpAgentFactory INSTANCE;
98  	
99  	private static final Logger LOGGER = LoggerFactory.getLogger(SmtpAgentFactory.class);	
100 
101 	protected static final String MESSAGE_SETTING_RAW = "Raw";
102 	protected static final String MESSAGE_SETTING_INCOMING = "Incoming";
103 	protected static final String MESSAGE_SETTING_OUTGOING = "Outgoing";
104 	protected static final String MESSAGE_SETTING_BAD = "Bad";	
105 	
106 	protected static final String ANCHOR_RES_TYPE_UNIFORM = "uniform";
107 	protected static final String ANCHOR_RES_TYPE_MULTIDOMAIN = "multidomain";	
108 	
109 	protected static final String STORE_TYPE_WS = "WS";
110 	protected static final String STORE_TYPE_LDAP = "LDAP";
111 	protected static final String STORE_TYPE_PUBLIC_LDAP = "PublicLDAP";
112 	protected static final String STORE_TYPE_KEYSTORE = "keystore";
113 	protected static final String STORE_TYPE_DNS = "DNS";	
114 	
115 	
116 	protected final CertificateService certService;
117 	protected final TrustBundleService bundleService;
118 	protected final DomainService domainService;
119 	protected final AnchorService anchorService;
120 	protected final SettingService settingService;
121 	protected final CertPolicyService polService;
122 	protected final Auditor auditor;
123 	protected final KeyStoreProtectionManager keyStoreMgr;
124 	
125 	public static SmtpAgentFactory getInstance(CertificateService certService, TrustBundleService bundleService, DomainService domainService,
126 			AnchorService anchorService, SettingService settingService, CertPolicyService polService, Auditor auditor, KeyStoreProtectionManager keyStoreMgr)
127 	{
128 
129 		INSTANCE = new SmtpAgentFactory(certService, bundleService, domainService,
130 					anchorService, settingService, polService, auditor, keyStoreMgr);
131 		
132 		return INSTANCE;
133 	}
134 	
135 	protected SmtpAgentFactory(CertificateService certService, TrustBundleService bundleService, DomainService domainService,
136 			AnchorService anchorService, SettingService settingService, CertPolicyService polService, Auditor auditor, KeyStoreProtectionManager keyStoreMgr)
137 	{
138 		this.certService = certService;
139 		this.bundleService = bundleService;
140 		this.domainService = domainService;
141 		this.anchorService = anchorService;
142 		this.settingService = settingService;
143 		this.polService = polService;
144 		this.auditor = auditor;
145 		this.keyStoreMgr = keyStoreMgr;
146 	}
147 	
148 	public SmtpAgent createSmtpAgent() throws SmtpAgentException
149 	{
150 		// get the settings
151 		final SmtpAgentSettings settings = createSMTPAgentSetting();
152 		
153 		// create the agent
154 		final SmtpAgent retVal = new DefaultSmtpAgent(settings, createNHINDAgent(), auditor);
155 		
156 		return retVal;
157 	}
158 	
159 	public NHINDAgent createNHINDAgent() throws SmtpAgentException
160 	{
161 		final List<String> domains = getDomains();
162 		
163 		final TrustAnchorResolver anchorResover = getTrustAnchorResolver(domains);
164 		
165 		final Collection<CertificateResolver> publicResolvers = getPublicCertResolvers();
166 		
167 		final CertificateResolver privateResolver = getPrivateCertResolver();
168 		
169 		final PolicyResolvers polResolvers = this.getPolicyResolvers();
170 		
171 		final MutableAgent retVal = new DefaultNHINDAgent(domains, privateResolver, publicResolvers, anchorResover, 
172 				TrustModel.Default, SMIMECryptographerImpl.Default);
173 		
174 		retVal.setPrivatePolicyResolver(polResolvers.getPrivateResolver());
175 		retVal.setPublicPolicyResolver(polResolvers.getPublicResolver());
176 		retVal.getTrustModel().setTrustPolicyResolver(polResolvers.getTrustResolver());
177 		
178 		return (NHINDAgent)retVal;
179 	}	
180 	
181 
182 	protected SmtpAgentSettings createSMTPAgentSetting()
183 	{
184 		final MessageProcessingSettings rawSetting = getMessageProcessingSetting(MESSAGE_SETTING_RAW + "MessageSaveFolder");
185 		final MessageProcessingSettings outgoingSettings = getMessageProcessingSetting(MESSAGE_SETTING_OUTGOING + "MessageSaveFolder");
186 		final MessageProcessingSettings incomingSettings = getMessageProcessingSetting(MESSAGE_SETTING_INCOMING + "MessageSaveFolder");
187 		final MessageProcessingSettings badSettings = getMessageProcessingSetting(MESSAGE_SETTING_BAD + "MessageSaveFolder");
188 		
189 		final Setting prodNameSetting = getSafeSetting("MDNProdName");
190 		final Setting textSetting = getSafeSetting("MDNText");
191 		boolean autoResponse = true;  /* MDNs are part of the Direct spec.  This should not be an option */
192 		final String prodName = (prodNameSetting == null) ? "" : prodNameSetting.getValue();
193 		final String text = (textSetting == null) ? "" : textSetting.getValue();
194 		final NotificationProducertificationProducer">NotificationProducer notificationProducer = new NotificationProducer(new NotificationSettings(autoResponse, prodName, text));
195 		
196 		return new SmtpAgentSettings(rawSetting, outgoingSettings,
197 				incomingSettings, badSettings, notificationProducer);
198 				
199 				
200 	}
201 
202 	protected List<String> getDomains()
203 	{
204 		List<String> domains = null;
205 
206 		try
207 		{
208 			domains = domainService.searchDomains("", null).stream().map(domain -> domain.getDomainName())
209 				.collect(Collectors.toList());
210 		}
211 		catch (Exception e)
212 		{
213 			throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "WebService error getting domains list: " + e.getMessage(), e);
214 		}
215 		
216 		if (domains.size() == 0)
217 			throw new SmtpAgentException(SmtpAgentError.MissingDomains);
218 		
219 		return domains;
220 	}
221 	
222 	
223 	protected TrustAnchorResolver getTrustAnchorResolver(List<String> domains) 
224 	{
225 
226 		final Map<String, Collection<X509Certificate>> incomingAnchors = new HashMap<>();
227 		final Map<String, Collection<X509Certificate>> outgoingAnchors = new HashMap<>();
228 		
229 		/* 
230 		 * first determine how anchors are stored... possibilities are LDAP, keystore, and WS
231 		 * 
232 		 */
233 		Setting setting = null;
234 		String storeType;
235 		String resolverType;
236 		try
237 		{
238 			setting = getSafeSetting("AnchorStoreType");
239 		}
240 		catch (Exception e)
241 		{
242 			throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "WebService error getting anchor store type: " + e.getMessage(), e);
243 		}
244 		
245 		if (setting == null || setting.getValue() == null || setting.getValue().isEmpty())
246 			storeType = STORE_TYPE_WS; // default to WS
247 		else
248 			storeType = setting.getValue();		
249 		
250 		if (!storeType.equalsIgnoreCase(STORE_TYPE_WS))
251 		{
252 			getAnchorsFromNonWS(incomingAnchors, outgoingAnchors, storeType, domains);
253 		}				
254 		else
255 		{
256 			Map<String, TrustBundle> bundleMap = null;
257 			
258 			/*
259 			 * Get all the of the trust bundles and put them into a map
260 			 * keyed by the bundle name.  We will use this later
261 			 * when populating each domain's collection of anchors.
262 			 */
263 			try
264 			{
265 				bundleMap = bundleService.getTrustBundles(true).
266 						stream().collect(Collectors.toMap(TrustBundle::getBundleName, Function.identity()));
267 			}
268 			catch (Exception e)
269 			{
270 				throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat,  
271 						"WebService error getting trust bundles: " + e.getMessage(), e);
272 			}
273 	
274 			final Map<String, TrustBundle> finalBundleMap = Collections.unmodifiableMap(bundleMap);
275 			
276 			/*
277 			 * Get all of the anchors in the system in one call.  This is more efficient
278 			 * than making numerous REST and database calls.  
279 			 */
280 			Collection<Anchor> systemAnchors = null;
281 			try
282 			{
283 				systemAnchors = anchorService.getAnchors();
284 			}
285 			catch (Exception e)
286 			{
287 				throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat,  
288 						"WebService error getting anchors: " + e.getMessage(), e);
289 			}
290 			
291 			/*
292 			 * Iterate through all of the domains and start them out with an empty collection.
293 			 * We will fill in the collections as we go in the next sections
294 			 */
295 			for (String domain : domains)
296 			{
297 				incomingAnchors.put(domain, new ArrayList<X509Certificate>());
298 				outgoingAnchors.put(domain, new ArrayList<X509Certificate>());
299 			}
300 
301 			/*
302 			 * Iterate through all anchors and drop them into their
303 			 * appropriate domain collections
304 			 */
305 			systemAnchors.forEach(anchor -> 
306 				{
307 					final X509Certificate anchorToAdd = CertUtils.toX509Certificate(anchor.getCertificateData());
308 					if (anchor.isIncoming())
309 						incomingAnchors.get(anchor.getOwner()).add(anchorToAdd);
310 					if (anchor.isOutgoing())
311 						outgoingAnchors.get(anchor.getOwner()).add(anchorToAdd);					
312 				});
313 			
314 			try
315 			{
316 				/*
317 				 * Get all of the trust bundle to domain relation ships.  We will lookup
318 				 * the anchors in the bundles using the bundle map that we retrieved earlier.
319 				 * Then drop the anchors from each bundle into it appropriate domain collection
320 				 */
321 				bundleService.getAllTrustBundleDomainReltns(false).stream().forEach(domainAssoc ->
322 				{
323 					final TrustBundle bundle = finalBundleMap.get(domainAssoc.getTrustBundle().getBundleName());
324 					if (bundle != null && bundle.getTrustBundleAnchors() != null)
325 					{
326 						for (TrustBundleAnchor anchor : bundle.getTrustBundleAnchors())
327 						{
328 							final X509Certificate anchorToAdd = CertUtils.toX509Certificate(anchor.getAnchorData());
329 							if (domainAssoc.isIncoming())
330 								incomingAnchors.get(domainAssoc.getDomain().getDomainName()).add(anchorToAdd);
331 							if (domainAssoc.isOutgoing())
332 								outgoingAnchors.get(domainAssoc.getDomain().getDomainName()).add(anchorToAdd);
333 						}
334 					}					
335 				});
336 			}
337 			catch (Exception e)
338 			{
339 				throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat,  
340 						"WebService error getting trust bundle/domain relationships: " + e.getMessage(), e);
341 			}
342 					
343 
344 		}
345 		
346 		if (incomingAnchors.size() == 0 && outgoingAnchors.size() == 0)
347 			throw new SmtpAgentException(SmtpAgentError.InvalidTrustAnchorSettings, "No trust anchors defined.");		
348 		
349 		try
350 		{
351 			setting = getSafeSetting("AnchorResolverType");
352 		}
353 		catch (Exception e)
354 		{
355 			throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "WebService error getting anchor resolver type: " + e.getMessage(), e);
356 		}		
357 		
358 		if (setting == null || setting.getValue() == null || setting.getValue().isEmpty())
359 		{
360 			// multi domain should be the default... uniform really only makes sense for dev purposes
361 			resolverType = ANCHOR_RES_TYPE_MULTIDOMAIN; 		
362 		}
363 		else
364 			resolverType = setting.getValue();		
365 		
366 		if (resolverType.equalsIgnoreCase(ANCHOR_RES_TYPE_UNIFORM))
367 		{
368 			// this is uniform... doesn't really matter what we use for incoming or outgoing because in theory they should be
369 			// the same... just get the first collection in the incoming map
370 			final Collection<X509Certificate> anchorsToUse = (incomingAnchors.size() > 0) ? incomingAnchors.values().iterator().next() 
371 					: outgoingAnchors.values().iterator().next();
372 			
373 			return new DefaultTrustAnchorResolver(new UniformCertificateStore(anchorsToUse));
374 		}
375 		else if (resolverType.equalsIgnoreCase(ANCHOR_RES_TYPE_MULTIDOMAIN))
376 		{
377 			final TrustAnchorCertificateStore incomingTrustStore = new TrustAnchorCertificateStore(incomingAnchors);
378 			final TrustAnchorCertificateStore outgoingTrustStore = new TrustAnchorCertificateStore(outgoingAnchors);
379 			
380 			return new DefaultTrustAnchorResolver(outgoingTrustStore, incomingTrustStore);
381 		}
382 		else
383 		{
384 			throw new SmtpAgentException(SmtpAgentError.InvalidTrustAnchorSettings);
385 		}		
386 	}
387 	
388 	protected void getAnchorsFromNonWS(Map<String, Collection<X509Certificate>> incomingAnchors, 
389 			Map<String, Collection<X509Certificate>> outgoingAnchors, String storeType, Collection<String> domains)
390 	{		
391 		
392 
393 		ArrayList<String> incomingLookups = new ArrayList<String>();
394 		ArrayList<String> outgoingLookups = new ArrayList<String>();
395 		for (String domain : domains)
396 		{
397 			incomingLookups.add(domain + "IncomingAnchorAliases");
398 			outgoingLookups.add(domain + "OutgoingAnchorAliases");
399 		}
400 		
401 		Collection<Setting> incomingAliasSettings = new ArrayList<Setting>();
402 		Collection<Setting> outgoingAliasSettings = new ArrayList<Setting>();
403 		for (String lookup : incomingLookups)
404 		{
405 			try
406 			{
407 				Setting st = getSafeSetting(lookup);
408 				if (st != null)
409 					incomingAliasSettings.add(st);
410 			}
411 			catch (Exception e)
412 			{
413 				throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "WebService error getting anchor aliases: " + e.getMessage(), e);
414 			}
415 		}
416 		
417 		for (String lookup : outgoingLookups)
418 		{
419 			try
420 			{
421 				Setting st = getSafeSetting(lookup);
422 				if (st != null)
423 					outgoingAliasSettings.add(st);
424 			}
425 			catch (Exception e)
426 			{
427 				throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "WebService error getting anchor aliases: " + e.getMessage(), e);
428 			}
429 		}
430 		
431 		// get the anchors from the correct store
432 		if (storeType.equalsIgnoreCase(STORE_TYPE_KEYSTORE))
433 		{
434 			Setting file;
435 			Setting pass;
436 			Setting privKeyPass;
437 			try
438 			{
439 				file = getSafeSetting("AnchorKeyStoreFile");
440 				pass = getSafeSetting("AnchorKeyStoreFilePass");
441 				privKeyPass = getSafeSetting("AnchorKeyStorePrivKeyPass");
442 			}
443 			catch (Exception e)
444 			{
445 				throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "WebService error getting anchor key store settings: " + e.getMessage(), e);
446 			}
447 			
448 			final KeyStoreCertificateStore store = new KeyStoreCertificateStore((file == null) ? null : file.getValue(), 
449 					(pass == null) ? "DefaultFilePass" : pass.getValue(), (privKeyPass == null) ? "DefaultKeyPass" : privKeyPass.getValue());
450 			
451 			// get incoming anchors
452 			if (incomingAliasSettings != null)
453 			{
454 				for (Setting setting : incomingAliasSettings)				
455 				{
456 					Collection<X509Certificate> certs = new ArrayList<X509Certificate>();				
457 					String aliases[] = setting.getValue().split(",");
458 					for (String alias : aliases)
459 					{
460 						X509Certificate cert = store.getByAlias(alias);
461 						if (cert != null)
462 						{
463 							certs.add(cert);
464 						}
465 					}				
466 					incomingAnchors.put(setting.getName().substring(0, setting.getName().lastIndexOf("IncomingAnchorAliases")), certs);
467 				}
468 			}
469 			
470 			// get outgoing anchors
471 			if (outgoingAliasSettings != null)
472 			{
473 				for (Setting setting : outgoingAliasSettings)				
474 				{
475 					Collection<X509Certificate> certs = new ArrayList<X509Certificate>();
476 					String aliases[] = setting.getValue().split(",");
477 					for (String alias : aliases)
478 					{
479 						X509Certificate cert = store.getByAlias(alias);
480 						if (cert != null)
481 						{
482 							certs.add(cert);
483 						}
484 					}				
485 					outgoingAnchors.put(setting.getName().substring(0, setting.getName().lastIndexOf("OutgoingAnchorAliases")), certs);
486 				}
487 			}
488 		}
489 		else
490 		{
491 			throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "Unknow anchor store type: " + storeType);
492 		}
493 	}	
494 	
495 	protected Collection<CertificateResolver> getPublicCertResolvers() 
496 	{
497 		final Collection<CertificateResolver> resolvers = new ArrayList<>();
498 		
499 		Setting setting = null;
500 		String storeTypes;
501 		try
502 		{
503 			setting = getSafeSetting("PublicStoreType");
504 		}
505 		catch (Exception e)
506 		{
507 			throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "WebService error getting public store type: " + e.getMessage(), e);
508 		}	
509 		
510 		if (setting == null || setting.getValue() == null || setting.getValue().isEmpty())
511 			storeTypes = STORE_TYPE_DNS + "," + STORE_TYPE_PUBLIC_LDAP; // default to DNS,LDAP
512 		else
513 			storeTypes = setting.getValue();
514 		
515 		final String[] types = storeTypes.split(",");
516 		CertificateResolver lookedUpResolver = null;
517 		for (String storeType : types)
518 		{
519 			/*
520 			 * Keystore
521 			 */
522 			if (storeType.equalsIgnoreCase(STORE_TYPE_KEYSTORE))
523 			{
524 				Setting file;
525 				Setting pass;
526 				Setting privKeyPass;
527 				try
528 				{
529 					file = getSafeSetting("PublicStoreFile");
530 					pass = getSafeSetting("PublicStoreFilePass"); 
531 					privKeyPass = getSafeSetting("PublicStorePrivKeyPass"); 
532 				}
533 				catch (Exception e)
534 				{
535 					throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "WebService error getting public store file settings: " + e.getMessage(), e);
536 				}
537 				
538 				lookedUpResolver = new KeyStoreCertificateStore((file == null) ? "PublicStoreKeyFile" : file.getValue(), 
539 						(pass == null) ? "DefaultFilePass" : pass.getValue(), (privKeyPass == null) ? "DefaultKeyPass" : privKeyPass.getValue());
540 			}
541 			/*
542 			 * DNS resolver
543 			 */			
544 			else if(storeType.equalsIgnoreCase(STORE_TYPE_DNS))
545 			{
546 				lookedUpResolver = new DNSCertificateStore(Collections.emptyList(), null, new DNSCertificateStore.DefaultDNSCachePolicy());								
547 			}
548 			/*
549 			 * Config Service
550 			 */
551 			else if (storeType.equalsIgnoreCase(STORE_TYPE_WS))
552 			{
553 				lookedUpResolver = new ConfigServiceRESTCertificateStore(certService, 
554 						null, new ConfigServiceRESTCertificateStore.DefaultConfigStoreCachePolicy(), keyStoreMgr);
555 			}
556 			/*
557 			 * Public LDAP resolver
558 			 */
559 			else if (storeType.equalsIgnoreCase(STORE_TYPE_PUBLIC_LDAP))
560 			{
561 				lookedUpResolver = new LDAPCertificateStore(new LdapPublicCertUtilImpl(), null, 
562 						new LDAPCertificateStore.DefaultLDAPCachePolicy());
563 			}
564 			/*
565 			 * Default to DNS with a default cache policy
566 			 */
567 			else
568 			{
569 				lookedUpResolver = new DNSCertificateStore(Collections.emptyList(), null, new DNSCertificateStore.DefaultDNSCachePolicy());				
570 			}		
571 			
572 			resolvers.add(lookedUpResolver);
573 		}
574 		
575 		
576 		return resolvers;
577 	}
578 	
579 	protected CertificateResolver getPrivateCertResolver() 
580 	{
581 		CertificateResolver resolver = null;
582 		
583 		Setting setting = null;
584 		String storeType;
585 		try
586 		{
587 			setting = getSafeSetting("PrivateStoreType"); 
588 		}
589 		catch (Exception e)
590 		{
591 			throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "WebService error getting private store type: " + e.getMessage(), e);
592 		}			
593 		
594 		if (setting == null || setting.getValue() == null || setting.getValue().isEmpty())
595 			storeType = STORE_TYPE_WS; // default to WS
596 		else
597 			storeType = setting.getValue();	
598 		
599 		if (storeType.equalsIgnoreCase(STORE_TYPE_KEYSTORE))
600 		{
601 			Setting file;
602 			Setting pass;
603 			Setting privKeyPass;
604 			try
605 			{
606 				file = getSafeSetting("PrivateStoreFile");
607 				pass = getSafeSetting("PrivateStoreFilePass"); 
608 				privKeyPass = getSafeSetting("PrivateStorePrivKeyPass"); 
609 			}
610 			catch (Exception e)
611 			{
612 				throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "WebService error getting public store file settings: " + e.getMessage(), e);
613 			}
614 			
615 			resolver = new KeyStoreCertificateStore((file == null) ? "PublicStoreKeyFile" : file.getValue(), 
616 					(pass == null) ? "DefaultFilePass" : pass.getValue(), (privKeyPass == null) ? "DefaultKeyPass" : privKeyPass.getValue());
617 		}		
618 		else if(storeType.equalsIgnoreCase(STORE_TYPE_LDAP))
619 		{
620 			resolver = getPrivateLdapCertificateStore("PrivateStore", "LDAPPrivateCertStore");
621 		}
622 		else if (storeType.equalsIgnoreCase(STORE_TYPE_WS))
623 		{
624 			resolver = new ConfigServiceRESTCertificateStore(certService, 
625 					null, new ConfigServiceRESTCertificateStore.DefaultConfigStoreCachePolicy(), keyStoreMgr);
626 		}
627 		else
628 		{
629 			throw new SmtpAgentException(SmtpAgentError.InvalidPrivateCertStoreSettings);
630 		}	
631 		
632 		return resolver;
633 	}
634 	
635 	protected MessageProcessingSettings getMessageProcessingSetting(String settingName)
636 	{
637 		final MessageProcessingSettingsttings.html#MessageProcessingSettings">MessageProcessingSettings retVal = new MessageProcessingSettings();
638 		try
639 		{
640 			final Setting setting = getSafeSetting(settingName);
641 			
642 			if (setting != null && !StringUtils.isEmpty(setting.getValue()))
643 				retVal.setSaveMessageFolder(new File(setting.getValue()));
644 		}
645 		catch (Exception e)
646 		{
647 			LOGGER.warn("Could not get setting " + settingName, e);
648 		}
649 		
650 		return retVal;
651 	}
652 	
653 	protected Setting getSafeSetting(String settingName)
654 	{
655 		try
656 		{
657 			return settingService.getSetting(settingName);
658 		}
659 		catch (Exception e)
660 		{
661 			LOGGER.info("Could not get setting " + settingName);
662 			return null;
663 		}
664 	}
665 	
666 	protected CertificateResolver getPrivateLdapCertificateStore(String type, String cacheStoreName)
667 	{
668 	    //required
669 		Setting ldapURLSetting;
670 		Setting ldapSearchBaseSetting;
671 		Setting ldapSearchAttrSetting;
672 		Setting ldapCertAttrSetting;
673 		Setting ldapCertFormatSetting;
674         //optional	    
675 	    Setting ldapUserSetting;
676 	    Setting ldapPasswordSetting;
677 	    Setting ldapConnTimeoutSetting;	   
678 	    Setting ldapCertPassphraseSetting;	
679 		try
680 		{
681 			ldapURLSetting = getSafeSetting(type +  "LDAPUrl"); 
682 			ldapSearchBaseSetting = getSafeSetting(type +  "LDAPSearchBase"); 
683 			ldapSearchAttrSetting = getSafeSetting(type +  "LDAPSearchAttr"); 
684 			ldapCertAttrSetting = getSafeSetting(type +  "LDAPCertAttr"); 
685 			ldapCertFormatSetting = getSafeSetting(type +  "LDAPCertFormat"); 
686 	        //optional	    
687 		    ldapUserSetting = getSafeSetting(type +  "LDAPUser"); 
688 		    ldapPasswordSetting =  getSafeSetting(type +  "LDAPPassword"); 
689 		    ldapConnTimeoutSetting =  getSafeSetting(type +  "LDAPConnTimeout");   
690 		    ldapCertPassphraseSetting =  getSafeSetting(type +  "LDAPCertPassphrase"); 
691 		}
692 		catch (Exception e)
693 		{
694 			throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "WebService error getting LDAP store settings: " + e.getMessage(), e);
695 		}
696         if (ldapURLSetting == null || ldapURLSetting.getValue() == null || ldapURLSetting.getValue().isEmpty())
697         	 throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "Missing LDAP URL");
698         
699 		String ldapSearchBase = (ldapSearchBaseSetting == null) ? null : ldapSearchBaseSetting.getValue();
700 		String ldapSearchAttr = (ldapSearchAttrSetting == null) ? null : ldapSearchAttrSetting.getValue();
701 		String ldapCertAttr = (ldapCertAttrSetting == null) ? null : ldapCertAttrSetting.getValue();
702 		String ldapCertFormat = (ldapCertFormatSetting == null) ? null : ldapCertFormatSetting.getValue();
703         String[] ldapURL = ldapURLSetting.getValue().split(",");
704 
705         if(ldapURL[0].isEmpty() || ldapSearchBase.isEmpty() || ldapSearchAttr.isEmpty() ||
706                 ldapCertAttr.isEmpty() || ldapCertFormat.isEmpty())
707         {
708             throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "Missing required LDAP parameters.");
709         }        
710         	    
711 	    String ldapUser = (ldapUserSetting == null) ? null : ldapUserSetting.getValue();
712 	    String ldapPassword =  (ldapPasswordSetting == null) ? null : ldapPasswordSetting.getValue();
713 	    String ldapConnTimeout =  (ldapConnTimeoutSetting == null) ? null : ldapConnTimeoutSetting.getValue();	   
714 	    String ldapCertPassphrase =  (ldapCertPassphraseSetting == null) ? null : ldapCertPassphraseSetting.getValue();    
715 	    
716 	    
717 	    if(ldapCertFormat.equalsIgnoreCase("pkcs12") && ( ldapCertPassphrase == null || ldapCertPassphrase.isEmpty()))
718 	    {
719 	        throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat);
720 	    }
721 	    LdapStoreConfiguration ldapStoreConfiguration = new LdapStoreConfiguration(ldapURL, ldapSearchBase, ldapSearchAttr, ldapCertAttr, ldapCertFormat);
722 	    if(ldapUser != null && !ldapUser.isEmpty() && ldapPassword != null && !ldapPassword.isEmpty())
723 	    {
724 	        ldapStoreConfiguration.setEmployLdapAuthInformation(new EmployLdapAuthInformation(ldapUser, ldapPassword));
725 	    }
726 	    if(ldapConnTimeout != null && !ldapConnTimeout.isEmpty())
727 	    {
728 	        ldapStoreConfiguration.setLdapConnectionTimeOut(ldapConnTimeout);
729 	    }
730 	    if(ldapCertPassphrase != null && !ldapCertPassphrase.isEmpty())
731 	    {
732 	        ldapStoreConfiguration.setLdapCertPassphrase(ldapCertPassphrase);
733 	    }
734 	    
735 	    return LdapCertificateStoreFactory.createInstance(ldapStoreConfiguration, null, new LDAPCertificateStore.DefaultLDAPCachePolicy());
736 	}	
737 	
738 	protected PolicyResolvers getPolicyResolvers()
739 	{
740 		final Map<String, Collection<PolicyExpression>> incomingPrivatePolicies = new HashMap<>();
741 		final Map<String, Collection<PolicyExpression>> outgoingPrivatePolicies = new HashMap<>();
742 		
743 		final Map<String, Collection<PolicyExpression>> incomingPublicPolicies = new HashMap<>();
744 		final Map<String, Collection<PolicyExpression>> outgoingPublicPolicies = new HashMap<>();
745 	
746 		final Map<String, Collection<PolicyExpression>> trustPolicies = new HashMap<>();
747 		
748 		Collection<CertPolicyGroupDomainReltn> domainReltns = null;
749 		try
750 		{   
751 			// get all of the policy group to domain relations... 
752 			// doing this all in one call for efficiency
753 			domainReltns = polService.getPolicyGroupDomainReltns();
754 		}
755 		catch (Exception e)
756 		{
757 			throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "WebService error getting certificate policy configuration: " + e.getMessage(), e);
758 		}
759 		
760 		if (domainReltns != null)
761 		{
762 			for (CertPolicyGroupDomainReltn domainReltn : domainReltns)
763 			{
764 				if (domainReltn.getPolicyGroup().getPolicies() != null)
765 				{
766 					for (CertPolicyGroupUse policyReltn : domainReltn.getPolicyGroup().getPolicies())
767 					{						
768 						if (policyReltn.getPolicyUse().equals(CertPolicyUse.PRIVATE_RESOLVER))
769 						{
770 							if (policyReltn.isIncoming())
771 								addPolicyToMap(incomingPrivatePolicies, domainReltn.getDomain().getDomainName(), policyReltn);
772 							if (policyReltn.isOutgoing())
773 								addPolicyToMap(outgoingPrivatePolicies, domainReltn.getDomain().getDomainName(), policyReltn);
774 						}
775 						else if (policyReltn.getPolicyUse().equals(CertPolicyUse.PUBLIC_RESOLVER))
776 						{
777 							if (policyReltn.isIncoming())
778 								addPolicyToMap(incomingPublicPolicies, domainReltn.getDomain().getDomainName(), policyReltn);
779 							if (policyReltn.isOutgoing())
780 								addPolicyToMap(outgoingPublicPolicies, domainReltn.getDomain().getDomainName(), policyReltn);							
781 						}
782 						else if (policyReltn.getPolicyUse().equals(CertPolicyUse.TRUST))
783 						{
784 							addPolicyToMap(trustPolicies, domainReltn.getDomain().getDomainName(), policyReltn);
785 						}	
786 					}
787 				}
788 			}
789 		}
790 		
791 		final PolicyResolvers retVal = new PolicyResolvers(new DomainPolicyResolver(incomingPublicPolicies, outgoingPublicPolicies), 
792 				new DomainPolicyResolver(incomingPrivatePolicies, outgoingPrivatePolicies),
793 				new DomainPolicyResolver(trustPolicies, trustPolicies));
794 		
795 		return retVal;
796 	}	
797 	
798 	
799 	@SuppressWarnings("deprecation")
800 	public void addPolicyToMap(Map<String, Collection<PolicyExpression>> policyMap, String domainName, CertPolicyGroupUse policyReltn)
801 	{
802 		// check to see if the domain is in the map
803 		Collection<PolicyExpression> policyExpressionCollection = policyMap.get(domainName);
804 		if (policyExpressionCollection == null)
805 		{
806 			policyExpressionCollection = new ArrayList<PolicyExpression>();
807 			policyMap.put(domainName, policyExpressionCollection);
808 		}
809 		
810 		final CertPolicy policy = policyReltn.getPolicy();
811 		final PolicyLexicon lexicon = policy.getLexicon();
812 		
813 		final InputStream inStr = new ByteArrayInputStream(policy.getPolicyData());
814 		
815 		try
816 		{
817 			// grab a parser and compile this policy
818 			final PolicyLexiconParser parser = PolicyLexiconParserFactory.getInstance(lexicon);
819 			
820 			policyExpressionCollection.add(parser.parse(inStr));
821 		}
822 		catch (PolicyParseException ex)
823 		{
824 			throw new SmtpAgentException(SmtpAgentError.InvalidConfigurationFormat, "Failed parse policy into policy expression: " + ex.getMessage(), ex);
825 		}
826 		finally
827 		{
828 			IOUtils.closeQuietly(inStr);
829 		}
830 		
831 	}
832 	
833 	protected static class PolicyResolvers
834 	{
835 		private final PolicyResolver publicResolver;
836 		private final PolicyResolver privateResolver;
837 		private final PolicyResolver trustResolver;
838 		
839 		public PolicyResolvers(PolicyResolver publicResolver, PolicyResolver privateResolver, PolicyResolver trustResolver)
840 		{
841 			this.publicResolver = publicResolver;
842 			this.privateResolver = privateResolver;
843 			this.trustResolver = trustResolver;
844 		}
845 		
846 		public PolicyResolver getPublicResolver()
847 		{
848 			return publicResolver;
849 		}
850 
851 		public PolicyResolver getPrivateResolver()
852 		{
853 			return privateResolver;
854 		}
855 
856 		public PolicyResolver getTrustResolver()
857 		{
858 			return trustResolver;
859 		}
860 	}
861 }