opends/resource/schema/02-config.ldif
@@ -2068,6 +2068,36 @@ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' ) attributeTypes: ( 1.3.6.1.4.1.26027.1.1.423 NAME 'ds-cfg-cipher-transformation-name' DESC 'The name of a cryptographic cipher transformation consisting of an algorithm, a mode, and a padding specification, separated by slashes' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' ) attributeTypes: ( 1.3.6.1.4.1.26027.1.1.424 NAME 'ds-cfg-mac-algorithm-name' DESC 'The name of a cryptographic message authentication code (MAC) algorithm' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' ) attributeTypes: ( 1.3.6.1.4.1.26027.1.1.425 NAME 'ds-cfg-key-length-bits' DESC 'The length of a cryptographic secret key' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' ) attributeTypes: ( 1.3.6.1.4.1.26027.1.1.426 NAME 'ds-cfg-initialization-vector-length-bits' DESC 'The length of a cryptographic cipher initialization vector' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' ) attributeTypes: ( 1.3.6.1.4.1.26027.1.1.427 NAME 'ds-cfg-symmetric-key' DESC 'A cryptographic secret-key wrapped by a public-key' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'OpenDS Directory Server' ) objectClasses: ( 1.3.6.1.4.1.26027.1.2.1 NAME 'ds-cfg-access-control-handler' SUP top @@ -3511,4 +3541,19 @@ SUP ds-cfg-extended-operation-handler STRUCTURAL X-ORIGIN 'OpenDS Directory Server' ) objectClasses: ( 1.3.6.1.4.1.26027.1.2.174 NAME 'ds-cfg-cipher-key' SUP top STRUCTURAL MUST ( ds-cfg-key-id $ ds-cfg-cipher-transformation-name $ ds-cfg-key-length-bits $ ds-cfg-symmetric-key ) MAY ( ds-cfg-initialization-vector-length-bits $ ds-cfg-key-compromised-time ) X-ORIGIN 'OpenDS Directory Server' ) objectClasses: ( 1.3.6.1.4.1.26027.1.2.175 NAME 'ds-cfg-mac-key' SUP top STRUCTURAL MUST ( ds-cfg-key-id $ ds-cfg-mac-algorithm-name $ ds-cfg-key-length-bits $ ds-cfg-symmetric-key ) MAY ds-cfg-key-compromised-time X-ORIGIN 'OpenDS Directory Server' ) opends/src/server/org/opends/server/backends/TrustStoreBackend.java
@@ -574,7 +574,7 @@ { // Make sure that the DN specifies a certificate alias. AttributeType t = DirectoryServer.getAttributeType(ATTR_CERT_ALIAS, true); DirectoryServer.getAttributeType(ATTR_CRYPTO_KEY_ID, true); AttributeValue v = entryDN.getRDN().getAttributeValue(t); if (v == null) { @@ -615,8 +615,8 @@ ocMap.put(DirectoryServer.getTopObjectClass(), OC_TOP); ObjectClass objectClass = DirectoryServer.getObjectClass(OC_INSTANCE_KEY, true); ocMap.put(objectClass, OC_INSTANCE_KEY); DirectoryServer.getObjectClass(OC_CRYPTO_INSTANCE_KEY, true); ocMap.put(objectClass, OC_CRYPTO_INSTANCE_KEY); LinkedHashMap<AttributeType,List<Attribute>> opAttrs = new LinkedHashMap<AttributeType,List<Attribute>>(0); @@ -632,7 +632,8 @@ userAttrs.put(t, attrList); t = DirectoryServer.getAttributeType(ATTR_ADS_CERTIFICATE, true); t = DirectoryServer.getAttributeType( ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE, true); valueSet = new LinkedHashSet<AttributeValue>(1); valueSet.add(new AttributeValue(t, certValue)); @@ -792,7 +793,7 @@ if ((scope != SearchScope.BASE_OBJECT) && (! (aliases.length == 0) )) { AttributeType certAliasType = DirectoryServer.getAttributeType(ATTR_CERT_ALIAS, true); DirectoryServer.getAttributeType(ATTR_CRYPTO_KEY_ID, true); for (String alias : aliases) { DN certDN = makeChildDN(this.baseDN, certAliasType, @@ -1570,7 +1571,7 @@ // Make sure that the DN specifies a certificate alias. AttributeType t = DirectoryServer.getAttributeType(ATTR_CERT_ALIAS, true); DirectoryServer.getAttributeType(ATTR_CRYPTO_KEY_ID, true); AttributeValue v = entryDN.getRDN().getAttributeValue(t); if (v == null) { @@ -1612,12 +1613,14 @@ } else { List<Attribute> certAttrs = entry.getAttribute(ATTR_ADS_CERTIFICATE); List<Attribute> certAttrs = entry.getAttribute( ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE); if (certAttrs == null) { Message message = ERR_TRUSTSTORE_ENTRY_MISSING_CERT_ATTR.get( String.valueOf(entryDN), ATTR_ADS_CERTIFICATE); String.valueOf(entryDN), ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE); throw new DirectoryException( DirectoryServer.getServerErrorResultCode(), message); } @@ -1625,7 +1628,8 @@ { Message message = ERR_TRUSTSTORE_ENTRY_HAS_MULTIPLE_CERT_ATTRS.get( String.valueOf(entryDN), ATTR_ADS_CERTIFICATE); String.valueOf(entryDN), ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE); throw new DirectoryException( DirectoryServer.getServerErrorResultCode(), message); } @@ -1635,7 +1639,8 @@ { Message message = ERR_TRUSTSTORE_ENTRY_MISSING_CERT_VALUE.get( String.valueOf(entryDN), ATTR_ADS_CERTIFICATE); String.valueOf(entryDN), ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE); throw new DirectoryException( DirectoryServer.getServerErrorResultCode(), message); } @@ -1643,7 +1648,8 @@ { Message message = ERR_TRUSTSTORE_ENTRY_HAS_MULTIPLE_CERT_VALUES.get( String.valueOf(entryDN), ATTR_ADS_CERTIFICATE); String.valueOf(entryDN), ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE); throw new DirectoryException( DirectoryServer.getServerErrorResultCode(), message); } @@ -1699,7 +1705,7 @@ { // Make sure that the DN specifies a certificate alias. AttributeType t = DirectoryServer.getAttributeType(ATTR_CERT_ALIAS, true); DirectoryServer.getAttributeType(ATTR_CRYPTO_KEY_ID, true); AttributeValue v = entryDN.getRDN().getAttributeValue(t); if (v == null) { opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -2873,9 +2873,9 @@ /** * The name of the attribute that holds a server certificate alias. * The name of the attribute that holds a cryptographic cipher-key identifier. */ public static final String ATTR_CERT_ALIAS = "ds-cfg-key-id"; public static final String ATTR_CRYPTO_KEY_ID = "ds-cfg-key-id"; @@ -2883,7 +2883,7 @@ * The name of the objectclass that will be used for a server * certificate entry. */ public static final String OC_INSTANCE_KEY = public static final String OC_CRYPTO_INSTANCE_KEY = "ds-cfg-instance-key"; @@ -2898,14 +2898,75 @@ /** * The name of the attribute that is used to specify a server * instance key. * The name of the objectclass that will be used for a cipher key. */ public static final String ATTR_ADS_CERTIFICATE = public static final String OC_CRYPTO_CIPHER_KEY = "ds-cfg-cipher-key"; /** * The name of the objectclass that will be used for a mac key. */ public static final String OC_CRYPTO_MAC_KEY = "ds-cfg-mac-key"; /** * The name of the attribute that is used to hold a cryptographic * public key certificate. */ public static final String ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE = "ds-cfg-public-key-certificate"; /** * The name of the attribute that is used to hold the name of a * cryptographic cipher transformation. */ public static final String ATTR_CRYPTO_CIPHER_TRANSFORMATION_NAME = "ds-cfg-cipher-transformation-name"; /** * The name of the attribute that is used to hold the name of a * cryptographic message authentication code (MAC) algorithm. */ public static final String ATTR_CRYPTO_MAC_ALGORITHM_NAME = "ds-cfg-mac-algorithm-name"; /** * The name of the attribute that is used to hold the length of a * cryptographic secret key. */ public static final String ATTR_CRYPTO_KEY_LENGTH_BITS = "ds-cfg-key-length-bits"; /** * The name of the attribute that is used to hold the length of a * cryptographic cipher initialization vector. */ public static final String ATTR_CRYPTO_INIT_VECTOR_LENGTH_BITS = "ds-cfg-initialization-vector-length-bits"; /** * The name of the attribute that is used to hold a cryptographic * cipher-key wrapped by a public-key. */ public static final String ATTR_CRYPTO_SYMMETRIC_KEY = "ds-cfg-symmetric-key"; /** * The name of the attribute that is used to hold time a cryptographic key * was suspected to be compromised. */ public static final String ATTR_CRYPTO_KEY_COMPROMISED_TIME = "ds-cfg-key-compromised-time"; /** * The DN of the entry that will serve as the base for all Directory Server * loggers. */ opends/src/server/org/opends/server/core/TrustStoreSyncThread.java
@@ -46,7 +46,9 @@ import static org.opends.server.util.ServerConstants. OID_ENTRY_CHANGE_NOTIFICATION; import org.opends.server.config.ConfigConstants; import static org.opends.server.config.ConfigConstants.OC_INSTANCE_KEY; import static org.opends.server.config.ConfigConstants.OC_CRYPTO_INSTANCE_KEY; import static org.opends.server.config.ConfigConstants.OC_CRYPTO_CIPHER_KEY; import static org.opends.server.config.ConfigConstants.OC_CRYPTO_MAC_KEY; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.protocols.internal.InternalSearchOperation; import org.opends.server.protocols.internal.InternalSearchListener; @@ -90,6 +92,9 @@ // The DN of the instance keys container within the admin suffix. private DN instanceKeysDN; // The DN of the secret keys container within the admin suffix. private DN secretKeysDN; // The DN of the trust store root. private DN trustStoreRootDN; @@ -102,8 +107,8 @@ // The attribute type that holds the time a key was compromised. AttributeType attrCompromisedTime; // A filter on the instance key object class. private SearchFilter instanceKeyFilter; // A filter on object class to select key entries. private SearchFilter keySearchFilter; // Indicates whether the ADS suffix backend is initialized. private boolean adminBackendInitialized; @@ -117,6 +122,15 @@ // Indicates whether the initial search has been done. private boolean searchDone; // The instance key objectclass. private ObjectClass ocInstanceKey; // The cipher key objectclass. private ObjectClass ocCipherKey; // The mac key objectclass. private ObjectClass ocMacKey; /** * Creates a new instance of this trust store synchronization thread. */ @@ -140,22 +154,33 @@ { adminSuffixDN = DN.decode(ADSContext.getAdministrationSuffixDN()); instanceKeysDN = adminSuffixDN.concat(DN.decode("cn=instance keys")); secretKeysDN = adminSuffixDN.concat(DN.decode("cn=secret keys")); trustStoreRootDN = DN.decode(ConfigConstants.DN_TRUST_STORE_ROOT); instanceKeyFilter = SearchFilter.createFilterFromString( "(objectclass=" + ConfigConstants.OC_INSTANCE_KEY + ")"); keySearchFilter = SearchFilter.createFilterFromString("(|" + "(objectclass=" + OC_CRYPTO_INSTANCE_KEY + ")" + "(objectclass=" + OC_CRYPTO_CIPHER_KEY + ")" + "(objectclass=" + OC_CRYPTO_MAC_KEY + ")" + ")"); } catch (DirectoryException e) { // } ocInstanceKey = DirectoryServer.getObjectClass( OC_CRYPTO_INSTANCE_KEY, true); ocCipherKey = DirectoryServer.getObjectClass( OC_CRYPTO_CIPHER_KEY, true); ocMacKey = DirectoryServer.getObjectClass( OC_CRYPTO_MAC_KEY, true); attrCert = DirectoryServer.getAttributeType( ConfigConstants.ATTR_ADS_CERTIFICATE, true); ConfigConstants.ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE, true); attrAlias = DirectoryServer.getAttributeType( ConfigConstants.ATTR_CERT_ALIAS, true); ConfigConstants.ATTR_CRYPTO_KEY_ID, true); attrCompromisedTime = DirectoryServer.getAttributeType( "ds-cfg-key-compromised-time", true); ConfigConstants.ATTR_CRYPTO_KEY_COMPROMISED_TIME, true); if (DirectoryServer.getBackendWithBaseDN(adminSuffixDN) != null) { @@ -226,7 +251,7 @@ adminSuffixDN, SearchScope.WHOLE_SUBTREE, DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false, instanceKeyFilter, attributes, false, keySearchFilter, attributes, this); searchOperation.run(); @@ -403,6 +428,35 @@ SearchResultEntry searchEntry) throws DirectoryException { if (searchEntry.hasObjectClass(ocInstanceKey)) { handleInstanceKeySearchEntry(searchEntry); } else { try { if (searchEntry.hasObjectClass(ocCipherKey)) { DirectoryServer.getCryptoManager().importCipherKeyEntry(searchEntry); } else if (searchEntry.hasObjectClass(ocMacKey)) { DirectoryServer.getCryptoManager().importMacKeyEntry(searchEntry); } } catch (CryptoManager.CryptoManagerException e) { throw new DirectoryException( DirectoryServer.getServerErrorResultCode(), e); } } } private void handleInstanceKeySearchEntry(SearchResultEntry searchEntry) throws DirectoryException { RDN srcRDN = searchEntry.getDN().getRDN(); // Only process the entry if it has the expected form of RDN. @@ -545,13 +599,10 @@ */ private void addEntry(Entry srcEntry, DN dstDN) { ObjectClass instanceKeyOC = DirectoryServer.getObjectClass(OC_INSTANCE_KEY, true); LinkedHashMap<ObjectClass,String> ocMap = new LinkedHashMap<ObjectClass,String>(2); ocMap.put(DirectoryServer.getTopObjectClass(), OC_TOP); ocMap.put(instanceKeyOC, OC_INSTANCE_KEY); ocMap.put(ocInstanceKey, OC_CRYPTO_INSTANCE_KEY); HashMap<AttributeType, List<Attribute>> userAttrs = new HashMap<AttributeType, List<Attribute>>(); @@ -600,11 +651,35 @@ public void handleAddOperation(PostResponseAddOperation addOperation, Entry entry) { if (!addOperation.getEntryDN().isDescendantOf(instanceKeysDN)) if (addOperation.getEntryDN().isDescendantOf(instanceKeysDN)) { return; handleInstanceKeyAddOperation(entry); } else if (addOperation.getEntryDN().isDescendantOf(secretKeysDN)) { try { if (entry.hasObjectClass(ocCipherKey)) { DirectoryServer.getCryptoManager().importCipherKeyEntry(entry); } else if (entry.hasObjectClass(ocMacKey)) { DirectoryServer.getCryptoManager().importMacKeyEntry(entry); } } catch (CryptoManager.CryptoManagerException e) { Message message = Message.raw("Failed to import key entry: %s", e.getMessage()); ErrorLogger.logError(message); } } } private void handleInstanceKeyAddOperation(Entry entry) { RDN srcRDN = entry.getDN().getRDN(); // Only process the entry if it has the expected form of RDN. opends/src/server/org/opends/server/types/CryptoManager.java
@@ -66,6 +66,8 @@ import org.opends.server.util.Base64; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.protocols.internal.InternalSearchOperation; import org.opends.server.schema.DirectoryStringSyntax; import org.opends.server.schema.IntegerSyntax; /** * This class provides the interface to the Directory Server @@ -136,6 +138,17 @@ // The set of SSL cipher suites enabled or null for the default set. private final SortedSet<String> sslCipherSuites; // Various schema element references. private final AttributeType attrKeyID; private final AttributeType attrTransformation; private final AttributeType attrMacAlgorithm; private final AttributeType attrSymmetricKey; private final AttributeType attrInitVectorLength; private final AttributeType attrKeyLength; private final AttributeType attrCompromisedTime; private final ObjectClass ocCipherKey; private final ObjectClass ocMacKey; /** * Creates a new instance of this crypto manager object from a given @@ -155,6 +168,26 @@ public CryptoManager(CryptoManagerCfg cfg) throws ConfigException, InitializationException { // Initialize various schema references. attrKeyID = DirectoryServer.getAttributeType( ConfigConstants.ATTR_CRYPTO_KEY_ID); attrTransformation = DirectoryServer.getAttributeType( ConfigConstants.ATTR_CRYPTO_CIPHER_TRANSFORMATION_NAME); attrMacAlgorithm = DirectoryServer.getAttributeType( ConfigConstants.ATTR_CRYPTO_MAC_ALGORITHM_NAME); attrSymmetricKey = DirectoryServer.getAttributeType( ConfigConstants.ATTR_CRYPTO_SYMMETRIC_KEY); attrInitVectorLength = DirectoryServer.getAttributeType( ConfigConstants.ATTR_CRYPTO_INIT_VECTOR_LENGTH_BITS); attrKeyLength = DirectoryServer.getAttributeType( ConfigConstants.ATTR_CRYPTO_KEY_LENGTH_BITS); attrCompromisedTime = DirectoryServer.getAttributeType( ConfigConstants.ATTR_CRYPTO_KEY_COMPROMISED_TIME); ocCipherKey = DirectoryServer.getObjectClass( ConfigConstants.OC_CRYPTO_CIPHER_KEY); ocMacKey = DirectoryServer.getObjectClass( ConfigConstants.OC_CRYPTO_MAC_KEY); // TODO -- Get the crypto defaults from the configuration. // Preferred digest and validation. @@ -273,12 +306,13 @@ */ public byte[] getInstanceKeyCertificate() throws CryptoManagerException { final String DN_ADS_CERTIFICATE = ConfigConstants.ATTR_CERT_ALIAS final String DN_ADS_CERTIFICATE = ConfigConstants.ATTR_CRYPTO_KEY_ID + "=" + ConfigConstants.ADS_CERTIFICATE_ALIAS + "," + ConfigConstants.DN_TRUST_STORE_ROOT; // TODO: constant final String FILTER_OC_INSTANCE_KEY = new StringBuilder("(objectclass=") .append(ConfigConstants.OC_INSTANCE_KEY) .append(ConfigConstants.OC_CRYPTO_INSTANCE_KEY) .append(")").toString(); final String ATTR_INSTANCE_KEY_CERTIFICATE_BINARY = "ds-cfg-public-key-certificate;binary"; @@ -307,9 +341,9 @@ final Entry e = searchOp.getSearchEntries().getFirst(); /* attribute ds-cfg-public-key-certificate is a MUST in the schema */ final List<Attribute> attrs = e.getAttribute(DirectoryServer.getAttributeType( ConfigConstants.ATTR_ADS_CERTIFICATE)); final List<Attribute> attrs = e.getAttribute(DirectoryServer.getAttributeType( ConfigConstants.ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE)); final Attribute a = attrs.get(0); final AttributeValue v = a.getValues().iterator().next(); certificate = v.getValueBytes(); @@ -1509,6 +1543,164 @@ } /** * Imports a cipher key entry from an entry in ADS. * * @param entry The ADS cipher key entry to be imported. * The entry will be ignored if it does not have * the ds-cfg-cipher-key objectclass, or if the * key is already present. * * @throws CryptoManagerException * If the entry had the correct objectclass, * was not already present but could not * be imported. */ public void importCipherKeyEntry(Entry entry) throws CryptoManagerException { // Ignore the entry if it does not have the appropriate // objectclass. if (!entry.hasObjectClass(ocCipherKey)) return; try { String keyID = entry.getAttributeValue(attrKeyID, DirectoryStringSyntax.DECODER); int ivLengthBits = entry.getAttributeValue(attrInitVectorLength, IntegerSyntax.DECODER); int keyLengthBits = entry.getAttributeValue(attrKeyLength, IntegerSyntax.DECODER); String transformation = entry.getAttributeValue(attrTransformation, DirectoryStringSyntax.DECODER); String compromisedTime = entry.getAttributeValue(attrCompromisedTime, DirectoryStringSyntax.DECODER); ArrayList<String> symmetricKeys = new ArrayList<String>(); entry.getAttributeValues(attrSymmetricKey, DirectoryStringSyntax.DECODER, symmetricKeys); // Find the symmetric key value that was wrapped using // our instance key. SecretKey secretKey = null; for (String symmetricKey : symmetricKeys) { secretKey = decodeSymmetricKeyAttribute(symmetricKey); if (secretKey != null) break; } if (secretKey == null) { // TODO: i18n Message message = Message.raw("Key entry %s contains no " + "symmetric key value that can be decoded " + "by this server", entry.getDN()); throw new CryptoManagerException(message); } boolean isCompromised = compromisedTime != null; CipherKeyEntry.importCipherKeyEntry(this, keyID, transformation, secretKey, keyLengthBits, ivLengthBits, isCompromised); } catch (DirectoryException e) { // TODO: i18n Message message = Message.raw("Error decoding cipher key entry %s: %s", entry.getDN(), e.getMessage()); throw new CryptoManagerException(message, e); } } /** * Imports a mac key entry from an entry in ADS. * * @param entry The ADS mac key entry to be imported. The * entry will be ignored if it does not have the * ds-cfg-mac-key objectclass, or if the key is * already present. * * @throws CryptoManagerException * If the entry had the correct objectclass, * was not already present but could not * be imported. */ public void importMacKeyEntry(Entry entry) throws CryptoManagerException { // Ignore the entry if it does not have the appropriate // objectclass. if (!entry.hasObjectClass(ocMacKey)) return; try { String keyID = entry.getAttributeValue(attrKeyID, DirectoryStringSyntax.DECODER); int keyLengthBits = entry.getAttributeValue(attrKeyLength, IntegerSyntax.DECODER); String algorithm = entry.getAttributeValue(attrMacAlgorithm, DirectoryStringSyntax.DECODER); String compromisedTime = entry.getAttributeValue(attrCompromisedTime, DirectoryStringSyntax.DECODER); ArrayList<String> symmetricKeys = new ArrayList<String>(); entry.getAttributeValues(attrSymmetricKey, DirectoryStringSyntax.DECODER, symmetricKeys); // Find the symmetric key value that was wrapped using our // instance key. SecretKey secretKey = null; for (String symmetricKey : symmetricKeys) { secretKey = decodeSymmetricKeyAttribute(symmetricKey); if (secretKey != null) break; } if (secretKey == null) { // TODO: i18n Message message = Message.raw("Key entry %s contains no " + "symmetric key value that can be decoded " + "by this server", entry.getDN()); throw new CryptoManagerException(message); } boolean isCompromised = compromisedTime != null; MacKeyEntry.importMacKeyEntry(this, keyID, algorithm, secretKey, keyLengthBits, isCompromised); } catch (DirectoryException e) { Message message = Message.raw("Error decoding mac key entry %s: %s", entry.getDN(), e.getMessage()); throw new CryptoManagerException(message, e); } } /** * This class implements a utility interface to the unique * identifier corresponding to a cryptographic key. For each key * stored in an entry in ADS, the key identifier is the naming @@ -1971,7 +2163,7 @@ CryptoManager cryptoManager, final KeyEntryID keyID) { return cryptoManager.cipherKeyEntryCache.get(keyID); /* TODO: Does ADS monitorying thread keep map updated with keys /* TODO: Does ADS monitoring thread keep map updated with keys produced at other sites? If not, fetch from ADS and update map (assuming a legitimate key ID, the key should exist in ADS because this routine is called for decryption). */