1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.ssl;
17
18 import io.netty.util.CharsetUtil;
19 import io.netty.util.internal.logging.InternalLogger;
20 import io.netty.util.internal.logging.InternalLoggerFactory;
21 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
22 import org.bouncycastle.openssl.PEMDecryptorProvider;
23 import org.bouncycastle.openssl.PEMEncryptedKeyPair;
24 import org.bouncycastle.openssl.PEMKeyPair;
25 import org.bouncycastle.openssl.PEMParser;
26 import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
27 import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
28 import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
29 import org.bouncycastle.operator.InputDecryptorProvider;
30 import org.bouncycastle.operator.OperatorCreationException;
31 import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
32 import org.bouncycastle.pkcs.PKCSException;
33
34 import java.io.File;
35 import java.io.FileNotFoundException;
36 import java.io.FileReader;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.InputStreamReader;
40 import java.security.AccessController;
41 import java.security.PrivateKey;
42 import java.security.PrivilegedAction;
43 import java.security.Provider;
44
45 final class BouncyCastlePemReader {
46 private static final String BC_PROVIDER = "org.bouncycastle.jce.provider.BouncyCastleProvider";
47 private static final String BC_PEMPARSER = "org.bouncycastle.openssl.PEMParser";
48 private static final InternalLogger logger = InternalLoggerFactory.getInstance(BouncyCastlePemReader.class);
49
50 private static volatile Throwable unavailabilityCause;
51 private static volatile Provider bcProvider;
52 private static volatile boolean attemptedLoading;
53
54 public static boolean hasAttemptedLoading() {
55 return attemptedLoading;
56 }
57
58 public static boolean isAvailable() {
59 if (!hasAttemptedLoading()) {
60 tryLoading();
61 }
62 return unavailabilityCause == null;
63 }
64
65
66
67
68 public static Throwable unavailabilityCause() {
69 return unavailabilityCause;
70 }
71
72 private static void tryLoading() {
73 AccessController.doPrivileged(new PrivilegedAction<Void>() {
74 @Override
75 public Void run() {
76 try {
77 ClassLoader classLoader = getClass().getClassLoader();
78
79 Class<Provider> bcProviderClass =
80 (Class<Provider>) Class.forName(BC_PROVIDER, true, classLoader);
81
82 Class.forName(BC_PEMPARSER, true, classLoader);
83 bcProvider = bcProviderClass.getConstructor().newInstance();
84 logger.debug("Bouncy Castle provider available");
85 attemptedLoading = true;
86 } catch (Throwable e) {
87 logger.debug("Cannot load Bouncy Castle provider", e);
88 unavailabilityCause = e;
89 attemptedLoading = true;
90 }
91 return null;
92 }
93 });
94 }
95
96
97
98
99
100
101
102
103
104 public static PrivateKey getPrivateKey(InputStream keyInputStream, String keyPassword) {
105 if (!isAvailable()) {
106 if (logger.isDebugEnabled()) {
107 logger.debug("Bouncy castle provider is unavailable.", unavailabilityCause());
108 }
109 return null;
110 }
111 try {
112 PEMParser parser = newParser(keyInputStream);
113 return getPrivateKey(parser, keyPassword);
114 } catch (Exception e) {
115 logger.debug("Unable to extract private key", e);
116 return null;
117 }
118 }
119
120
121
122
123
124
125
126
127
128 public static PrivateKey getPrivateKey(File keyFile, String keyPassword) {
129 if (!isAvailable()) {
130 if (logger.isDebugEnabled()) {
131 logger.debug("Bouncy castle provider is unavailable.", unavailabilityCause());
132 }
133 return null;
134 }
135 try {
136 PEMParser parser = newParser(keyFile);
137 return getPrivateKey(parser, keyPassword);
138 } catch (Exception e) {
139 logger.debug("Unable to extract private key", e);
140 return null;
141 }
142 }
143
144 private static JcaPEMKeyConverter newConverter() {
145 return new JcaPEMKeyConverter().setProvider(bcProvider);
146 }
147
148 private static PrivateKey getPrivateKey(PEMParser pemParser, String keyPassword) throws IOException,
149 PKCSException, OperatorCreationException {
150 try {
151 JcaPEMKeyConverter converter = newConverter();
152 PrivateKey pk = null;
153
154 Object object = pemParser.readObject();
155 while (object != null && pk == null) {
156 if (logger.isDebugEnabled()) {
157 logger.debug("Parsed PEM object of type {} and assume " +
158 "key is {}encrypted", object.getClass().getName(), keyPassword == null? "not " : "");
159 }
160
161 if (keyPassword == null) {
162
163 if (object instanceof PrivateKeyInfo) {
164 pk = converter.getPrivateKey((PrivateKeyInfo) object);
165 } else if (object instanceof PEMKeyPair) {
166 pk = converter.getKeyPair((PEMKeyPair) object).getPrivate();
167 } else {
168 logger.debug("Unable to handle PEM object of type {} as a non encrypted key",
169 object.getClass());
170 }
171 } else {
172
173 if (object instanceof PEMEncryptedKeyPair) {
174 PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder()
175 .setProvider(bcProvider)
176 .build(keyPassword.toCharArray());
177 pk = converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv)).getPrivate();
178 } else if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
179 InputDecryptorProvider pkcs8InputDecryptorProvider =
180 new JceOpenSSLPKCS8DecryptorProviderBuilder()
181 .setProvider(bcProvider)
182 .build(keyPassword.toCharArray());
183 pk = converter.getPrivateKey(((PKCS8EncryptedPrivateKeyInfo) object)
184 .decryptPrivateKeyInfo(pkcs8InputDecryptorProvider));
185 } else {
186 logger.debug("Unable to handle PEM object of type {} as a encrypted key", object.getClass());
187 }
188 }
189
190
191 if (pk == null) {
192 object = pemParser.readObject();
193 }
194 }
195
196 if (pk == null) {
197 if (logger.isDebugEnabled()) {
198 logger.debug("No key found");
199 }
200 }
201
202 return pk;
203 } finally {
204 if (pemParser != null) {
205 try {
206 pemParser.close();
207 } catch (Exception exception) {
208 logger.debug("Failed closing pem parser", exception);
209 }
210 }
211 }
212 }
213
214 private static PEMParser newParser(File keyFile) throws FileNotFoundException {
215 return new PEMParser(new FileReader(keyFile));
216 }
217
218 private static PEMParser newParser(InputStream keyInputStream) {
219 return new PEMParser(new InputStreamReader(keyInputStream, CharsetUtil.US_ASCII));
220 }
221
222 private BouncyCastlePemReader() { }
223 }