package at.gv.egiz.eaaf.core.impl.credential; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; import at.gv.egiz.eaaf.core.exception.EaafKeyAccessException; import at.gv.egiz.eaaf.core.impl.data.Pair; import lombok.extern.slf4j.Slf4j; @Slf4j public class EaafKeyStoreUtils { private static final String ERROR_MSG_REASON = "Maybe 'Alias' is not valid"; private static final String ERROR_MSG_1 = "Can NOT access key: {} in KeyStore: {}. Reason: {}"; private static final String ERROR_MSG_2 = "Key: {} will be NOT available"; /** * Read all certificates from a {@link KeyStore}. * * @param keyStore KeyStore with certificates * @return {@link List} of {@link X509Certificate}, but never null * @throws KeyStoreException In case of an error during KeyStore operations */ @Nonnull public static List readCertsFromKeyStore(@Nonnull final KeyStore keyStore) throws KeyStoreException { final List result = new ArrayList<>(); final Enumeration aliases = keyStore.aliases(); while (aliases.hasMoreElements()) { final String el = aliases.nextElement(); log.trace("Process TrustStoreEntry: " + el); if (keyStore.isCertificateEntry(el)) { final Certificate cert = keyStore.getCertificate(el); if (cert != null && cert instanceof X509Certificate) { result.add((X509Certificate) cert); } else { log.info("Can not process entry: {}. Reason: {}", el, cert != null ? cert.getType() : "cert is null"); } } } return Collections.unmodifiableList(result); } /** * Get a specific private Key and the corresponding certificate from a {@link KeyStore}. * * @param keyStore KeyStore with certificates * @param keyAlias Alias of the entry * @param keyPassword Password to access the Key * @param isRequired if true, the method throw an {@link EaafKeyAccessException} * if the key is not available or invalid * @param friendlyName Name of the KeyStore for logging purposes * @return A {@link Pair} of {@link Key} and {@link X509Certificate} for this alias, * or maybe null if isRequired was false * @throws EaafKeyAccessException In case of an error during KeyStore operations */ @Nullable public static Pair getPrivateKeyAndCertificates(@Nonnull KeyStore keyStore, @Nonnull String keyAlias, @Nullable char[] keyPassword, boolean isRequired, @Nonnull String friendlyName) throws EaafKeyAccessException { try { Key privKey = keyStore.getKey(keyAlias, keyPassword); if (privKey != null) { final Certificate[] certChainSigning = keyStore.getCertificateChain(keyAlias); X509Certificate[] certChain = new X509Certificate[certChainSigning.length]; for (int i = 0; i < certChainSigning.length; i++) { if (certChainSigning[i] instanceof X509Certificate) { certChain[i] = (X509Certificate) certChainSigning[i]; } else { log.warn("NO X509 certificate for signing: " + certChainSigning[i].getType()); } } Pair keyResult = Pair.newInstance(privKey, certChain); validateKeyResult(keyResult, friendlyName, keyAlias); return keyResult; } else { if (isRequired) { log.warn(ERROR_MSG_1, keyAlias, friendlyName, ERROR_MSG_REASON); throw new EaafKeyAccessException(EaafKeyAccessException.ERROR_CODE_09, friendlyName, keyAlias, ERROR_MSG_REASON); } else { log.info(ERROR_MSG_1, keyAlias, friendlyName, ERROR_MSG_REASON); log.info(ERROR_MSG_2, keyAlias); } } } catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException e) { if (isRequired) { log.warn(ERROR_MSG_1, keyAlias, friendlyName, e.getMessage()); throw new EaafKeyAccessException( EaafKeyAccessException.ERROR_CODE_09, e, friendlyName, keyAlias, e.getMessage()); } else { log.info(ERROR_MSG_1, keyAlias, friendlyName, e.getMessage()); log.info(ERROR_MSG_2, keyAlias); } } return null; } private static void validateKeyResult(Pair keyResult, String friendlyName, String keyAlias) throws EaafKeyAccessException { // some short validation if (!(keyResult.getFirst() instanceof PrivateKey)) { log.info("PrivateKey: {} in KeyStore: {} is of wrong type", keyAlias, friendlyName); throw new EaafKeyAccessException( EaafKeyAccessException.ERROR_CODE_09, friendlyName, keyAlias, "Wrong PrivateKey type "); } if (keyResult.getSecond() == null || keyResult.getSecond().length == 0) { log.info("NO certificate for Key: {} in KeyStore: {}", keyAlias, friendlyName); throw new EaafKeyAccessException( EaafKeyAccessException.ERROR_CODE_09, friendlyName, keyAlias, "NO certificate for PrivateKey"); } } }