io.netty.handler.ssl.PemReader Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.ssl;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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 InternalLogger logger = InternalLoggerFactory.getInstance(PemReader.class);
private static final Pattern CERT_HEADER = Pattern.compile(
"-+BEGIN\\s[^-\\r\\n]*CERTIFICATE[^-\\r\\n]*-+(?:\\s|\\r|\\n)+");
private static final Pattern CERT_FOOTER = Pattern.compile(
"-+END\\s[^-\\r\\n]*CERTIFICATE[^-\\r\\n]*-+(?:\\s|\\r|\\n)*");
private static final Pattern KEY_HEADER = Pattern.compile(
"-+BEGIN\\s[^-\\r\\n]*PRIVATE\\s+KEY[^-\\r\\n]*-+(?:\\s|\\r|\\n)+");
private static final Pattern KEY_FOOTER = Pattern.compile(
"-+END\\s[^-\\r\\n]*PRIVATE\\s+KEY[^-\\r\\n]*-+(?:\\s|\\r|\\n)*");
private static final Pattern BODY = Pattern.compile("[a-z0-9+/=][a-z0-9+/=\\r\\n]*", Pattern.CASE_INSENSITIVE);
static ByteBuf[] 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 ByteBuf[] 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_HEADER.matcher(content);
int start = 0;
for (;;) {
if (!m.find(start)) {
break;
}
m.usePattern(BODY);
if (!m.find()) {
break;
}
ByteBuf base64 = Unpooled.copiedBuffer(m.group(0), CharsetUtil.US_ASCII);
m.usePattern(CERT_FOOTER);
if (!m.find()) {
// Certificate is incomplete.
break;
}
ByteBuf der = Base64.decode(base64);
base64.release();
certs.add(der);
start = m.end();
m.usePattern(CERT_HEADER);
}
if (certs.isEmpty()) {
throw new CertificateException("found no certificates in input stream");
}
return certs.toArray(new ByteBuf[0]);
}
static ByteBuf 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 ByteBuf 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_HEADER.matcher(content);
if (!m.find()) {
throw keyNotFoundException();
}
m.usePattern(BODY);
if (!m.find()) {
throw keyNotFoundException();
}
ByteBuf base64 = Unpooled.copiedBuffer(m.group(0), CharsetUtil.US_ASCII);
m.usePattern(KEY_FOOTER);
if (!m.find()) {
// Key is incomplete.
throw keyNotFoundException();
}
ByteBuf der = Base64.decode(base64);
base64.release();
return der;
}
private static KeyException keyNotFoundException() {
return new KeyException("could not find a PKCS #8 private key in input stream" +
" (see https://netty.io/wiki/sslcontextbuilder-and-private-key.html for more information)");
}
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(CharsetUtil.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() { }
}