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

package com.nimbusds.jose.jwk;

import java.security.KeyStoreException;
import java.security.cert.Certificate;
import java.util.Enumeration;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.KeyStore;
import com.nimbusds.jose.util.Resource;
import com.nimbusds.jose.util.DefaultResourceRetriever;
import java.net.Proxy;
import java.net.URL;
import java.io.File;
import java.io.IOException;
import com.nimbusds.jose.util.IOUtils;
import com.nimbusds.jose.util.StandardCharset;
import java.io.InputStream;
import java.util.HashMap;
import java.text.ParseException;
import com.nimbusds.jose.util.JSONArrayUtils;
import com.nimbusds.jose.util.JSONObjectUtils;
import java.util.LinkedList;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.util.Base64URL;
import java.util.Iterator;
import java.util.Objects;
import java.util.Collections;
import java.util.Map;
import java.util.List;
import com.nimbusds.jose.shaded.jcip.Immutable;
import java.io.Serializable;

@Immutable
public class JWKSet implements Serializable
{
    private static final long serialVersionUID = 1L;
    public static final String MIME_TYPE = "application/jwk-set+json; charset=UTF-8";
    private final List<JWK> keys;
    private final Map<String, Object> customMembers;
    
    public JWKSet() {
        this(Collections.emptyList());
    }
    
    public JWKSet(final JWK key) {
        this(Collections.singletonList((JWK)Objects.requireNonNull((T)key, "The JWK must not be null")));
    }
    
    public JWKSet(final List<JWK> keys) {
        this(keys, Collections.emptyMap());
    }
    
    public JWKSet(final List<JWK> keys, final Map<String, Object> customMembers) {
        this.keys = Collections.unmodifiableList((List<? extends JWK>)Objects.requireNonNull((List<? extends T>)keys, "The JWK list must not be null"));
        this.customMembers = Collections.unmodifiableMap((Map<? extends String, ?>)customMembers);
    }
    
    public List<JWK> getKeys() {
        return this.keys;
    }
    
    public boolean isEmpty() {
        return this.keys.isEmpty();
    }
    
    public int size() {
        return this.keys.size();
    }
    
    public JWK getKeyByKeyId(final String kid) {
        for (final JWK key : this.getKeys()) {
            if (key.getKeyID() != null && key.getKeyID().equals(kid)) {
                return key;
            }
        }
        return null;
    }
    
    public boolean containsJWK(final JWK jwk) throws JOSEException {
        final Base64URL thumbprint = jwk.computeThumbprint();
        for (final JWK k : this.getKeys()) {
            if (thumbprint.equals(k.computeThumbprint())) {
                return true;
            }
        }
        return false;
    }
    
    public Map<String, Object> getAdditionalMembers() {
        return this.customMembers;
    }
    
    public JWKSet toPublicJWKSet() {
        final List<JWK> publicKeyList = new LinkedList<JWK>();
        for (final JWK key : this.keys) {
            final JWK publicKey = key.toPublicJWK();
            if (publicKey != null) {
                publicKeyList.add(publicKey);
            }
        }
        return new JWKSet(publicKeyList, this.customMembers);
    }
    
    public JWKSet filter(final JWKMatcher jwkMatcher) {
        final List<JWK> matches = new LinkedList<JWK>();
        for (final JWK key : this.keys) {
            if (jwkMatcher.matches(key)) {
                matches.add(key);
            }
        }
        return new JWKSet(matches, this.customMembers);
    }
    
    public boolean containsNonPublicKeys() {
        for (final JWK jwk : this.getKeys()) {
            if (jwk.isPrivate()) {
                return true;
            }
        }
        return false;
    }
    
    public Map<String, Object> toJSONObject() {
        return this.toJSONObject(true);
    }
    
    public Map<String, Object> toJSONObject(final boolean publicKeysOnly) {
        final Map<String, Object> o = JSONObjectUtils.newJSONObject();
        o.putAll(this.customMembers);
        final List<Object> a = JSONArrayUtils.newJSONArray();
        for (final JWK key : this.keys) {
            if (publicKeysOnly) {
                final JWK publicKey = key.toPublicJWK();
                if (publicKey == null) {
                    continue;
                }
                a.add(publicKey.toJSONObject());
            }
            else {
                a.add(key.toJSONObject());
            }
        }
        o.put("keys", a);
        return o;
    }
    
    public String toString(final boolean publicKeysOnly) {
        return JSONObjectUtils.toJSONString(this.toJSONObject(publicKeysOnly));
    }
    
    @Override
    public String toString() {
        return this.toString(true);
    }
    
    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof JWKSet)) {
            return false;
        }
        final JWKSet jwkSet = (JWKSet)o;
        return this.getKeys().equals(jwkSet.getKeys()) && this.customMembers.equals(jwkSet.customMembers);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(this.getKeys(), this.customMembers);
    }
    
    public static JWKSet parse(final String s) throws ParseException {
        return parse(JSONObjectUtils.parse(s));
    }
    
    public static JWKSet parse(final Map<String, Object> json) throws ParseException {
        final List<Object> keyArray = JSONObjectUtils.getJSONArray(json, "keys");
        if (keyArray == null) {
            throw new ParseException("Missing required \"keys\" member", 0);
        }
        final List<JWK> keys = new LinkedList<JWK>();
        for (int i = 0; i < keyArray.size(); ++i) {
            try {
                final Map<String, Object> keyJSONObject = keyArray.get(i);
                keys.add(JWK.parse(keyJSONObject));
            }
            catch (final ClassCastException e) {
                throw new ParseException("The \"keys\" JSON array must contain JSON objects only", 0);
            }
            catch (final ParseException e2) {
                if (e2.getMessage() == null || !e2.getMessage().startsWith("Unsupported key type")) {
                    throw new ParseException("Invalid JWK at position " + i + ": " + e2.getMessage(), 0);
                }
            }
        }
        final Map<String, Object> additionalMembers = new HashMap<String, Object>();
        for (final Map.Entry<String, Object> entry : json.entrySet()) {
            if (entry.getKey() != null) {
                if (entry.getKey().equals("keys")) {
                    continue;
                }
                additionalMembers.put(entry.getKey(), entry.getValue());
            }
        }
        return new JWKSet(keys, additionalMembers);
    }
    
    public static JWKSet load(final InputStream inputStream) throws IOException, ParseException {
        return parse(IOUtils.readInputStreamToString(inputStream, StandardCharset.UTF_8));
    }
    
    public static JWKSet load(final File file) throws IOException, ParseException {
        return parse(IOUtils.readFileToString(file, StandardCharset.UTF_8));
    }
    
    public static JWKSet load(final URL url, final int connectTimeout, final int readTimeout, final int sizeLimit) throws IOException, ParseException {
        return load(url, connectTimeout, readTimeout, sizeLimit, null);
    }
    
    public static JWKSet load(final URL url, final int connectTimeout, final int readTimeout, final int sizeLimit, final Proxy proxy) throws IOException, ParseException {
        final DefaultResourceRetriever resourceRetriever = new DefaultResourceRetriever(connectTimeout, readTimeout, sizeLimit);
        resourceRetriever.setProxy(proxy);
        final Resource resource = resourceRetriever.retrieveResource(url);
        return parse(resource.getContent());
    }
    
    public static JWKSet load(final URL url) throws IOException, ParseException {
        return load(url, 0, 0, 0);
    }
    
    public static JWKSet load(final KeyStore keyStore, final PasswordLookup pwLookup) throws KeyStoreException {
        final List<JWK> jwks = new LinkedList<JWK>();
        Enumeration<String> keyAliases = keyStore.aliases();
        while (keyAliases.hasMoreElements()) {
            final String keyAlias = keyAliases.nextElement();
            final char[] keyPassword = (pwLookup == null) ? "".toCharArray() : pwLookup.lookupPassword(keyAlias);
            final Certificate cert = keyStore.getCertificate(keyAlias);
            if (cert == null) {
                continue;
            }
            if (cert.getPublicKey() instanceof RSAPublicKey) {
                RSAKey rsaJWK;
                try {
                    rsaJWK = RSAKey.load(keyStore, keyAlias, keyPassword);
                }
                catch (final JOSEException e) {
                    continue;
                }
                if (rsaJWK == null) {
                    continue;
                }
                jwks.add(rsaJWK);
            }
            else {
                if (!(cert.getPublicKey() instanceof ECPublicKey)) {
                    continue;
                }
                ECKey ecJWK;
                try {
                    ecJWK = ECKey.load(keyStore, keyAlias, keyPassword);
                }
                catch (final JOSEException e) {
                    continue;
                }
                if (ecJWK == null) {
                    continue;
                }
                jwks.add(ecJWK);
            }
        }
        keyAliases = keyStore.aliases();
        while (keyAliases.hasMoreElements()) {
            final String keyAlias = keyAliases.nextElement();
            final char[] keyPassword = (pwLookup == null) ? "".toCharArray() : pwLookup.lookupPassword(keyAlias);
            OctetSequenceKey octJWK;
            try {
                octJWK = OctetSequenceKey.load(keyStore, keyAlias, keyPassword);
            }
            catch (final JOSEException e2) {
                continue;
            }
            if (octJWK != null) {
                jwks.add(octJWK);
            }
        }
        return new JWKSet(jwks);
    }
}
