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

org.bouncycastle.jsse.provider.ProvX509KeyManager Maven / Gradle / Ivy

Go to download

The Bouncy Castle Java APIs for the TLS, including a JSSE provider. The APIs are designed primarily to be used in conjunction with the BC FIPS provider. The APIs may also be used with other providers although if being used in a FIPS context it is the responsibility of the user to ensure that any other providers used are FIPS certified and used appropriately.

There is a newer version: 2.0.19
Show newest version
package org.bouncycastle.jsse.provider;

import java.net.Socket;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedKeyManager;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.TBSCertificate;

class ProvX509KeyManager
    extends X509ExtendedKeyManager
{
    private final List builders;

    // TODO: does this need to be threadsafe? Will leak memory...
    private final Map keys = new HashMap();

    private final AtomicLong version = new AtomicLong();

    public ProvX509KeyManager(List builders)
    {
        // the builder list is processed on request so the key manager is dynamic.
        this.builders = builders;
    }

    public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket)
    {
        // TODO[jsse] Socket argument currently unused
        return chooseAlias(false, keyTypes, issuers);
    }

    public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine)
    {
        // TODO[jsse] SSLEngine argument currently unused
        return chooseAlias(false, keyTypes, issuers);
    }

    public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine)
    {
        // TODO[jsse] SSLEngine argument currently unused
        return chooseAlias(true, new String[]{ keyType }, issuers);
    }

    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket)
    {
        // TODO[jsse] Socket argument currently unused
        return chooseAlias(true, new String[]{ keyType }, issuers);
    }

    public X509Certificate[] getCertificateChain(String alias)
    {
        PrivateKeyEntry entry = getPrivateKeyEntry(alias);
        return entry == null ? null : (X509Certificate[])entry.getCertificateChain();
    }

    public String[] getClientAliases(String keyType, Principal[] issuers)
    {
        return getAliases(false, keyType, issuers);
    }

    public PrivateKey getPrivateKey(String alias)
    {
        PrivateKeyEntry entry = getPrivateKeyEntry(alias);
        return entry == null ? null : entry.getPrivateKey();
    }

    public String[] getServerAliases(String keyType, Principal[] issuers)
    {
        return getAliases(true, keyType, issuers);
    }

    private String chooseAlias(boolean forServer, String[] keyTypes, Principal[] issuers)
    {
        try
        {
            Set issuerNames = JsseUtils.toX500Names(issuers);

            // TODO[jsse] Need to support the keyTypes that SunJSSE sends here
            for (int i = 0; i != keyTypes.length; i++)
            {
                List aliases = findAliases(forServer, keyTypes[i], issuerNames);
                if (!aliases.isEmpty())
                {
                    return aliases.get(0);
                }
            }
        }
        catch (Exception e)
        {
        }

        return null;
    }

    private List findAliases(boolean forServer, String keyType, Set issuers)
    {
        List aliases = new ArrayList();

        for (int i = 0; i != builders.size(); i++)
        {
            KeyStore.Builder builder = builders.get(i);

            try
            {
                aliases.addAll(findAliases(forServer, i, builder.getKeyStore(), builder, keyType, issuers));
            }
            catch (GeneralSecurityException e)
            {
                throw new IllegalStateException("unable to build key store: " + e.getMessage(), e);
            }
        }

        return aliases;
    }

    private List findAliases(boolean forServer, int index, KeyStore keyStore, KeyStore.Builder storeBuilder, String keyType, Set issuers)
        throws GeneralSecurityException
    {
        List aliases = new ArrayList();

        for (Enumeration en = keyStore.aliases(); en.hasMoreElements();)
        {
            String eName = (String)en.nextElement();

            if (!keyStore.isKeyEntry(eName))      // not a key entry
            {
                continue;
            }

            X509Certificate[] chain = JsseUtils.getX509CertificateChain(keyStore.getCertificateChain(eName));
            if (chain == null || chain.length == 0)    // not an entry with a certificate
            {
                continue;
            }

            // TODO: manage two key/certs in one store that matches

            if (isSuitableCredential(forServer, keyType, issuers, chain))
            {
                KeyStore.PrivateKeyEntry kEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(eName, storeBuilder.getProtectionParameter(eName));

                String alias = index + "." + eName + "." + version.getAndIncrement();

                keys.put(alias, kEntry);

                aliases.add(alias);
            }
        }

        return aliases;
    }

    private String[] getAliases(boolean forServer, String keyType, Principal[] issuers)
    {
        List aliases = findAliases(forServer, keyType, JsseUtils.toX500Names(issuers));
        return aliases.toArray(new String[aliases.size()]);
    }

    private PrivateKeyEntry getPrivateKeyEntry(String alias)
    {
        return alias == null ? null : keys.get(alias);
    }

    private boolean hasSuitableIssuer(Set issuerNames, X509Certificate c)
    {
        return issuerNames.contains(JsseUtils.toX500Name(c.getIssuerX500Principal()));
    }

    private boolean isSuitableCredential(boolean forServer, String keyType, Set issuerNames,
        X509Certificate[] certificateChain)
    {
        if (!isSuitableCertificate(forServer, keyType, certificateChain[0]))
        {
            return false;
        }
        if (issuerNames == null || issuerNames.isEmpty())
        {
            return true;
        }
        int pos = certificateChain.length;
        while (--pos >= 0)
        {
            if (hasSuitableIssuer(issuerNames, certificateChain[pos]))
            {
                return true;
            }
        }
        return false;
    }

    private boolean isSuitableCertificate(boolean forServer, String keyType, X509Certificate c)
    {
        if (keyType == null || c == null)
        {
            return false;
        }
        PublicKey pub = c.getPublicKey();
        if (keyType.equalsIgnoreCase("DHE_RSA")
            || keyType.equalsIgnoreCase("ECDHE_RSA")
            || keyType.equalsIgnoreCase("SRP_RSA"))
        {
            return (pub instanceof RSAPublicKey) && isSuitableKeyUsage(KeyUsage.digitalSignature, c);
        }
        else if (keyType.equalsIgnoreCase("DHE_DSS")
            || keyType.equalsIgnoreCase("SRP_DSS"))
        {
            return (pub instanceof DSAPublicKey) && isSuitableKeyUsage(KeyUsage.digitalSignature, c);
        }
        else if (keyType.equalsIgnoreCase("ECDHE_ECDSA"))
        {
            return (pub instanceof ECPublicKey) && isSuitableKeyUsage(KeyUsage.digitalSignature, c);
        }
        else if (keyType.equalsIgnoreCase("RSA"))
        {
            int keyUsage = forServer ? KeyUsage.keyEncipherment : KeyUsage.digitalSignature;
            return (pub instanceof RSAPublicKey) && isSuitableKeyUsage(keyUsage, c);
        }
        else if (keyType.equalsIgnoreCase("DSA"))
        {
            return !forServer && (pub instanceof DSAPublicKey) && isSuitableKeyUsage(KeyUsage.digitalSignature, c);
        }
        else if (keyType.equalsIgnoreCase("EC"))
        {
            // NOTE: SunJSSE server asks for "EC" for ECDHE_ECDSA key exchange
            return (pub instanceof ECPublicKey) && isSuitableKeyUsage(KeyUsage.digitalSignature, c);
        }
        // TODO[jsse] Support other key exchanges (and client certificate types)
        return false;
    }

    private boolean isSuitableKeyUsage(int keyUsageBits, X509Certificate c)
    {
        try
        {
            Extensions exts = TBSCertificate.getInstance(c.getTBSCertificate()).getExtensions();
            if (exts != null)
            {
                KeyUsage ku = KeyUsage.fromExtensions(exts);
                if (ku != null)
                {
                    int bits = ku.getBytes()[0] & 0xff;
                    if ((bits & keyUsageBits) != keyUsageBits)
                    {
                        return false;
                    }
                }
            }
        }
        catch (Exception e)
        {
            return false;
        }
        return true;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy