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