// 
// Decompiled by Procyon v0.6.0
// 

package org.bouncycastle.jcajce.provider.keystore.util;

import java.io.ByteArrayInputStream;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.io.Streams;
import org.bouncycastle.jcajce.provider.util.DigestFactory;
import org.bouncycastle.util.Strings;
import org.bouncycastle.crypto.Digest;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateFactory;
import org.bouncycastle.jcajce.BCLoadStoreParameter;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Enumeration;
import java.security.KeyStoreException;
import java.util.Date;
import java.security.cert.Certificate;
import java.security.UnrecoverableKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Key;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.InputStream;
import org.bouncycastle.jcajce.util.JcaJceHelper;
import java.util.Hashtable;
import java.security.KeyStoreSpi;

public class JKSKeyStoreSpi extends KeyStoreSpi
{
    private static final String NOT_IMPLEMENTED_MESSAGE = "BC JKS store is read-only and only supports certificate entries";
    private final Hashtable<String, BCJKSTrustedCertEntry> certificateEntries;
    private final JcaJceHelper helper;
    
    public JKSKeyStoreSpi(final JcaJceHelper helper) {
        this.certificateEntries = new Hashtable<String, BCJKSTrustedCertEntry>();
        this.helper = helper;
    }
    
    @Override
    public boolean engineProbe(final InputStream in) throws IOException {
        DataInputStream dataInputStream;
        if (in instanceof DataInputStream) {
            dataInputStream = (DataInputStream)in;
        }
        else {
            dataInputStream = new DataInputStream(in);
        }
        final int int1 = dataInputStream.readInt();
        final int int2 = dataInputStream.readInt();
        return int1 == -17957139 && (int2 == 1 || int2 == 2);
    }
    
    @Override
    public Key engineGetKey(final String s, final char[] array) throws NoSuchAlgorithmException, UnrecoverableKeyException {
        return null;
    }
    
    @Override
    public Certificate[] engineGetCertificateChain(final String s) {
        return null;
    }
    
    @Override
    public Certificate engineGetCertificate(final String key) {
        synchronized (this.certificateEntries) {
            final BCJKSTrustedCertEntry bcjksTrustedCertEntry = this.certificateEntries.get(key);
            if (bcjksTrustedCertEntry != null) {
                return bcjksTrustedCertEntry.cert;
            }
        }
        return null;
    }
    
    @Override
    public Date engineGetCreationDate(final String key) {
        synchronized (this.certificateEntries) {
            final BCJKSTrustedCertEntry bcjksTrustedCertEntry = this.certificateEntries.get(key);
            if (bcjksTrustedCertEntry != null) {
                return bcjksTrustedCertEntry.date;
            }
        }
        return null;
    }
    
    @Override
    public void engineSetKeyEntry(final String s, final Key key, final char[] array, final Certificate[] array2) throws KeyStoreException {
        throw new KeyStoreException("BC JKS store is read-only and only supports certificate entries");
    }
    
    @Override
    public void engineSetKeyEntry(final String s, final byte[] array, final Certificate[] array2) throws KeyStoreException {
        throw new KeyStoreException("BC JKS store is read-only and only supports certificate entries");
    }
    
    @Override
    public void engineSetCertificateEntry(final String s, final Certificate certificate) throws KeyStoreException {
        throw new KeyStoreException("BC JKS store is read-only and only supports certificate entries");
    }
    
    @Override
    public void engineDeleteEntry(final String s) throws KeyStoreException {
        throw new KeyStoreException("BC JKS store is read-only and only supports certificate entries");
    }
    
    @Override
    public Enumeration<String> engineAliases() {
        synchronized (this.certificateEntries) {
            return this.certificateEntries.keys();
        }
    }
    
    @Override
    public boolean engineContainsAlias(final String key) {
        if (key == null) {
            throw new NullPointerException("alias value is null");
        }
        synchronized (this.certificateEntries) {
            return this.certificateEntries.containsKey(key);
        }
    }
    
    @Override
    public int engineSize() {
        return this.certificateEntries.size();
    }
    
    @Override
    public boolean engineIsKeyEntry(final String s) {
        return false;
    }
    
    @Override
    public boolean engineIsCertificateEntry(final String key) {
        synchronized (this.certificateEntries) {
            return this.certificateEntries.containsKey(key);
        }
    }
    
    @Override
    public String engineGetCertificateAlias(final Certificate other) {
        synchronized (this.certificateEntries) {
            for (final Map.Entry entry : this.certificateEntries.entrySet()) {
                if (((BCJKSTrustedCertEntry)entry.getValue()).cert.equals(other)) {
                    return (String)entry.getKey();
                }
            }
            return null;
        }
    }
    
    @Override
    public void engineStore(final OutputStream outputStream, final char[] array) throws IOException, NoSuchAlgorithmException, CertificateException {
        throw new IOException("BC JKS store is read-only and only supports certificate entries");
    }
    
    @Override
    public void engineLoad(final KeyStore.LoadStoreParameter loadStoreParameter) throws IOException, NoSuchAlgorithmException, CertificateException {
        if (loadStoreParameter == null) {
            this.engineLoad(null, (char[])null);
        }
        else {
            if (!(loadStoreParameter instanceof BCLoadStoreParameter)) {
                throw new IllegalArgumentException("no support for 'param' of type " + loadStoreParameter.getClass().getName());
            }
            this.engineLoad(((BCLoadStoreParameter)loadStoreParameter).getInputStream(), ParameterUtil.extractPassword(loadStoreParameter));
        }
    }
    
    @Override
    public void engineLoad(final InputStream inputStream, final char[] array) throws IOException, NoSuchAlgorithmException, CertificateException {
        if (inputStream == null) {
            return;
        }
        final ErasableByteStream validateStream = this.validateStream(inputStream, array);
        synchronized (this.certificateEntries) {
            try {
                final DataInputStream dataInputStream = new DataInputStream(validateStream);
                final int int1 = dataInputStream.readInt();
                final int int2 = dataInputStream.readInt();
                if (int1 == -17957139) {
                    Object value = null;
                    Hashtable hashtable = null;
                    switch (int2) {
                        case 1: {
                            value = this.createCertFactory("X.509");
                            break;
                        }
                        case 2: {
                            hashtable = new Hashtable();
                            break;
                        }
                        default: {
                            throw new IllegalStateException("unable to discern store version");
                        }
                    }
                    final int int3 = dataInputStream.readInt();
                    int i = 0;
                    while (i < int3) {
                        switch (dataInputStream.readInt()) {
                            case 1: {
                                throw new IOException("BC JKS store is read-only and only supports certificate entries");
                            }
                            case 2: {
                                final String utf = dataInputStream.readUTF();
                                final Date date = new Date(dataInputStream.readLong());
                                if (int2 == 2) {
                                    final String utf2 = dataInputStream.readUTF();
                                    if (hashtable.containsKey(utf2)) {
                                        value = hashtable.get(utf2);
                                    }
                                    else {
                                        value = this.createCertFactory(utf2);
                                        hashtable.put(utf2, value);
                                    }
                                }
                                final byte[] b = new byte[dataInputStream.readInt()];
                                dataInputStream.readFully(b);
                                final ErasableByteStream inStream = new ErasableByteStream(b, 0, b.length);
                                Certificate generateCertificate;
                                try {
                                    generateCertificate = ((CertificateFactory)value).generateCertificate(inStream);
                                    if (inStream.available() != 0) {
                                        throw new IOException("password incorrect or store tampered with");
                                    }
                                }
                                finally {
                                    inStream.erase();
                                }
                                this.certificateEntries.put(utf, new BCJKSTrustedCertEntry(date, generateCertificate));
                                ++i;
                                continue;
                            }
                            default: {
                                throw new IllegalStateException("unable to discern entry type");
                            }
                        }
                    }
                }
                if (validateStream.available() != 0) {
                    throw new IOException("password incorrect or store tampered with");
                }
            }
            finally {
                validateStream.erase();
            }
        }
    }
    
    private CertificateFactory createCertFactory(final String type) throws CertificateException {
        if (this.helper != null) {
            try {
                return this.helper.createCertificateFactory(type);
            }
            catch (final NoSuchProviderException ex) {
                throw new CertificateException(ex.toString());
            }
        }
        return CertificateFactory.getInstance(type);
    }
    
    private void addPassword(final Digest digest, final char[] array) throws IOException {
        for (int i = 0; i < array.length; ++i) {
            digest.update((byte)(array[i] >> 8));
            digest.update((byte)array[i]);
        }
        digest.update(Strings.toByteArray("Mighty Aphrodite"), 0, 16);
    }
    
    private ErasableByteStream validateStream(final InputStream inputStream, final char[] array) throws IOException {
        final Digest digest = DigestFactory.getDigest("SHA-1");
        final byte[] all = Streams.readAll(inputStream);
        if (array == null) {
            return new ErasableByteStream(all, 0, all.length - digest.getDigestSize());
        }
        this.addPassword(digest, array);
        digest.update(all, 0, all.length - digest.getDigestSize());
        final byte[] array2 = new byte[digest.getDigestSize()];
        digest.doFinal(array2, 0);
        final byte[] array3 = new byte[array2.length];
        System.arraycopy(all, all.length - array2.length, array3, 0, array2.length);
        if (!Arrays.constantTimeAreEqual(array2, array3)) {
            Arrays.fill(all, (byte)0);
            throw new IOException("password incorrect or store tampered with");
        }
        return new ErasableByteStream(all, 0, all.length - array2.length);
    }
    
    private static final class BCJKSTrustedCertEntry
    {
        final Date date;
        final Certificate cert;
        
        public BCJKSTrustedCertEntry(final Date date, final Certificate cert) {
            this.date = date;
            this.cert = cert;
        }
    }
    
    private static final class ErasableByteStream extends ByteArrayInputStream
    {
        public ErasableByteStream(final byte[] buf, final int offset, final int length) {
            super(buf, offset, length);
        }
        
        public void erase() {
            Arrays.fill(this.buf, (byte)0);
        }
    }
}
