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_HEADER = Pattern.compile(
49 "-+BEGIN\\s[^-\\r\\n]*CERTIFICATE[^-\\r\\n]*-+(?:\\s|\\r|\\n)+");
50 private static final Pattern CERT_FOOTER = Pattern.compile(
51 "-+END\\s[^-\\r\\n]*CERTIFICATE[^-\\r\\n]*-+(?:\\s|\\r|\\n)*");
52 private static final Pattern KEY_HEADER = Pattern.compile(
53 "-+BEGIN\\s[^-\\r\\n]*PRIVATE\\s+KEY[^-\\r\\n]*-+(?:\\s|\\r|\\n)+");
54 private static final Pattern KEY_FOOTER = Pattern.compile(
55 "-+END\\s[^-\\r\\n]*PRIVATE\\s+KEY[^-\\r\\n]*-+(?:\\s|\\r|\\n)*");
56 private static final Pattern BODY = Pattern.compile("[a-z0-9+/=][a-z0-9+/=\\r\\n]*", Pattern.CASE_INSENSITIVE);
57
58 static ByteBuf[] readCertificates(File file) throws CertificateException {
59 try {
60 InputStream in = new FileInputStream(file);
61
62 try {
63 return readCertificates(in);
64 } finally {
65 safeClose(in);
66 }
67 } catch (FileNotFoundException e) {
68 throw new CertificateException("could not find certificate file: " + file);
69 }
70 }
71
72 static ByteBuf[] readCertificates(InputStream in) throws CertificateException {
73 String content;
74 try {
75 content = readContent(in);
76 } catch (IOException e) {
77 throw new CertificateException("failed to read certificate input stream", e);
78 }
79
80 List<ByteBuf> certs = new ArrayList<ByteBuf>();
81 Matcher m = CERT_HEADER.matcher(content);
82 int start = 0;
83 for (;;) {
84 if (!m.find(start)) {
85 break;
86 }
87
88
89
90
91
92 start = m.end();
93 m.usePattern(BODY);
94 if (!m.find(start)) {
95 break;
96 }
97
98 ByteBuf base64 = Unpooled.copiedBuffer(m.group(0), CharsetUtil.US_ASCII);
99 start = m.end();
100 m.usePattern(CERT_FOOTER);
101 if (!m.find(start)) {
102
103 break;
104 }
105 ByteBuf der = Base64.decode(base64);
106 base64.release();
107 certs.add(der);
108
109 start = m.end();
110 m.usePattern(CERT_HEADER);
111 }
112
113 if (certs.isEmpty()) {
114 throw new CertificateException("found no certificates in input stream");
115 }
116
117 return certs.toArray(new ByteBuf[0]);
118 }
119
120 static ByteBuf readPrivateKey(File file) throws KeyException {
121 try {
122 InputStream in = new FileInputStream(file);
123
124 try {
125 return readPrivateKey(in);
126 } finally {
127 safeClose(in);
128 }
129 } catch (FileNotFoundException e) {
130 throw new KeyException("could not find key file: " + file);
131 }
132 }
133
134 static ByteBuf readPrivateKey(InputStream in) throws KeyException {
135 String content;
136 try {
137 content = readContent(in);
138 } catch (IOException e) {
139 throw new KeyException("failed to read key input stream", e);
140 }
141 int start = 0;
142 Matcher m = KEY_HEADER.matcher(content);
143 if (!m.find(start)) {
144 throw keyNotFoundException();
145 }
146 start = m.end();
147 m.usePattern(BODY);
148 if (!m.find(start)) {
149 throw keyNotFoundException();
150 }
151
152 ByteBuf base64 = Unpooled.copiedBuffer(m.group(0), CharsetUtil.US_ASCII);
153 start = m.end();
154 m.usePattern(KEY_FOOTER);
155 if (!m.find(start)) {
156
157 throw keyNotFoundException();
158 }
159 ByteBuf der = Base64.decode(base64);
160 base64.release();
161 return der;
162 }
163
164 private static KeyException keyNotFoundException() {
165 return new KeyException("could not find a PKCS #8 private key in input stream" +
166 " (see https://netty.io/wiki/sslcontextbuilder-and-private-key.html for more information)");
167 }
168
169 private static String readContent(InputStream in) throws IOException {
170 ByteArrayOutputStream out = new ByteArrayOutputStream();
171 try {
172 byte[] buf = new byte[8192];
173 for (;;) {
174 int ret = in.read(buf);
175 if (ret < 0) {
176 break;
177 }
178 out.write(buf, 0, ret);
179 }
180 return out.toString(CharsetUtil.US_ASCII.name());
181 } finally {
182 safeClose(out);
183 }
184 }
185
186 private static void safeClose(InputStream in) {
187 try {
188 in.close();
189 } catch (IOException e) {
190 logger.warn("Failed to close a stream.", e);
191 }
192 }
193
194 private static void safeClose(OutputStream out) {
195 try {
196 out.close();
197 } catch (IOException e) {
198 logger.warn("Failed to close a stream.", e);
199 }
200 }
201
202 private PemReader() { }
203 }