com.firefly.net.tcp.secure.openssl.nativelib.PemReader Maven / Gradle / Ivy
package com.firefly.net.tcp.secure.openssl.nativelib;
import com.firefly.utils.codec.Base64;
import com.firefly.utils.io.BufferUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.KeyException;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Reads a PEM file and converts it into a list of DERs so that they are imported into a {@link KeyStore} easily.
*/
final class PemReader {
private static final Logger logger = LoggerFactory.getLogger("firefly-system");
private static final Pattern CERT_PATTERN = Pattern.compile(
"-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)+" + // Header
"([a-z0-9+/=\\r\\n]+)" + // Base64 text
"-+END\\s+.*CERTIFICATE[^-]*-+", // Footer
Pattern.CASE_INSENSITIVE);
private static final Pattern KEY_PATTERN = Pattern.compile(
"-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" + // Header
"([a-z0-9+/=\\r\\n]+)" + // Base64 text
"-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", // Footer
Pattern.CASE_INSENSITIVE);
static ByteBuffer[] readCertificates(File file) throws CertificateException {
try {
InputStream in = new FileInputStream(file);
try {
return readCertificates(in);
} finally {
safeClose(in);
}
} catch (FileNotFoundException e) {
throw new CertificateException("could not find certificate file: " + file);
}
}
static ByteBuffer[] readCertificates(InputStream in) throws CertificateException {
String content;
try {
content = readContent(in);
} catch (IOException e) {
throw new CertificateException("failed to read certificate input stream", e);
}
List certs = new ArrayList<>();
Matcher m = CERT_PATTERN.matcher(content);
int start = 0;
for (; ; ) {
if (!m.find(start)) {
break;
}
byte[] base64 = m.group(1).getBytes(StandardCharsets.US_ASCII);
byte[] base64Decoded = Base64.decodeBase64(base64);
ByteBuffer tmp = ByteBuffer.allocateDirect(base64Decoded.length);
tmp.put(base64Decoded).flip();
certs.add(tmp);
start = m.end();
}
if (certs.isEmpty()) {
throw new CertificateException("found no certificates in input stream");
}
return certs.toArray(BufferUtils.EMPTY_BYTE_BUFFER_ARRAY);
}
static ByteBuffer readPrivateKey(File file) throws KeyException {
try {
InputStream in = new FileInputStream(file);
try {
return readPrivateKey(in);
} finally {
safeClose(in);
}
} catch (FileNotFoundException e) {
throw new KeyException("could not find key file: " + file);
}
}
static ByteBuffer readPrivateKey(InputStream in) throws KeyException {
String content;
try {
content = readContent(in);
} catch (IOException e) {
throw new KeyException("failed to read key input stream", e);
}
Matcher m = KEY_PATTERN.matcher(content);
if (!m.find()) {
throw new KeyException("could not find a PKCS #8 private key in input stream" +
" (see http://netty.io/wiki/sslcontextbuilder-and-private-key.html for more information)");
}
byte[] base64 = m.group(1).getBytes(StandardCharsets.US_ASCII);
byte[] base64Decoded = Base64.decodeBase64(base64);
ByteBuffer tmp = ByteBuffer.allocateDirect(base64Decoded.length);
tmp.put(base64Decoded).flip();
return tmp;
}
private static String readContent(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
byte[] buf = new byte[8192];
for (; ; ) {
int ret = in.read(buf);
if (ret < 0) {
break;
}
out.write(buf, 0, ret);
}
return out.toString(StandardCharsets.US_ASCII.name());
} finally {
safeClose(out);
}
}
private static void safeClose(InputStream in) {
try {
in.close();
} catch (IOException e) {
logger.warn("Failed to close a stream.", e);
}
}
private static void safeClose(OutputStream out) {
try {
out.close();
} catch (IOException e) {
logger.warn("Failed to close a stream.", e);
}
}
private PemReader() {
}
}