1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.handler.ssl;
18
19 import io.netty.buffer.ByteBuf;
20 import io.netty.buffer.Unpooled;
21 import io.netty.handler.codec.base64.Base64;
22 import io.netty.util.CharsetUtil;
23 import io.netty.util.internal.logging.InternalLogger;
24 import io.netty.util.internal.logging.InternalLoggerFactory;
25
26 import java.io.ByteArrayOutputStream;
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.FileNotFoundException;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.security.KeyException;
34 import java.security.KeyStore;
35 import java.security.cert.CertificateException;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.regex.Matcher;
39 import java.util.regex.Pattern;
40
41
42
43
44 final class PemReader {
45
46 private static final InternalLogger logger = InternalLoggerFactory.getInstance(PemReader.class);
47
48 private static final Pattern CERT_PATTERN = Pattern.compile(
49 "-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)+" +
50 "([a-z0-9+/=\\r\\n]+)" +
51 "-+END\\s+.*CERTIFICATE[^-]*-+",
52 Pattern.CASE_INSENSITIVE);
53 private static final Pattern KEY_PATTERN = Pattern.compile(
54 "-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" +
55 "([a-z0-9+/=\\r\\n]+)" +
56 "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+",
57 Pattern.CASE_INSENSITIVE);
58
59 static ByteBuf[] readCertificates(File file) throws CertificateException {
60 try {
61 InputStream in = new FileInputStream(file);
62
63 try {
64 return readCertificates(in);
65 } finally {
66 safeClose(in);
67 }
68 } catch (FileNotFoundException e) {
69 throw new CertificateException("could not find certificate file: " + file);
70 }
71 }
72
73 static ByteBuf[] readCertificates(InputStream in) throws CertificateException {
74 String content;
75 try {
76 content = readContent(in);
77 } catch (IOException e) {
78 throw new CertificateException("failed to read certificate input stream", e);
79 }
80
81 List<ByteBuf> certs = new ArrayList<ByteBuf>();
82 Matcher m = CERT_PATTERN.matcher(content);
83 int start = 0;
84 for (;;) {
85 if (!m.find(start)) {
86 break;
87 }
88
89 ByteBuf base64 = Unpooled.copiedBuffer(m.group(1), CharsetUtil.US_ASCII);
90 ByteBuf der = Base64.decode(base64);
91 base64.release();
92 certs.add(der);
93
94 start = m.end();
95 }
96
97 if (certs.isEmpty()) {
98 throw new CertificateException("found no certificates in input stream");
99 }
100
101 return certs.toArray(new ByteBuf[certs.size()]);
102 }
103
104 static ByteBuf readPrivateKey(File file) throws KeyException {
105 try {
106 InputStream in = new FileInputStream(file);
107
108 try {
109 return readPrivateKey(in);
110 } finally {
111 safeClose(in);
112 }
113 } catch (FileNotFoundException e) {
114 throw new KeyException("could not find key file: " + file);
115 }
116 }
117
118 static ByteBuf readPrivateKey(InputStream in) throws KeyException {
119 String content;
120 try {
121 content = readContent(in);
122 } catch (IOException e) {
123 throw new KeyException("failed to read key input stream", e);
124 }
125
126 Matcher m = KEY_PATTERN.matcher(content);
127 if (!m.find()) {
128 throw new KeyException("could not find a PKCS #8 private key in input stream" +
129 " (see http://netty.io/wiki/sslcontextbuilder-and-private-key.html for more information)");
130 }
131
132 ByteBuf base64 = Unpooled.copiedBuffer(m.group(1), CharsetUtil.US_ASCII);
133 ByteBuf der = Base64.decode(base64);
134 base64.release();
135 return der;
136 }
137
138 private static String readContent(InputStream in) throws IOException {
139 ByteArrayOutputStream out = new ByteArrayOutputStream();
140 try {
141 byte[] buf = new byte[8192];
142 for (;;) {
143 int ret = in.read(buf);
144 if (ret < 0) {
145 break;
146 }
147 out.write(buf, 0, ret);
148 }
149 return out.toString(CharsetUtil.US_ASCII.name());
150 } finally {
151 safeClose(out);
152 }
153 }
154
155 private static void safeClose(InputStream in) {
156 try {
157 in.close();
158 } catch (IOException e) {
159 logger.warn("Failed to close a stream.", e);
160 }
161 }
162
163 private static void safeClose(OutputStream out) {
164 try {
165 out.close();
166 } catch (IOException e) {
167 logger.warn("Failed to close a stream.", e);
168 }
169 }
170
171 private PemReader() { }
172 }