|
12 | 12 | */ |
13 | 13 | package io.kubernetes.client.util; |
14 | 14 |
|
15 | | -import java.io.BufferedReader; |
16 | 15 | import java.io.ByteArrayInputStream; |
17 | 16 | import java.io.File; |
18 | 17 | import java.io.FileInputStream; |
19 | 18 | import java.io.IOException; |
20 | 19 | import java.io.InputStream; |
21 | 20 | import java.io.InputStreamReader; |
22 | 21 | import java.io.StringWriter; |
23 | | -import java.math.BigInteger; |
24 | | -import java.security.KeyFactory; |
| 22 | +import java.security.KeyPair; |
25 | 23 | import java.security.KeyStore; |
26 | 24 | import java.security.KeyStoreException; |
27 | 25 | import java.security.NoSuchAlgorithmException; |
|
33 | 31 | import java.security.cert.CertificateFactory; |
34 | 32 | import java.security.cert.X509Certificate; |
35 | 33 | import java.security.spec.InvalidKeySpecException; |
36 | | -import java.security.spec.PKCS8EncodedKeySpec; |
37 | | -import java.security.spec.RSAPrivateCrtKeySpec; |
38 | 34 | import java.util.Collection; |
39 | 35 | import javax.net.ssl.KeyManager; |
40 | 36 | import javax.net.ssl.KeyManagerFactory; |
41 | | -import org.apache.commons.codec.binary.Base64; |
| 37 | +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; |
42 | 38 | import org.bouncycastle.openssl.PEMKeyPair; |
43 | 39 | import org.bouncycastle.openssl.PEMParser; |
44 | 40 | import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator; |
@@ -117,54 +113,43 @@ public static String recognizePrivateKeyAlgo(byte[] privateKeyBytes) { |
117 | 113 | } |
118 | 114 |
|
119 | 115 | public static PrivateKey loadKey(byte[] privateKeyBytes) |
120 | | - throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { |
| 116 | + throws IOException, InvalidKeySpecException { |
121 | 117 | return loadKey( |
122 | 118 | new ByteArrayInputStream(privateKeyBytes), recognizePrivateKeyAlgo(privateKeyBytes)); |
123 | 119 | } |
124 | 120 |
|
125 | 121 | public static PrivateKey loadKey(byte[] pemPrivateKeyBytes, String algo) |
126 | | - throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { |
| 122 | + throws IOException, InvalidKeySpecException { |
127 | 123 | return loadKey(new ByteArrayInputStream(pemPrivateKeyBytes), algo); |
128 | 124 | } |
129 | 125 |
|
130 | 126 | public static PrivateKey loadKey(InputStream keyInputStream, String clientKeyAlgo) |
131 | | - throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { |
132 | | - |
133 | | - // Try PKCS7 / EC |
134 | | - if (clientKeyAlgo.equals("EC")) { |
135 | | - Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); |
136 | | - PEMParser pemParser = new PEMParser(new InputStreamReader(keyInputStream)); |
137 | | - Object pemObject; |
138 | | - while ((pemObject = pemParser.readObject()) != null) { |
139 | | - if (pemObject instanceof PEMKeyPair) { |
140 | | - return new JcaPEMKeyConverter().getKeyPair(((PEMKeyPair) pemObject)).getPrivate(); |
141 | | - } |
| 127 | + throws IOException, InvalidKeySpecException { |
| 128 | + final PrivateKey privateKey; |
| 129 | + try (final PEMParser pemParser = new PEMParser(new InputStreamReader(keyInputStream))) { |
| 130 | + final Object pemObject = pemParser.readObject(); |
| 131 | + if (pemObject == null) { |
| 132 | + final String message = String.format("PEM Private Key Algorithm [%s] not parsed", clientKeyAlgo); |
| 133 | + throw new InvalidKeySpecException(message); |
| 134 | + } |
| 135 | + final JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); |
| 136 | + if (pemObject instanceof PEMKeyPair) { |
| 137 | + final PEMKeyPair pemKeyPair = (PEMKeyPair) pemObject; |
| 138 | + final KeyPair keyPair = converter.getKeyPair(pemKeyPair); |
| 139 | + privateKey = keyPair.getPrivate(); |
| 140 | + } else if (pemObject instanceof PrivateKeyInfo) { |
| 141 | + final PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) pemObject; |
| 142 | + privateKey = converter.getPrivateKey(privateKeyInfo); |
| 143 | + } else { |
| 144 | + final String pemObjectType = pemObject.getClass().getSimpleName(); |
| 145 | + final String message = String.format("PEM Private Key Algorithm [%s] Type [%s] not supported", |
| 146 | + clientKeyAlgo, |
| 147 | + pemObjectType |
| 148 | + ); |
| 149 | + throw new InvalidKeySpecException(message); |
142 | 150 | } |
143 | 151 | } |
144 | | - |
145 | | - byte[] keyBytes = decodePem(keyInputStream); |
146 | | - |
147 | | - // Try PKCS1 / RSA |
148 | | - if (clientKeyAlgo.equals("RSA")) { |
149 | | - RSAPrivateCrtKeySpec keySpec = decodePKCS1(keyBytes); |
150 | | - return KeyFactory.getInstance("RSA").generatePrivate(keySpec); |
151 | | - } |
152 | | - |
153 | | - // Try PKCS8 |
154 | | - // TODO: There _has_ to be a better way to do this, but I spent > |
155 | | - // 2 hours trying to find it and failed... |
156 | | - PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); |
157 | | - try { |
158 | | - return KeyFactory.getInstance("RSA").generatePrivate(spec); |
159 | | - } catch (InvalidKeySpecException ex) { |
160 | | - // ignore if it's not RSA |
161 | | - } |
162 | | - try { |
163 | | - return KeyFactory.getInstance("ECDSA").generatePrivate(spec); |
164 | | - } catch (InvalidKeySpecException ex) { |
165 | | - // ignore if it's not DSA |
166 | | - } |
167 | | - throw new InvalidKeySpecException("Unknown type of PKCS8 Private Key, tried RSA and ECDSA"); |
| 152 | + return privateKey; |
168 | 153 | } |
169 | 154 |
|
170 | 155 | public static KeyStore createKeyStore( |
@@ -205,140 +190,6 @@ public static KeyStore createKeyStore( |
205 | 190 | return keyStore; |
206 | 191 | } |
207 | 192 |
|
208 | | - // This method is inspired and partly taken over from |
209 | | - // http://oauth.googlecode.com/svn/code/java/ |
210 | | - // All credits to belong to them. |
211 | | - private static byte[] decodePem(InputStream keyInputStream) throws IOException { |
212 | | - BufferedReader reader = new BufferedReader(new InputStreamReader(keyInputStream)); |
213 | | - try { |
214 | | - String line; |
215 | | - while ((line = reader.readLine()) != null) { |
216 | | - if (line.contains("-----BEGIN ")) { |
217 | | - return readBytes(reader, line.trim().replace("BEGIN", "END")); |
218 | | - } |
219 | | - } |
220 | | - throw new IOException("PEM is invalid: no begin marker"); |
221 | | - } finally { |
222 | | - reader.close(); |
223 | | - } |
224 | | - } |
225 | | - |
226 | | - private static byte[] readBytes(BufferedReader reader, String endMarker) throws IOException { |
227 | | - String line; |
228 | | - StringBuffer buf = new StringBuffer(); |
229 | | - |
230 | | - while ((line = reader.readLine()) != null) { |
231 | | - if (line.indexOf(endMarker) != -1) { |
232 | | - return Base64.decodeBase64(buf.toString()); |
233 | | - } |
234 | | - buf.append(line.trim()); |
235 | | - } |
236 | | - throw new IOException("PEM is invalid : No end marker"); |
237 | | - } |
238 | | - |
239 | | - public static RSAPrivateCrtKeySpec decodePKCS1(byte[] keyBytes) throws IOException { |
240 | | - DerParser parser = new DerParser(keyBytes); |
241 | | - Asn1Object sequence = parser.read(); |
242 | | - sequence.validateSequence(); |
243 | | - parser = new DerParser(sequence.getValue()); |
244 | | - parser.read(); |
245 | | - |
246 | | - return new RSAPrivateCrtKeySpec( |
247 | | - next(parser), |
248 | | - next(parser), |
249 | | - next(parser), |
250 | | - next(parser), |
251 | | - next(parser), |
252 | | - next(parser), |
253 | | - next(parser), |
254 | | - next(parser)); |
255 | | - } |
256 | | - |
257 | | - private static BigInteger next(DerParser parser) throws IOException { |
258 | | - return parser.read().getInteger(); |
259 | | - } |
260 | | - |
261 | | - static class DerParser { |
262 | | - |
263 | | - private InputStream in; |
264 | | - |
265 | | - DerParser(byte[] bytes) throws IOException { |
266 | | - this.in = new ByteArrayInputStream(bytes); |
267 | | - } |
268 | | - |
269 | | - Asn1Object read() throws IOException { |
270 | | - int tag = in.read(); |
271 | | - |
272 | | - if (tag == -1) { |
273 | | - throw new IOException("Invalid DER: stream too short, missing tag"); |
274 | | - } |
275 | | - |
276 | | - int length = getLength(); |
277 | | - byte[] value = new byte[length]; |
278 | | - if (in.read(value) < length) { |
279 | | - throw new IOException("Invalid DER: stream too short, missing value"); |
280 | | - } |
281 | | - |
282 | | - return new Asn1Object(tag, value); |
283 | | - } |
284 | | - |
285 | | - private int getLength() throws IOException { |
286 | | - int i = in.read(); |
287 | | - if (i == -1) { |
288 | | - throw new IOException("Invalid DER: length missing"); |
289 | | - } |
290 | | - |
291 | | - if ((i & ~0x7F) == 0) { |
292 | | - return i; |
293 | | - } |
294 | | - |
295 | | - int num = i & 0x7F; |
296 | | - if (i >= 0xFF || num > 4) { |
297 | | - throw new IOException("Invalid DER: length field too big (" + i + ")"); |
298 | | - } |
299 | | - |
300 | | - byte[] bytes = new byte[num]; |
301 | | - if (in.read(bytes) < num) { |
302 | | - throw new IOException("Invalid DER: length too short"); |
303 | | - } |
304 | | - |
305 | | - return new BigInteger(1, bytes).intValue(); |
306 | | - } |
307 | | - } |
308 | | - |
309 | | - static class Asn1Object { |
310 | | - |
311 | | - private final int type; |
312 | | - private final byte[] value; |
313 | | - private final int tag; |
314 | | - |
315 | | - public Asn1Object(int tag, byte[] value) { |
316 | | - this.tag = tag; |
317 | | - this.type = tag & 0x1F; |
318 | | - this.value = value; |
319 | | - } |
320 | | - |
321 | | - public byte[] getValue() { |
322 | | - return value; |
323 | | - } |
324 | | - |
325 | | - BigInteger getInteger() throws IOException { |
326 | | - if (type != 0x02) { |
327 | | - throw new IOException("Invalid DER: object is not integer"); // $NON-NLS-1$ |
328 | | - } |
329 | | - return new BigInteger(value); |
330 | | - } |
331 | | - |
332 | | - void validateSequence() throws IOException { |
333 | | - if (type != 0x10) { |
334 | | - throw new IOException("Invalid DER: not a sequence"); |
335 | | - } |
336 | | - if ((tag & 0x20) != 0x20) { |
337 | | - throw new IOException("Invalid DER: can't parse primitive entity"); |
338 | | - } |
339 | | - } |
340 | | - } |
341 | | - |
342 | 193 | private static void loadDefaultKeyStoreFile(KeyStore keyStore, char[] keyStorePassphrase) |
343 | 194 | throws CertificateException, NoSuchAlgorithmException, IOException { |
344 | 195 |
|
|
0 commit comments