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

org.bouncycastle.jcajce.provider.ProvJKS Maven / Gradle / Ivy

Go to download

The FIPS 140-2 Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms certified to FIPS 140-2 level 1. This jar contains the debug version JCE provider and low-level API for the BC-FJA version 1.0.2.3, FIPS Certificate #3514. Please note the debug jar is not certified.

There is a newer version: 2.0.0
Show newest version
package org.bouncycastle.jcajce.provider;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.fips.FipsOutputDigestCalculator;
import org.bouncycastle.crypto.fips.FipsSHS;
import org.bouncycastle.jcajce.BCLoadStoreParameter;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.io.Streams;

/**
 * Reads a certificate only key store.
 */
class ProvJKS
    extends AlgorithmProvider
{
    private static final String PREFIX = "org.bouncycastle.jcajce.provider.keystore" + ".jks.";

    void configure(final BouncyCastleFipsProvider provider)
    {
        provider.addAlgorithmImplementation("KeyStore.JKS", PREFIX + "JKS", new EngineCreator()
        {
            public Object createInstance(Object constructorParameter)
            {
                return new JKSKeyStoreSpi(true, provider);
            }
        });
        if (!CryptoServicesRegistrar.isInApprovedOnlyMode())
        {
            provider.addAlgorithmImplementation("KeyStore.JKS-DEF", PREFIX + "JKSDef", new GuardedEngineCreator(new EngineCreator()
            {
                public Object createInstance(Object constructorParameter)
                {
                    return new JKSKeyStoreSpi(false,null);
                }
            }));
        }
    }

    static class JKSKeyStoreSpi
        extends KeyStoreSpi
    {
        private final Hashtable certificateEntries = new Hashtable();
        private static final String NOT_IMPLEMENTED_MESSAGE = "BCFIPS JKS store is read-only and only supports certificate entries";
        private final boolean matchOnProbe;
        private final BouncyCastleFipsProvider fipsProvider;

        public JKSKeyStoreSpi(boolean matchOnProbe, BouncyCastleFipsProvider provider)
        {
            this.matchOnProbe = matchOnProbe;
            this.fipsProvider = provider;
        }

        public boolean engineProbe(InputStream stream)
            throws IOException
        {
            if (!matchOnProbe)
            {
                return false;
            }

            DataInputStream storeStream;
            if (stream instanceof DataInputStream)
            {
                storeStream = (DataInputStream)stream;
            }
            else
            {
                storeStream = new DataInputStream(stream);
            }

            int magic = storeStream.readInt();
            int storeVersion = storeStream.readInt();
            return magic == (int)0x0000feedfeedL && (storeVersion == 1 || storeVersion == 2);
        }

        public Key engineGetKey(String alias, char[] password)
            throws NoSuchAlgorithmException, UnrecoverableKeyException
        {
            return null;  // by definition
        }

        public Certificate[] engineGetCertificateChain(String alias)
        {
            return null;  // by definition
        }

        public Certificate engineGetCertificate(String alias)
        {
            synchronized (certificateEntries)
            {
                BCJKSTrustedCertEntry ent = certificateEntries.get(alias);
                if (ent != null)
                {
                    return ent.cert;
                }
            }
            return null;
        }

        public Date engineGetCreationDate(String alias)
        {
            synchronized (certificateEntries)
            {
                BCJKSTrustedCertEntry ent = certificateEntries.get(alias);
                if (ent != null)
                {
                    return ent.date;
                }
            }
            return null;
        }

        public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
            throws KeyStoreException
        {
            throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
        }

        public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
            throws KeyStoreException
        {
            throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
        }

        public void engineSetCertificateEntry(String alias, Certificate cert)
            throws KeyStoreException
        {
            throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
        }

        public void engineDeleteEntry(String alias)
            throws KeyStoreException
        {
            throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
        }

        public Enumeration engineAliases()
        {
            synchronized (certificateEntries)
            {
                return certificateEntries.keys();
            }
        }

        public boolean engineContainsAlias(String alias)
        {
            if (alias == null)
            {
                throw new NullPointerException("alias value is null");
            }

            synchronized (certificateEntries)
            {
                return certificateEntries.containsKey(alias);
            }
        }

        public int engineSize()
        {
            return certificateEntries.size();
        }

        public boolean engineIsKeyEntry(String alias)
        {
            return false;    // by definition
        }

        public boolean engineIsCertificateEntry(String alias)
        {
            synchronized (certificateEntries)
            {
                return certificateEntries.containsKey(alias);
            }
        }

        public String engineGetCertificateAlias(Certificate cert)
        {
            synchronized (certificateEntries)
            {
                for (Iterator> it = certificateEntries.entrySet().iterator(); it.hasNext(); )
                {
                    Map.Entry entry = it.next();
                    if (entry.getValue().cert.equals(cert))
                    {
                        return entry.getKey();
                    }
                }
                return null;
            }
        }

        public void engineStore(OutputStream stream, char[] password)
            throws IOException, NoSuchAlgorithmException, CertificateException
        {
            throw new IOException(NOT_IMPLEMENTED_MESSAGE);
        }

        public void engineLoad(KeyStore.LoadStoreParameter loadStoreParameter)
            throws IOException, NoSuchAlgorithmException, CertificateException
        {
            if (loadStoreParameter == null)
            {
                engineLoad(null, null);
            }
            else if (loadStoreParameter instanceof BCLoadStoreParameter)
            {
                BCLoadStoreParameter bcParam = (BCLoadStoreParameter)loadStoreParameter;

                engineLoad(bcParam.getInputStream(), Utils.extractPassword(loadStoreParameter));
            }
            else
            {
                throw new IllegalArgumentException(
                    "no support for 'param' of type " + loadStoreParameter.getClass().getName());
            }
        }

        public void engineLoad(InputStream stream, char[] password)
            throws IOException, NoSuchAlgorithmException, CertificateException
        {
            if (stream == null)
            {
                return;
            }

            ErasableByteStream  storeStream = validateStream(stream, password);

            synchronized (certificateEntries)
            {
                try
                {
                    DataInputStream dIn = new DataInputStream(storeStream);

                    int magic = dIn.readInt();
                    int storeVersion = dIn.readInt();
                    if (magic == (int)0x0000feedfeedL)
                    {
                        CertificateFactory certFact = null;
                        Hashtable certFactories = null;

                        switch (storeVersion)
                        {
                        case 1:  // all certs X.509
                            certFact = createCertFactory("X.509");
                            break;
                        case 2:  // provision for format in store.
                            certFactories = new Hashtable();
                            break;
                        default:
                            throw new IllegalStateException("unable to discern store version");
                        }

                        int numEntries = dIn.readInt();
                        for (int t = 0; t < numEntries; t++)
                        {
                            int tag = dIn.readInt();
                            switch (tag)
                            {
                            case 1: // we can't process keys
                                throw new IOException(NOT_IMPLEMENTED_MESSAGE);
                            case 2: // certificate
                                String alias = dIn.readUTF();
                                Date date = new Date(dIn.readLong());

                                if (storeVersion == 2)
                                {
                                    String certFormat = dIn.readUTF();
                                    if (certFactories.containsKey(certFormat))
                                    {
                                        certFact = (CertificateFactory)certFactories.get(certFormat);
                                    }
                                    else
                                    {
                                        certFact = createCertFactory(certFormat);
                                        certFactories.put(certFormat, certFact);
                                    }
                                }

                                int l = dIn.readInt();
                                byte[] certData = new byte[l];
                                dIn.readFully(certData);

                                ErasableByteStream certStream = new ErasableByteStream(certData, 0, certData.length);
                                Certificate cert;
                                try
                                {
                                    cert = certFact.generateCertificate(certStream);

                                    if (certStream.available() != 0)
                                    {
                                        throw new IOException("password incorrect or store tampered with");
                                    }
                                }
                                finally
                                {
                                    certStream.erase();
                                }

                                certificateEntries.put(alias, new BCJKSTrustedCertEntry(date, cert));
                                break;
                            default:
                                throw new IllegalStateException("unable to discern entry type");
                            }
                        }
                    }

                    if (storeStream.available() != 0)
                    {
                        throw new IOException("password incorrect or store tampered with");
                    }
                }
                finally
                {
                    storeStream.erase();
                }
            }
        }

        private CertificateFactory createCertFactory(String certFormat)
            throws CertificateException
        {
            if (fipsProvider != null)
            {
                return CertificateFactory.getInstance(certFormat, fipsProvider);
            }
            else
            {
                return CertificateFactory.getInstance(certFormat);
            }
        }

        /**
         * Process password updates the digest with the password.
         *
         * @param digestStream The digest instance.
         * @param password The password.
         */
        private void addPassword(OutputStream digestStream, char[] password)
            throws IOException
        {
            for (int i = 0; i < password.length; ++i)
            {
                digestStream.write((byte)(password[i] >> 8));
                digestStream.write((byte)password[i]);
            }

            //
            // This "Mighty Aphrodite" string goes all the way back to the
            // first java betas in the mid 90's, why who knows? But see
            // https://cryptosense.com/mighty-aphrodite-dark-secrets-of-the-java-keystore/
            //
            digestStream.write(Strings.toByteArray("Mighty Aphrodite"));
        }

        /**
         * Validate password takes the checksum of the store and will either.
         * 1. If password is null, load the store into memory, return the result.
         * 2. If password is not null, load the store into memory, test the checksum, and if successful return
         * a new input stream instance of the store.
         * 3. Fail if there is a password and an invalid checksum.
         *
         * @param inputStream The input stream.
         * @param password    the password.
         * @return Either the passed in input stream or a new input stream.
         * @throws IOException
         */
        private ErasableByteStream validateStream(InputStream inputStream, char[] password)
            throws IOException
        {
            FipsOutputDigestCalculator checksumCalculator = new FipsSHS.OperatorFactory().createOutputDigestCalculator(FipsSHS.SHA1);
            byte[]                     rawStore = Streams.readAll(inputStream);

            if (password != null)
            {
                OutputStream checksumStream = checksumCalculator.getDigestStream();

                addPassword(checksumStream, password);
                checksumStream.write(rawStore, 0, rawStore.length - checksumCalculator.getDigestSize());

                byte[] checksum = checksumCalculator.getDigest();
                byte[] streamChecksum = new byte[checksum.length];
                System.arraycopy(rawStore, rawStore.length - checksum.length, streamChecksum, 0, checksum.length);

                if (!Arrays.constantTimeAreEqual(checksum, streamChecksum))
                {
                    Arrays.fill(rawStore, (byte)0);
                    throw new IOException("password incorrect or store tampered with");
                }

                return new ErasableByteStream(rawStore, 0, rawStore.length - checksum.length);
            }

            return new ErasableByteStream(rawStore, 0, rawStore.length - checksumCalculator.getDigestSize());
        }
    }


    /**
     * BCJKSTrustedCertEntry is a internal container for the certificate entry.
     */
    private static final class BCJKSTrustedCertEntry
    {
        final Date date;
        final Certificate cert;

        public BCJKSTrustedCertEntry(Date date, Certificate cert)
        {
            this.date = date;
            this.cert = cert;
        }
    }

    private static final class ErasableByteStream
        extends ByteArrayInputStream
    {
        public ErasableByteStream(byte[] buf, int offSet, int length)
        {
            super(buf, offSet, length);
        }

        public void erase()
        {
            // this will also erase the checksum from memory.
            Arrays.fill(buf, (byte)0);
        }
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy