PkiUtil.java
package emissary.util;
import jakarta.xml.bind.DatatypeConverter;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import static java.nio.charset.StandardCharsets.US_ASCII;
public class PkiUtil {
private static final String FILE_PRE = "file://";
private static final Pattern ENV_VARIABLE_PATTERN = Pattern.compile("\\$\\{(\\w+)}");
private static final String BASE_64_ENCODED_DATA = "base64EncodedData";
// get between 64 and 4096 characters of Base64-encoded cert data
private static final Pattern CERT_PATTERN = Pattern.compile(
"^-----BEGIN CERTIFICATE-----$(?<" + BASE_64_ENCODED_DATA + ">(?i)[a-z0-9+/=\\r\\n]{64,4096})^-----END CERTIFICATE-----$",
Pattern.MULTILINE);
private static final Logger log = LoggerFactory.getLogger(PkiUtil.class);
/* build the key/trust store from props */
@Nullable
public static KeyStore buildStore(@Nullable final String path, final char[] pazz, final String type)
throws IOException, GeneralSecurityException {
if ((path == null) || path.isEmpty()) {
return null;
}
final KeyStore keyStore = KeyStore.getInstance(type);
String pemData = FileUtils.readFileToString(new File(path), US_ASCII);
if (isPemCertificate(pemData)) {
loadKeyStore(keyStore, pemData);
} else {
try (InputStream is = Files.newInputStream(Paths.get(path))) {
keyStore.load(is, pazz);
}
}
return keyStore;
}
protected static boolean isPemCertificate(String data) {
return CERT_PATTERN.matcher(data).find();
}
private static void loadKeyStore(KeyStore keyStore, String pemData)
throws CertificateException, IOException, NoSuchAlgorithmException, KeyStoreException {
// initialize the keystore
keyStore.load(null, null);
int certCount = 0;
int matcherPosition = 0;
Matcher matcher = CERT_PATTERN.matcher(pemData);
while (matcher.find(matcherPosition)) {
byte[] derBytes = DatatypeConverter.parseBase64Binary(matcher.group(BASE_64_ENCODED_DATA).trim());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate x509Certificate = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(derBytes));
keyStore.setCertificateEntry("cert_" + certCount++, x509Certificate);
matcherPosition = matcher.end();
}
}
/*
* Build char array from password, load from file or read from environment variable.
*/
@Nullable
public static char[] loadPassword(@Nullable final String password) throws IOException {
if (password == null) {
return null;
}
String realPw;
if (password.startsWith(FILE_PRE)) {
final String pth = password.substring(FILE_PRE.length());
log.debug("Loading key password from file " + pth);
try (BufferedReader r = new BufferedReader(new FileReader(pth))) {
realPw = r.readLine();
}
if (realPw == null) {
throw new IOException("Unable to load store password from " + password);
}
} else {
Matcher matcher = ENV_VARIABLE_PATTERN.matcher(password);
if (matcher.matches()) {
realPw = System.getenv(matcher.group(1));
} else {
realPw = password;
}
}
return realPw.toCharArray();
}
private PkiUtil() {}
}