All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.sqlserver.jdbc.SQLServerSymmetricKeyCache Maven / Gradle / Ivy

There is a newer version: 62
Show newest version
/*
 * Microsoft JDBC Driver for SQL Server Copyright(c) Microsoft Corporation All rights reserved. This program is made
 * available under the terms of the MIT License. See the LICENSE file in the project root for more information.
 */

package com.microsoft.sqlserver.jdbc;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.SECONDS;

import java.text.MessageFormat;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;


class CacheClear implements Runnable {

    private String keylookupValue;
    static private java.util.logging.Logger aeLogger = java.util.logging.Logger
            .getLogger("com.microsoft.sqlserver.jdbc.CacheClear");

    CacheClear(String keylookupValue) {
        this.keylookupValue = keylookupValue;
    }

    @Override
    public void run() {
        // remove() is a no-op if the key is not in the map.
        // It is a concurrentHashMap, update/remove operations are thread safe.
        synchronized (SQLServerSymmetricKeyCache.lock) {
            SQLServerSymmetricKeyCache instance = SQLServerSymmetricKeyCache.getInstance();
            if (instance.getCache().containsKey(keylookupValue)) {
                instance.getCache().get(keylookupValue).zeroOutKey();
                instance.getCache().remove(keylookupValue);
                if (aeLogger.isLoggable(java.util.logging.Level.FINE)) {
                    aeLogger.fine("Removed encryption key from cache...");
                }
            }
        }
    }
}


/**
 * 
 * Cache for the Symmetric keys
 *
 */
final class SQLServerSymmetricKeyCache {
    static final Object lock = new Object();
    private final ConcurrentHashMap cache;
    private static final SQLServerSymmetricKeyCache instance = new SQLServerSymmetricKeyCache();
    private static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread t = Executors.defaultThreadFactory().newThread(r);
            t.setDaemon(true);
            return t;
        }
    });

    static final private java.util.logging.Logger aeLogger = java.util.logging.Logger
            .getLogger("com.microsoft.sqlserver.jdbc.SQLServerSymmetricKeyCache");

    private SQLServerSymmetricKeyCache() {
        cache = new ConcurrentHashMap<>();
    }

    static SQLServerSymmetricKeyCache getInstance() {
        return instance;
    }

    ConcurrentHashMap getCache() {
        return cache;
    }

    /**
     * Returns key
     * 
     * @param keyInfo
     *        contains encryption meta data information
     * @param connection
     * @return plain text key
     */
    SQLServerSymmetricKey getKey(EncryptionKeyInfo keyInfo, SQLServerConnection connection) throws SQLServerException {
        SQLServerSymmetricKey encryptionKey = null;
        synchronized (lock) {
            String serverName = connection.getTrustedServerNameAE();
            assert null != serverName : "serverName should not be null in getKey.";

            StringBuilder keyLookupValuebuffer = new StringBuilder(serverName);
            String keyLookupValue;
            keyLookupValuebuffer.append(":");

            keyLookupValuebuffer
                    .append(Base64.getEncoder().encodeToString((new String(keyInfo.encryptedKey, UTF_8)).getBytes()));

            keyLookupValuebuffer.append(":");
            keyLookupValuebuffer.append(keyInfo.keyStoreName);
            keyLookupValue = keyLookupValuebuffer.toString();
            keyLookupValuebuffer.setLength(0); // Get rid of the buffer, will be garbage collected.

            if (aeLogger.isLoggable(java.util.logging.Level.FINE)) {
                aeLogger.fine("Checking trusted master key path...");
            }
            Boolean[] hasEntry = new Boolean[1];
            List trustedKeyPaths = SQLServerConnection.getColumnEncryptionTrustedMasterKeyPaths(serverName,
                    hasEntry);
            if (hasEntry[0]) {
                if ((null == trustedKeyPaths) || (0 == trustedKeyPaths.size())
                        || (!trustedKeyPaths.contains(keyInfo.keyPath))) {
                    MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_UntrustedKeyPath"));
                    Object[] msgArgs = {keyInfo.keyPath, serverName};
                    throw new SQLServerException(this, form.format(msgArgs), null, 0, false);
                }
            }

            if (aeLogger.isLoggable(java.util.logging.Level.FINE)) {
                aeLogger.fine("Checking Symmetric key cache...");
            }

            // if ColumnEncryptionKeyCacheTtl is 0 no caching at all
            if (!cache.containsKey(keyLookupValue)) {
                byte[] plaintextKey;
                plaintextKey = connection.getColumnEncryptionKeyStoreProvider(keyInfo.keyStoreName)
                        .decryptColumnEncryptionKey(keyInfo.keyPath, keyInfo.algorithmName, keyInfo.encryptedKey);
                encryptionKey = new SQLServerSymmetricKey(plaintextKey);

                /*
                 * a ColumnEncryptionKeyCacheTtl value of '0' means no caching at all. The expected use case is to have
                 * the application set it once. The application could set it multiple times, in which case a key gets
                 * the TTL defined at the time of its entry into the cache.
                 */
                long columnEncryptionKeyCacheTtl = SQLServerConnection.getColumnEncryptionKeyCacheTtl();
                if (0 != columnEncryptionKeyCacheTtl) {
                    cache.putIfAbsent(keyLookupValue, encryptionKey);
                    if (aeLogger.isLoggable(java.util.logging.Level.FINE)) {
                        aeLogger.fine("Adding encryption key to cache...");
                    }
                    scheduler.schedule(new CacheClear(keyLookupValue), columnEncryptionKeyCacheTtl, SECONDS);
                }
            } else {
                encryptionKey = cache.get(keyLookupValue);
            }
        }
        return encryptionKey;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy