private AuthenticodeSigner build()

in jsign-core/src/main/java/net/jsign/SignerHelper.java [271:467]


    private AuthenticodeSigner build() throws SignerException {
        PrivateKey privateKey;
        Certificate[] chain;

        // some exciting parameter validation...
        if (keystore == null && keyfile == null && certfile == null && !"YUBIKEY".equals(storetype) && !"DIGICERTONE".equals(storetype)) {
            throw new SignerException("keystore " + parameterName + ", or keyfile and certfile " + parameterName + "s must be set");
        }
        if (keystore != null && keyfile != null) {
            throw new SignerException("keystore " + parameterName + " can't be mixed with keyfile");
        }
        if ("AZUREKEYVAULT".equals(storetype)) {
            if (keystore == null) {
                throw new SignerException("keystore " + parameterName + " must specify the Azure vault name");
            }
            if (storepass == null) {
                throw new SignerException("storepass " + parameterName + " must specify the Azure API access token");
            }
        } else if ("DIGICERTONE".equals(storetype)) {
            if (storepass == null || storepass.split("\\|").length != 3) {
                throw new SignerException("storepass " + parameterName + " must specify the DigiCert ONE API key and the client certificate: <apikey>|<keystore>|<password>");
            }
        } else if ("GOOGLECLOUD".equals(storetype)) {
            if (keystore == null) {
                throw new SignerException("keystore " + parameterName + " must specify the Goole Cloud keyring");
            }
            if (storepass == null) {
                throw new SignerException("storepass " + parameterName + " must specify the Goole Cloud API access token");
            }
            if (certfile == null) {
                throw new SignerException("certfile " + parameterName + " must be set");
            }
        }
        
        Provider provider = null;
        if ("PKCS11".equals(storetype)) {
            // the keystore parameter is either the provider name or the SunPKCS11 configuration file
            if (keystore != null && keystore.exists()) {
                provider = ProviderUtils.createSunPKCS11Provider(keystore.getPath());
            } else if (keystore != null && keystore.getName().startsWith("SunPKCS11-")) {
                provider = Security.getProvider(keystore.getName());
                if (provider == null) {
                    throw new SignerException("Security provider " + keystore.getName() + " not found");
                }
            } else {
                throw new SignerException("keystore " + parameterName + " should either refer to the SunPKCS11 configuration file or to the name of the provider configured in jre/lib/security/java.security");
            }
        } else if ("YUBIKEY".equals(storetype)) {
            provider = YubiKey.getProvider();
        } else if ("AZUREKEYVAULT".equals(storetype)) {
            provider = new SigningServiceJcaProvider(new AzureKeyVaultSigningService(keystore.getName(), storepass));
        } else if ("DIGICERTONE".equals(storetype)) {
            String[] elements = storepass.split("\\|");
            provider = new SigningServiceJcaProvider(new DigiCertOneSigningService(elements[0], new File(elements[1]), elements[2]));
        } else if ("GOOGLECLOUD".equals(storetype)) {
            provider = new SigningServiceJcaProvider(new GoogleCloudSigningService(keystore.getPath(), storepass, alias -> {
                try {
                    return loadCertificateChain(certfile);
                } catch (IOException | CertificateException e) {
                    throw new RuntimeException("Failed to load the certificate from " + certfile, e);
                }
            }));
        }

        if (keystore != null || "YUBIKEY".equals(storetype) || "DIGICERTONE".equals(storetype)) {
            KeyStore ks;
            try {
                ks = KeyStoreUtils.load(keystore, "YUBIKEY".equals(storetype) ? "PKCS11" : storetype, storepass, provider);
            } catch (KeyStoreException e) {
                throw new SignerException("Failed to load the keystore " + keystore, e);
            }

            Set<String> aliases = null;
            if (alias == null) {
                if ("YUBIKEY".equals(storetype)) {
                    alias = "X.509 Certificate for Digital Signature";

                } else {
                    // guess the alias if there is only one in the keystore
                    try {
                        aliases = new LinkedHashSet<>(Collections.list(ks.aliases()));
                    } catch (KeyStoreException e) {
                        throw new SignerException(e.getMessage(), e);
                    }

                    if (aliases.isEmpty()) {
                        throw new SignerException("No certificate found in the keystore " + (provider != null ? provider.getName() : keystore));
                    } else if (aliases.size() == 1) {
                        alias = aliases.iterator().next();
                    } else {
                        throw new SignerException("alias " + parameterName + " must be set to select a certificate (available aliases: " + String.join(", ", aliases) + ")");
                    }
                }
            }

            try {
                chain = ks.getCertificateChain(alias);
            } catch (KeyStoreException e) {
                throw new SignerException(e.getMessage(), e);
            }
            if (chain == null) {
                String message = "No certificate found under the alias '" + alias + "' in the keystore " + (provider != null ? provider.getName() : keystore);
                if (aliases == null) {
                    try {
                        aliases = new LinkedHashSet<>(Collections.list(ks.aliases()));
                        if (aliases.isEmpty()) {
                            message = "No certificate found in the keystore " + (provider != null ? provider.getName() : keystore);
                        } else {
                            message += " (available aliases: " + String.join(", ", aliases) + ")";
                        }
                    } catch (KeyStoreException e) {
                        message += " (couldn't load the list of available aliases: " + e.getMessage() + ")";
                    }
                }
                throw new SignerException(message);
            }
            if (certfile != null && !"GOOGLECLOUD".equals(storetype)) {
                if (chain.length != 1) {
                    throw new SignerException("certfile " + parameterName + " can only be specified if the certificate from the keystore contains only one entry");
                }
                // replace the certificate chain from the keystore with the complete chain from file
                try {
                    Certificate[] chainFromFile = loadCertificateChain(certfile);
                    if (chainFromFile[0].equals(chain[0])) {
                        // replace certificate with complete chain
                        chain = chainFromFile;
                    } else {
                        throw new SignerException("The certificate chain in " + certfile + " does not match the chain from the keystore");
                    }
                } catch (SignerException e) {
                    throw e;
                } catch (Exception e) {
                    throw new SignerException("Failed to load the certificate from " + certfile, e);
                }
            }

            char[] password = keypass != null ? keypass.toCharArray() : storepass.toCharArray();

            try {
                privateKey = (PrivateKey) ks.getKey(alias, password);
            } catch (Exception e) {
                throw new SignerException("Failed to retrieve the private key from the keystore", e);
            }

        } else {
            // separate private key and certificate files (PVK/SPC)
            if (keyfile == null) {
                throw new SignerException("keyfile " + parameterName + " must be set");
            }
            if (!keyfile.exists()) {
                throw new SignerException("The keyfile " + keyfile + " couldn't be found");
            }
            if (certfile == null) {
                throw new SignerException("certfile " + parameterName + " must be set");
            }
            if (!certfile.exists()) {
                throw new SignerException("The certfile " + certfile + " couldn't be found");
            }

            // load the certificate chain
            try {
                chain = loadCertificateChain(certfile);
            } catch (Exception e) {
                throw new SignerException("Failed to load the certificate from " + certfile, e);
            }

            // load the private key
            try {
                privateKey = PrivateKeyUtils.load(keyfile, keypass != null ? keypass : storepass);
            } catch (Exception e) {
                throw new SignerException("Failed to load the private key from " + keyfile, e);
            }
        }

        if (alg != null && DigestAlgorithm.of(alg) == null) {
            throw new SignerException("The digest algorithm " + alg + " is not supported");
        }

        try {
            initializeProxy(proxyUrl, proxyUser, proxyPass);
        } catch (Exception e) {
            throw new SignerException("Couldn't initialize proxy", e);
        }
        
        // configure the signer
        return new AuthenticodeSigner(chain, privateKey)
                .withProgramName(name)
                .withProgramURL(url)
                .withDigestAlgorithm(DigestAlgorithm.of(alg))
                .withSignatureProvider(provider)
                .withSignaturesReplaced(replace)
                .withTimestamping(tsaurl != null || tsmode != null)
                .withTimestampingMode(tsmode != null ? TimestampingMode.of(tsmode) : TimestampingMode.AUTHENTICODE)
                .withTimestampingRetries(tsretries)
                .withTimestampingRetryWait(tsretrywait)
                .withTimestampingAuthority(tsaurl != null ? tsaurl.split(",") : null);
    }