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

package io.netty.handler.ssl;

import java.util.Objects;
import io.netty.util.collection.IntCollections;
import io.netty.util.collection.IntObjectHashMap;
import java.util.concurrent.ConcurrentHashMap;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.PSSParameterSpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.spec.MGF1ParameterSpec;
import java.security.Provider;
import java.security.Security;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import io.netty.util.internal.ObjectUtil;
import java.security.PrivateKey;
import java.util.concurrent.ConcurrentMap;
import io.netty.util.collection.IntObjectMap;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.internal.tcnative.SSLPrivateKeyMethod;

final class JdkDelegatingPrivateKeyMethod implements SSLPrivateKeyMethod
{
    private static final InternalLogger logger;
    private static final IntObjectMap<String> SSL_TO_JDK_SIGNATURE_ALGORITHM;
    private static final ConcurrentMap<CacheKey, String> PROVIDER_CACHE;
    private final PrivateKey privateKey;
    private final String privateKeyTypeName;
    
    JdkDelegatingPrivateKeyMethod(final PrivateKey privateKey) {
        this.privateKey = ObjectUtil.checkNotNull(privateKey, "privateKey");
        this.privateKeyTypeName = privateKey.getClass().getName();
    }
    
    public byte[] sign(final long ssl, final int signatureAlgorithm, final byte[] input) throws Exception {
        final Signature signature = this.createSignature(signatureAlgorithm);
        signature.update(input);
        final byte[] result = signature.sign();
        if (JdkDelegatingPrivateKeyMethod.logger.isDebugEnabled()) {
            JdkDelegatingPrivateKeyMethod.logger.debug("Signing operation completed successfully, result length: {}", (Object)result.length);
        }
        return result;
    }
    
    public byte[] decrypt(final long ssl, final byte[] input) {
        throw new UnsupportedOperationException("Direct decryption is not supported");
    }
    
    private Signature createSignature(final int opensslAlgorithm) throws NoSuchAlgorithmException {
        final String jdkAlgorithm = JdkDelegatingPrivateKeyMethod.SSL_TO_JDK_SIGNATURE_ALGORITHM.get(opensslAlgorithm);
        if (jdkAlgorithm == null) {
            final String errorMsg = "Unsupported signature algorithm: " + opensslAlgorithm;
            throw new NoSuchAlgorithmException(errorMsg);
        }
        final CacheKey cacheKey = new CacheKey(jdkAlgorithm, this.privateKeyTypeName);
        final String cachedProviderName = JdkDelegatingPrivateKeyMethod.PROVIDER_CACHE.get(cacheKey);
        if (cachedProviderName != null) {
            try {
                final Signature signature = Signature.getInstance(jdkAlgorithm, cachedProviderName);
                configureOpenSslAlgorithmParameters(signature, opensslAlgorithm);
                signature.initSign(this.privateKey);
                if (JdkDelegatingPrivateKeyMethod.logger.isDebugEnabled()) {
                    JdkDelegatingPrivateKeyMethod.logger.debug("Using cached provider {} for OpenSSL algorithm {} ({}) with key type {}", cachedProviderName, opensslAlgorithm, jdkAlgorithm, this.privateKeyTypeName);
                }
                return signature;
            }
            catch (final Exception e) {
                JdkDelegatingPrivateKeyMethod.PROVIDER_CACHE.remove(cacheKey);
                if (JdkDelegatingPrivateKeyMethod.logger.isDebugEnabled()) {
                    JdkDelegatingPrivateKeyMethod.logger.debug("Cached provider {} failed for key type {}, removing from cache: {}", cachedProviderName, this.privateKeyTypeName, e.getMessage());
                }
            }
        }
        final Signature signature = this.findCompatibleSignature(opensslAlgorithm, jdkAlgorithm);
        JdkDelegatingPrivateKeyMethod.PROVIDER_CACHE.put(cacheKey, signature.getProvider().getName());
        if (JdkDelegatingPrivateKeyMethod.logger.isDebugEnabled()) {
            JdkDelegatingPrivateKeyMethod.logger.debug("Discovered and cached provider {} for OpenSSL algorithm {} ({}) with key type {}", signature.getProvider().getName(), opensslAlgorithm, jdkAlgorithm, this.privateKeyTypeName);
        }
        return signature;
    }
    
    private Signature findCompatibleSignature(final int opensslAlgorithm, final String jdkAlgorithm) throws NoSuchAlgorithmException {
        try {
            final Signature signature = Signature.getInstance(jdkAlgorithm);
            configureOpenSslAlgorithmParameters(signature, opensslAlgorithm);
            signature.initSign(this.privateKey);
            if (JdkDelegatingPrivateKeyMethod.logger.isDebugEnabled()) {
                JdkDelegatingPrivateKeyMethod.logger.debug("Default provider {} handles key type {} for OpenSSL algorithm {} ({})", signature.getProvider().getName(), this.privateKey.getClass().getName(), opensslAlgorithm, jdkAlgorithm);
            }
            return signature;
        }
        catch (final InvalidKeyException e) {
            if (JdkDelegatingPrivateKeyMethod.logger.isDebugEnabled()) {
                JdkDelegatingPrivateKeyMethod.logger.debug("Default provider cannot handle key type {} for OpenSSL algorithm {} ({}): {}", this.privateKey.getClass().getName(), opensslAlgorithm, jdkAlgorithm, e.getMessage());
            }
        }
        catch (final Exception e2) {
            if (JdkDelegatingPrivateKeyMethod.logger.isDebugEnabled()) {
                JdkDelegatingPrivateKeyMethod.logger.debug("Default provider failed for OpenSSL algorithm {} ({}): {}", opensslAlgorithm, jdkAlgorithm, e2.getMessage());
            }
        }
        final Provider[] providers2;
        final Provider[] providers = providers2 = Security.getProviders();
        for (final Provider provider : providers2) {
            try {
                final Signature signature2 = Signature.getInstance(jdkAlgorithm, provider);
                configureOpenSslAlgorithmParameters(signature2, opensslAlgorithm);
                signature2.initSign(this.privateKey);
                if (JdkDelegatingPrivateKeyMethod.logger.isDebugEnabled()) {
                    JdkDelegatingPrivateKeyMethod.logger.debug("Found compatible provider {} for key type {} with OpenSSL algorithm {} ({})", provider.getName(), this.privateKey.getClass().getName(), opensslAlgorithm, jdkAlgorithm);
                }
                return signature2;
            }
            catch (final InvalidKeyException e3) {
                if (JdkDelegatingPrivateKeyMethod.logger.isTraceEnabled()) {
                    JdkDelegatingPrivateKeyMethod.logger.trace("Provider {} cannot handle key type {}: {}", provider.getName(), this.privateKey.getClass().getName(), e3.getMessage());
                }
            }
            catch (final Exception e4) {
                if (JdkDelegatingPrivateKeyMethod.logger.isTraceEnabled()) {
                    JdkDelegatingPrivateKeyMethod.logger.trace("Provider {} failed for OpenSSL algorithm {} ({}): {}", provider.getName(), opensslAlgorithm, jdkAlgorithm, e4.getMessage());
                }
            }
        }
        throw new NoSuchAlgorithmException("No provider found for OpenSSL algorithm " + opensslAlgorithm + " (" + jdkAlgorithm + ") with private key type: " + this.privateKey.getClass().getName());
    }
    
    private static void configureOpenSslAlgorithmParameters(final Signature signature, final int opensslAlgorithm) throws InvalidAlgorithmParameterException {
        if (opensslAlgorithm == OpenSslAsyncPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA256) {
            configurePssParameters(signature, MGF1ParameterSpec.SHA256, 32);
        }
        else if (opensslAlgorithm == OpenSslAsyncPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA384) {
            configurePssParameters(signature, MGF1ParameterSpec.SHA384, 48);
        }
        else if (opensslAlgorithm == OpenSslAsyncPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA512) {
            configurePssParameters(signature, MGF1ParameterSpec.SHA512, 64);
        }
        else if (JdkDelegatingPrivateKeyMethod.SSL_TO_JDK_SIGNATURE_ALGORITHM.containsKey(opensslAlgorithm)) {
            if (JdkDelegatingPrivateKeyMethod.logger.isTraceEnabled()) {
                JdkDelegatingPrivateKeyMethod.logger.trace("No parameter configuration needed for OpenSSL algorithm {}", (Object)opensslAlgorithm);
            }
        }
        else if (JdkDelegatingPrivateKeyMethod.logger.isDebugEnabled()) {
            JdkDelegatingPrivateKeyMethod.logger.debug("Unknown OpenSSL algorithm {}, using default configuration", (Object)opensslAlgorithm);
        }
    }
    
    private static void configurePssParameters(final Signature signature, final MGF1ParameterSpec mgfSpec, final int saltLength) throws InvalidAlgorithmParameterException {
        final PSSParameterSpec pssSpec = new PSSParameterSpec(mgfSpec.getDigestAlgorithm(), "MGF1", mgfSpec, saltLength, 1);
        signature.setParameter(pssSpec);
        if (JdkDelegatingPrivateKeyMethod.logger.isDebugEnabled()) {
            JdkDelegatingPrivateKeyMethod.logger.debug("Configured PSS parameters: hash={}, saltLength={}", mgfSpec.getDigestAlgorithm(), saltLength);
        }
    }
    
    static {
        logger = InternalLoggerFactory.getInstance(JdkDelegatingPrivateKeyMethod.class);
        PROVIDER_CACHE = new ConcurrentHashMap<CacheKey, String>();
        final IntObjectMap<String> algorithmMap = new IntObjectHashMap<String>();
        algorithmMap.put(OpenSslAsyncPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA1, "SHA1withRSA");
        algorithmMap.put(OpenSslAsyncPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA256, "SHA256withRSA");
        algorithmMap.put(OpenSslAsyncPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA384, "SHA384withRSA");
        algorithmMap.put(OpenSslAsyncPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA512, "SHA512withRSA");
        algorithmMap.put(OpenSslAsyncPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_MD5_SHA1, "MD5andSHA1withRSA");
        algorithmMap.put(OpenSslAsyncPrivateKeyMethod.SSL_SIGN_ECDSA_SHA1, "SHA1withECDSA");
        algorithmMap.put(OpenSslAsyncPrivateKeyMethod.SSL_SIGN_ECDSA_SECP256R1_SHA256, "SHA256withECDSA");
        algorithmMap.put(OpenSslAsyncPrivateKeyMethod.SSL_SIGN_ECDSA_SECP384R1_SHA384, "SHA384withECDSA");
        algorithmMap.put(OpenSslAsyncPrivateKeyMethod.SSL_SIGN_ECDSA_SECP521R1_SHA512, "SHA512withECDSA");
        algorithmMap.put(OpenSslAsyncPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA256, "RSASSA-PSS");
        algorithmMap.put(OpenSslAsyncPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA384, "RSASSA-PSS");
        algorithmMap.put(OpenSslAsyncPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA512, "RSASSA-PSS");
        algorithmMap.put(OpenSslAsyncPrivateKeyMethod.SSL_SIGN_ED25519, "EdDSA");
        SSL_TO_JDK_SIGNATURE_ALGORITHM = IntCollections.unmodifiableMap(algorithmMap);
    }
    
    private static final class CacheKey
    {
        private final String jdkAlgorithm;
        private final String keyTypeName;
        private final int hashCode;
        
        @Override
        public boolean equals(final Object o) {
            if (o == null || this.getClass() != o.getClass()) {
                return false;
            }
            final CacheKey cacheKey = (CacheKey)o;
            return Objects.equals(cacheKey.jdkAlgorithm, this.jdkAlgorithm) && Objects.equals(cacheKey.keyTypeName, this.keyTypeName);
        }
        
        @Override
        public int hashCode() {
            return this.hashCode;
        }
        
        CacheKey(final String jdkAlgorithm, final String keyTypeName) {
            this.jdkAlgorithm = jdkAlgorithm;
            this.keyTypeName = keyTypeName;
            this.hashCode = 31 * jdkAlgorithm.hashCode() + keyTypeName.hashCode();
        }
    }
}
