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