comparison src/ch/ethz/ssh2/crypto/PEMDecoder.java @ 275:03ae56b26003 ganymed

start conversion from trilead to ganymed
author Carl Byington <carl@five-ten-sg.com>
date Fri, 18 Jul 2014 11:56:27 -0700
parents 91a31873c42a
children 3a1deb1040f6
comparison
equal deleted inserted replaced
274:82bd20cb0d1d 275:03ae56b26003
1 /* 1
2 * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. 2 package com.trilead.ssh2.crypto;
3 * Please refer to the LICENSE.txt for licensing details.
4 */
5 package ch.ethz.ssh2.crypto;
6 3
7 import java.io.BufferedReader; 4 import java.io.BufferedReader;
8 import java.io.CharArrayReader; 5 import java.io.CharArrayReader;
9 import java.io.IOException; 6 import java.io.IOException;
10 import java.math.BigInteger; 7 import java.math.BigInteger;
11 import java.security.DigestException; 8 import java.security.DigestException;
12 9 import java.security.KeyFactory;
13 import ch.ethz.ssh2.crypto.cipher.AES; 10 import java.security.KeyPair;
14 import ch.ethz.ssh2.crypto.cipher.BlockCipher; 11 import java.security.MessageDigest;
15 import ch.ethz.ssh2.crypto.cipher.CBCMode; 12 import java.security.NoSuchAlgorithmException;
16 import ch.ethz.ssh2.crypto.cipher.DES; 13 import java.security.PrivateKey;
17 import ch.ethz.ssh2.crypto.cipher.DESede; 14 import java.security.PublicKey;
18 import ch.ethz.ssh2.crypto.digest.MD5; 15 import java.security.spec.DSAPrivateKeySpec;
19 import ch.ethz.ssh2.signature.DSAPrivateKey; 16 import java.security.spec.DSAPublicKeySpec;
20 import ch.ethz.ssh2.signature.RSAPrivateKey; 17 import java.security.spec.ECParameterSpec;
21 import ch.ethz.ssh2.util.StringEncoder; 18 import java.security.spec.ECPoint;
19 import java.security.spec.ECPrivateKeySpec;
20 import java.security.spec.ECPublicKeySpec;
21 import java.security.spec.InvalidKeySpecException;
22 import java.security.spec.KeySpec;
23 import java.security.spec.RSAPrivateCrtKeySpec;
24 import java.security.spec.RSAPrivateKeySpec;
25 import java.security.spec.RSAPublicKeySpec;
26
27 import com.trilead.ssh2.crypto.cipher.AES;
28 import com.trilead.ssh2.crypto.cipher.BlockCipher;
29 import com.trilead.ssh2.crypto.cipher.CBCMode;
30 import com.trilead.ssh2.crypto.cipher.DES;
31 import com.trilead.ssh2.crypto.cipher.DESede;
32 import com.trilead.ssh2.signature.ECDSASHA2Verify;
22 33
23 /** 34 /**
24 * PEM Support. 35 * PEM Support.
25 * 36 *
26 * @author Christian Plattner 37 * @author Christian Plattner, plattner@trilead.com
27 * @version $Id: PEMDecoder.java 154 2014-04-28 11:45:02Z dkocher@sudo.ch $ 38 * @version $Id: PEMDecoder.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
28 */ 39 */
29 public class PEMDecoder 40 public class PEMDecoder {
30 { 41 public static final int PEM_RSA_PRIVATE_KEY = 1;
31 private static final int PEM_RSA_PRIVATE_KEY = 1; 42 public static final int PEM_DSA_PRIVATE_KEY = 2;
32 private static final int PEM_DSA_PRIVATE_KEY = 2; 43 public static final int PEM_EC_PRIVATE_KEY = 3;
33 44
34 private static int hexToInt(char c) 45 private static final int hexToInt(char c) {
35 { 46 if ((c >= 'a') && (c <= 'f')) {
36 if ((c >= 'a') && (c <= 'f')) 47 return (c - 'a') + 10;
37 { 48 }
38 return (c - 'a') + 10; 49
39 } 50 if ((c >= 'A') && (c <= 'F')) {
40 51 return (c - 'A') + 10;
41 if ((c >= 'A') && (c <= 'F')) 52 }
42 { 53
43 return (c - 'A') + 10; 54 if ((c >= '0') && (c <= '9')) {
44 } 55 return (c - '0');
45 56 }
46 if ((c >= '0') && (c <= '9')) 57
47 { 58 throw new IllegalArgumentException("Need hex char");
48 return (c - '0'); 59 }
49 } 60
50 61 private static byte[] hexToByteArray(String hex) {
51 throw new IllegalArgumentException("Need hex char"); 62 if (hex == null)
52 } 63 throw new IllegalArgumentException("null argument");
53 64
54 private static byte[] hexToByteArray(String hex) 65 if ((hex.length() % 2) != 0)
55 { 66 throw new IllegalArgumentException("Uneven string length in hex encoding.");
56 if (hex == null) 67
57 throw new IllegalArgumentException("null argument"); 68 byte decoded[] = new byte[hex.length() / 2];
58 69
59 if ((hex.length() % 2) != 0) 70 for (int i = 0; i < decoded.length; i++) {
60 throw new IllegalArgumentException("Uneven string length in hex encoding."); 71 int hi = hexToInt(hex.charAt(i * 2));
61 72 int lo = hexToInt(hex.charAt((i * 2) + 1));
62 byte decoded[] = new byte[hex.length() / 2]; 73 decoded[i] = (byte)(hi * 16 + lo);
63 74 }
64 for (int i = 0; i < decoded.length; i++) 75
65 { 76 return decoded;
66 int hi = hexToInt(hex.charAt(i * 2)); 77 }
67 int lo = hexToInt(hex.charAt((i * 2) + 1)); 78
68 79 private static byte[] generateKeyFromPasswordSaltWithMD5(byte[] password, byte[] salt, int keyLen)
69 decoded[i] = (byte) (hi * 16 + lo); 80 throws IOException {
70 } 81 if (salt.length < 8)
71 82 throw new IllegalArgumentException("Salt needs to be at least 8 bytes for key generation.");
72 return decoded; 83
73 } 84 MessageDigest md5;
74 85
75 private static byte[] generateKeyFromPasswordSaltWithMD5(byte[] password, byte[] salt, int keyLen) 86 try {
76 throws IOException 87 md5 = MessageDigest.getInstance("MD5");
77 { 88 }
78 if (salt.length < 8) 89 catch (NoSuchAlgorithmException e) {
79 throw new IllegalArgumentException("Salt needs to be at least 8 bytes for key generation."); 90 throw new IllegalArgumentException("VM does not support MD5", e);
80 91 }
81 MD5 md5 = new MD5(); 92
82 93 byte[] key = new byte[keyLen];
83 byte[] key = new byte[keyLen]; 94 byte[] tmp = new byte[md5.getDigestLength()];
84 byte[] tmp = new byte[md5.getDigestLength()]; 95
85 96 while (true) {
86 while (true) 97 md5.update(password, 0, password.length);
87 { 98 md5.update(salt, 0, 8); // ARGH we only use the first 8 bytes of the
88 md5.update(password, 0, password.length); 99 // salt in this step.
89 md5.update(salt, 0, 8); // ARGH we only use the first 8 bytes of the salt in this step. 100 // This took me two hours until I got AES-xxx running.
90 // This took me two hours until I got AES-xxx running. 101 int copy = (keyLen < tmp.length) ? keyLen : tmp.length;
91
92 int copy = (keyLen < tmp.length) ? keyLen : tmp.length;
93 102
94 try { 103 try {
95 md5.digest(tmp, 0); 104 md5.digest(tmp, 0, tmp.length);
96 } 105 }
97 catch(DigestException e) { 106 catch (DigestException e) {
98 throw new IOException(e); 107 IOException ex = new IOException("could not digest password");
108 ex.initCause(e);
109 throw ex;
99 } 110 }
100 111
101 System.arraycopy(tmp, 0, key, key.length - keyLen, copy); 112 System.arraycopy(tmp, 0, key, key.length - keyLen, copy);
102 113 keyLen -= copy;
103 keyLen -= copy; 114
104 115 if (keyLen == 0)
105 if (keyLen == 0) 116 return key;
106 return key; 117
107 118 md5.update(tmp, 0, tmp.length);
108 md5.update(tmp, 0, tmp.length); 119 }
109 } 120 }
110 } 121
111 122 private static byte[] removePadding(byte[] buff, int blockSize) throws IOException {
112 private static byte[] removePadding(byte[] buff, int blockSize) throws IOException 123 /* Removes RFC 1423/PKCS #7 padding */
113 { 124 int rfc_1423_padding = buff[buff.length - 1] & 0xff;
114 /* Removes RFC 1423/PKCS #7 padding */ 125
115 126 if ((rfc_1423_padding < 1) || (rfc_1423_padding > blockSize))
116 int rfc_1423_padding = buff[buff.length - 1] & 0xff; 127 throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?");
117 128
118 if ((rfc_1423_padding < 1) || (rfc_1423_padding > blockSize)) 129 for (int i = 2; i <= rfc_1423_padding; i++) {
119 throw new PEMDecryptException("Decrypted PEM has wrong padding, did you specify the correct password?"); 130 if (buff[buff.length - i] != rfc_1423_padding)
120 131 throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?");
121 for (int i = 2; i <= rfc_1423_padding; i++) 132 }
122 { 133
123 if (buff[buff.length - i] != rfc_1423_padding) 134 byte[] tmp = new byte[buff.length - rfc_1423_padding];
124 throw new PEMDecryptException("Decrypted PEM has wrong padding, did you specify the correct password?"); 135 System.arraycopy(buff, 0, tmp, 0, buff.length - rfc_1423_padding);
125 } 136 return tmp;
126 137 }
127 byte[] tmp = new byte[buff.length - rfc_1423_padding]; 138
128 System.arraycopy(buff, 0, tmp, 0, buff.length - rfc_1423_padding); 139 public static final PEMStructure parsePEM(char[] pem) throws IOException {
129 return tmp; 140 PEMStructure ps = new PEMStructure();
130 } 141 String line = null;
131 142 BufferedReader br = new BufferedReader(new CharArrayReader(pem));
132 private static PEMStructure parsePEM(char[] pem) throws IOException 143 String endLine = null;
133 { 144
134 PEMStructure ps = new PEMStructure(); 145 while (true) {
135 146 line = br.readLine();
136 String line = null; 147
137 148 if (line == null)
138 BufferedReader br = new BufferedReader(new CharArrayReader(pem)); 149 throw new IOException("Invalid PEM structure, '-----BEGIN...' missing");
139 150
140 String endLine = null; 151 line = line.trim();
141 152
142 while (true) 153 if (line.startsWith("-----BEGIN DSA PRIVATE KEY-----")) {
143 { 154 endLine = "-----END DSA PRIVATE KEY-----";
144 line = br.readLine(); 155 ps.pemType = PEM_DSA_PRIVATE_KEY;
145 156 break;
146 if (line == null) 157 }
147 throw new IOException("Invalid PEM structure, '-----BEGIN...' missing"); 158
148 159 if (line.startsWith("-----BEGIN RSA PRIVATE KEY-----")) {
149 line = line.trim(); 160 endLine = "-----END RSA PRIVATE KEY-----";
150 161 ps.pemType = PEM_RSA_PRIVATE_KEY;
151 if (line.startsWith("-----BEGIN DSA PRIVATE KEY-----")) 162 break;
152 { 163 }
153 endLine = "-----END DSA PRIVATE KEY-----"; 164
154 ps.pemType = PEM_DSA_PRIVATE_KEY; 165 if (line.startsWith("-----BEGIN EC PRIVATE KEY-----")) {
155 break; 166 endLine = "-----END EC PRIVATE KEY-----";
156 } 167 ps.pemType = PEM_EC_PRIVATE_KEY;
157 168 break;
158 if (line.startsWith("-----BEGIN RSA PRIVATE KEY-----")) 169 }
159 { 170 }
160 endLine = "-----END RSA PRIVATE KEY-----"; 171
161 ps.pemType = PEM_RSA_PRIVATE_KEY; 172 while (true) {
162 break; 173 line = br.readLine();
163 } 174
164 } 175 if (line == null)
165 176 throw new IOException("Invalid PEM structure, " + endLine + " missing");
166 while (true) 177
167 { 178 line = line.trim();
168 line = br.readLine(); 179 int sem_idx = line.indexOf(':');
169 180
170 if (line == null) 181 if (sem_idx == -1)
171 throw new IOException("Invalid PEM structure, " + endLine + " missing"); 182 break;
172 183
173 line = line.trim(); 184 String name = line.substring(0, sem_idx + 1);
174 185 String value = line.substring(sem_idx + 1);
175 int sem_idx = line.indexOf(':'); 186 String values[] = value.split(",");
176 187
177 if (sem_idx == -1) 188 for (int i = 0; i < values.length; i++)
178 break; 189 values[i] = values[i].trim();
179 190
180 String name = line.substring(0, sem_idx + 1); 191 // Proc-Type: 4,ENCRYPTED
181 String value = line.substring(sem_idx + 1); 192 // DEK-Info: DES-EDE3-CBC,579B6BE3E5C60483
182 193
183 String values[] = value.split(","); 194 if ("Proc-Type:".equals(name)) {
184 195 ps.procType = values;
185 for (int i = 0; i < values.length; i++) 196 continue;
186 values[i] = values[i].trim(); 197 }
187 198
188 // Proc-Type: 4,ENCRYPTED 199 if ("DEK-Info:".equals(name)) {
189 // DEK-Info: DES-EDE3-CBC,579B6BE3E5C60483 200 ps.dekInfo = values;
190 201 continue;
191 if ("Proc-Type:".equals(name)) 202 }
192 { 203
193 ps.procType = values; 204 /* Ignore line */
194 continue; 205 }
195 } 206
196 207 StringBuffer keyData = new StringBuffer();
197 if ("DEK-Info:".equals(name)) 208
198 { 209 while (true) {
199 ps.dekInfo = values; 210 if (line == null)
200 continue; 211 throw new IOException("Invalid PEM structure, " + endLine + " missing");
201 } 212
202 /* Ignore line */ 213 line = line.trim();
203 } 214
204 215 if (line.startsWith(endLine))
205 StringBuilder keyData = new StringBuilder(); 216 break;
206 217
207 while (true) 218 keyData.append(line);
208 { 219 line = br.readLine();
209 if (line == null) 220 }
210 throw new IOException("Invalid PEM structure, " + endLine + " missing"); 221
211 222 char[] pem_chars = new char[keyData.length()];
212 line = line.trim(); 223 keyData.getChars(0, pem_chars.length, pem_chars, 0);
213 224 ps.data = Base64.decode(pem_chars);
214 if (line.startsWith(endLine)) 225
215 break; 226 if (ps.data.length == 0)
216 227 throw new IOException("Invalid PEM structure, no data available");
217 keyData.append(line); 228
218 229 return ps;
219 line = br.readLine(); 230 }
220 } 231
221 232 private static final void decryptPEM(PEMStructure ps, byte[] pw) throws IOException {
222 char[] pem_chars = new char[keyData.length()]; 233 if (ps.dekInfo == null)
223 keyData.getChars(0, pem_chars.length, pem_chars, 0); 234 throw new IOException("Broken PEM, no mode and salt given, but encryption enabled");
224 235
225 ps.data = Base64.decode(pem_chars); 236 if (ps.dekInfo.length != 2)
226 237 throw new IOException("Broken PEM, DEK-Info is incomplete!");
227 if (ps.data.length == 0) 238
228 throw new IOException("Invalid PEM structure, no data available"); 239 String algo = ps.dekInfo[0];
229 240 byte[] salt = hexToByteArray(ps.dekInfo[1]);
230 return ps; 241 BlockCipher bc = null;
231 } 242
232 243 if (algo.equals("DES-EDE3-CBC")) {
233 private static void decryptPEM(PEMStructure ps, byte[] pw) throws IOException 244 DESede des3 = new DESede();
234 { 245 des3.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
235 if (ps.dekInfo == null) 246 bc = new CBCMode(des3, salt, false);
236 throw new IOException("Broken PEM, no mode and salt given, but encryption enabled"); 247 }
237 248 else if (algo.equals("DES-CBC")) {
238 if (ps.dekInfo.length != 2) 249 DES des = new DES();
239 throw new IOException("Broken PEM, DEK-Info is incomplete!"); 250 des.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 8));
240 251 bc = new CBCMode(des, salt, false);
241 String algo = ps.dekInfo[0]; 252 }
242 byte[] salt = hexToByteArray(ps.dekInfo[1]); 253 else if (algo.equals("AES-128-CBC")) {
243 254 AES aes = new AES();
244 BlockCipher bc = null; 255 aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 16));
245 256 bc = new CBCMode(aes, salt, false);
246 if (algo.equals("DES-EDE3-CBC")) 257 }
247 { 258 else if (algo.equals("AES-192-CBC")) {
248 DESede des3 = new DESede(); 259 AES aes = new AES();
249 des3.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24)); 260 aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
250 bc = new CBCMode(des3, salt, false); 261 bc = new CBCMode(aes, salt, false);
251 } 262 }
252 else if (algo.equals("DES-CBC")) 263 else if (algo.equals("AES-256-CBC")) {
253 { 264 AES aes = new AES();
254 DES des = new DES(); 265 aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 32));
255 des.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 8)); 266 bc = new CBCMode(aes, salt, false);
256 bc = new CBCMode(des, salt, false); 267 }
257 } 268 else {
258 else if (algo.equals("AES-128-CBC")) 269 throw new IOException("Cannot decrypt PEM structure, unknown cipher " + algo);
259 { 270 }
260 AES aes = new AES(); 271
261 aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 16)); 272 if ((ps.data.length % bc.getBlockSize()) != 0)
262 bc = new CBCMode(aes, salt, false); 273 throw new IOException("Invalid PEM structure, size of encrypted block is not a multiple of "
263 } 274 + bc.getBlockSize());
264 else if (algo.equals("AES-192-CBC")) 275
265 { 276 /* Now decrypt the content */
266 AES aes = new AES(); 277 byte[] dz = new byte[ps.data.length];
267 aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24)); 278
268 bc = new CBCMode(aes, salt, false); 279 for (int i = 0; i < ps.data.length / bc.getBlockSize(); i++) {
269 } 280 bc.transformBlock(ps.data, i * bc.getBlockSize(), dz, i * bc.getBlockSize());
270 else if (algo.equals("AES-256-CBC")) 281 }
271 { 282
272 AES aes = new AES(); 283 /* Now check and remove RFC 1423/PKCS #7 padding */
273 aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 32)); 284 dz = removePadding(dz, bc.getBlockSize());
274 bc = new CBCMode(aes, salt, false); 285 ps.data = dz;
275 } 286 ps.dekInfo = null;
276 else 287 ps.procType = null;
277 { 288 }
278 throw new IOException("Cannot decrypt PEM structure, unknown cipher " + algo); 289
279 } 290 public static final boolean isPEMEncrypted(PEMStructure ps) throws IOException {
280 291 if (ps.procType == null)
281 if ((ps.data.length % bc.getBlockSize()) != 0) 292 return false;
282 throw new IOException("Invalid PEM structure, size of encrypted block is not a multiple of " 293
283 + bc.getBlockSize()); 294 if (ps.procType.length != 2)
284 295 throw new IOException("Unknown Proc-Type field.");
285 /* Now decrypt the content */ 296
286 297 if ("4".equals(ps.procType[0]) == false)
287 byte[] dz = new byte[ps.data.length]; 298 throw new IOException("Unknown Proc-Type field (" + ps.procType[0] + ")");
288 299
289 for (int i = 0; i < ps.data.length / bc.getBlockSize(); i++) 300 if ("ENCRYPTED".equals(ps.procType[1]))
290 { 301 return true;
291 bc.transformBlock(ps.data, i * bc.getBlockSize(), dz, i * bc.getBlockSize()); 302
292 } 303 return false;
293 304 }
294 /* Now check and remove RFC 1423/PKCS #7 padding */ 305
295 306 public static KeyPair decode(char[] pem, String password) throws IOException {
296 dz = removePadding(dz, bc.getBlockSize()); 307 PEMStructure ps = parsePEM(pem);
297 308 return decode(ps, password);
298 ps.data = dz; 309 }
299 ps.dekInfo = null; 310
300 ps.procType = null; 311 public static KeyPair decode(PEMStructure ps, String password) throws IOException {
301 } 312 if (isPEMEncrypted(ps)) {
302 313 if (password == null)
303 public static final boolean isPEMEncrypted(PEMStructure ps) throws IOException 314 throw new IOException("PEM is encrypted, but no password was specified");
304 { 315
305 if (ps.procType == null) 316 decryptPEM(ps, password.getBytes("ISO-8859-1"));
306 return false; 317 }
307 318
308 if (ps.procType.length != 2) 319 if (ps.pemType == PEM_DSA_PRIVATE_KEY) {
309 throw new IOException("Unknown Proc-Type field."); 320 SimpleDERReader dr = new SimpleDERReader(ps.data);
310 321 byte[] seq = dr.readSequenceAsByteArray();
311 if ("4".equals(ps.procType[0]) == false) 322
312 throw new IOException("Unknown Proc-Type field (" + ps.procType[0] + ")"); 323 if (dr.available() != 0)
313 324 throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
314 if ("ENCRYPTED".equals(ps.procType[1])) 325
315 return true; 326 dr.resetInput(seq);
316 327 BigInteger version = dr.readInt();
317 return false; 328
318 } 329 if (version.compareTo(BigInteger.ZERO) != 0)
319 330 throw new IOException("Wrong version (" + version + ") in DSA PRIVATE KEY DER stream.");
320 public static final boolean isPEMEncrypted(char[] pem) throws IOException 331
321 { 332 BigInteger p = dr.readInt();
322 return isPEMEncrypted(parsePEM(pem)); 333 BigInteger q = dr.readInt();
323 } 334 BigInteger g = dr.readInt();
324 335 BigInteger y = dr.readInt();
325 public static Object decode(char[] pem, String password) throws IOException 336 BigInteger x = dr.readInt();
326 { 337
327 PEMStructure ps = parsePEM(pem); 338 if (dr.available() != 0)
328 339 throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
329 if (isPEMEncrypted(ps)) 340
330 { 341 DSAPrivateKeySpec privSpec = new DSAPrivateKeySpec(x, p, q, g);
331 if (password == null) 342 DSAPublicKeySpec pubSpec = new DSAPublicKeySpec(y, p, q, g);
332 throw new IOException("PEM is encrypted, but no password was specified"); 343 return generateKeyPair("DSA", privSpec, pubSpec);
333 344 }
334 decryptPEM(ps, StringEncoder.GetBytes(password)); 345
335 } 346 if (ps.pemType == PEM_RSA_PRIVATE_KEY) {
336 347 SimpleDERReader dr = new SimpleDERReader(ps.data);
337 if (ps.pemType == PEM_DSA_PRIVATE_KEY) 348 byte[] seq = dr.readSequenceAsByteArray();
338 { 349
339 SimpleDERReader dr = new SimpleDERReader(ps.data); 350 if (dr.available() != 0)
340 351 throw new IOException("Padding in RSA PRIVATE KEY DER stream.");
341 byte[] seq = dr.readSequenceAsByteArray(); 352
342 353 dr.resetInput(seq);
343 if (dr.available() != 0) 354 BigInteger version = dr.readInt();
344 throw new IOException("Padding in DSA PRIVATE KEY DER stream."); 355
345 356 if ((version.compareTo(BigInteger.ZERO) != 0) && (version.compareTo(BigInteger.ONE) != 0))
346 dr.resetInput(seq); 357 throw new IOException("Wrong version (" + version + ") in RSA PRIVATE KEY DER stream.");
347 358
348 BigInteger version = dr.readInt(); 359 BigInteger n = dr.readInt();
349 360 BigInteger e = dr.readInt();
350 if (version.compareTo(BigInteger.ZERO) != 0) 361 BigInteger d = dr.readInt();
351 throw new IOException("Wrong version (" + version + ") in DSA PRIVATE KEY DER stream."); 362 // TODO: is this right?
352 363 BigInteger primeP = dr.readInt();
353 BigInteger p = dr.readInt(); 364 BigInteger primeQ = dr.readInt();
354 BigInteger q = dr.readInt(); 365 BigInteger expP = dr.readInt();
355 BigInteger g = dr.readInt(); 366 BigInteger expQ = dr.readInt();
356 BigInteger y = dr.readInt(); 367 BigInteger coeff = dr.readInt();
357 BigInteger x = dr.readInt(); 368 RSAPrivateKeySpec privSpec = new RSAPrivateCrtKeySpec(n, e, d, primeP, primeQ, expP, expQ, coeff);
358 369 RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(n, e);
359 if (dr.available() != 0) 370 return generateKeyPair("RSA", privSpec, pubSpec);
360 throw new IOException("Padding in DSA PRIVATE KEY DER stream."); 371 }
361 372
362 return new DSAPrivateKey(p, q, g, y, x); 373 if (ps.pemType == PEM_EC_PRIVATE_KEY) {
363 } 374 SimpleDERReader dr = new SimpleDERReader(ps.data);
364 375 byte[] seq = dr.readSequenceAsByteArray();
365 if (ps.pemType == PEM_RSA_PRIVATE_KEY) 376
366 { 377 if (dr.available() != 0)
367 SimpleDERReader dr = new SimpleDERReader(ps.data); 378 throw new IOException("Padding in EC PRIVATE KEY DER stream.");
368 379
369 byte[] seq = dr.readSequenceAsByteArray(); 380 dr.resetInput(seq);
370 381 BigInteger version = dr.readInt();
371 if (dr.available() != 0) 382
372 throw new IOException("Padding in RSA PRIVATE KEY DER stream."); 383 if ((version.compareTo(BigInteger.ONE) != 0))
373 384 throw new IOException("Wrong version (" + version + ") in EC PRIVATE KEY DER stream.");
374 dr.resetInput(seq); 385
375 386 byte[] privateBytes = dr.readOctetString();
376 BigInteger version = dr.readInt(); 387 String curveOid = null;
377 388 byte[] publicBytes = null;
378 if ((version.compareTo(BigInteger.ZERO) != 0) && (version.compareTo(BigInteger.ONE) != 0)) 389
379 throw new IOException("Wrong version (" + version + ") in RSA PRIVATE KEY DER stream."); 390 while (dr.available() > 0) {
380 391 int type = dr.readConstructedType();
381 BigInteger n = dr.readInt(); 392 SimpleDERReader cr = dr.readConstructed();
382 BigInteger e = dr.readInt(); 393
383 BigInteger d = dr.readInt(); 394 switch (type) {
384 395 case 0:
385 return new RSAPrivateKey(d, e, n); 396 curveOid = cr.readOid();
386 } 397 break;
387 398
388 throw new IOException("PEM problem: it is of unknown type"); 399 case 1:
389 } 400 publicBytes = cr.readOctetString();
390 401 break;
402 }
403 }
404
405 ECParameterSpec params = ECDSASHA2Verify.getCurveForOID(curveOid);
406
407 if (params == null)
408 throw new IOException("invalid OID");
409
410 BigInteger s = new BigInteger(privateBytes);
411 byte[] publicBytesSlice = new byte[publicBytes.length - 1];
412 System.arraycopy(publicBytes, 1, publicBytesSlice, 0, publicBytesSlice.length);
413 ECPoint w = ECDSASHA2Verify.decodeECPoint(publicBytesSlice, params.getCurve());
414 ECPrivateKeySpec privSpec = new ECPrivateKeySpec(s, params);
415 ECPublicKeySpec pubSpec = new ECPublicKeySpec(w, params);
416 return generateKeyPair("EC", privSpec, pubSpec);
417 }
418
419 throw new IOException("PEM problem: it is of unknown type");
420 }
421
422 /**
423 * Generate a {@code KeyPair} given an {@code algorithm} and {@code KeySpec}.
424 */
425 private static KeyPair generateKeyPair(String algorithm, KeySpec privSpec, KeySpec pubSpec)
426 throws IOException {
427 try {
428 final KeyFactory kf = KeyFactory.getInstance(algorithm);
429 final PublicKey pubKey = kf.generatePublic(pubSpec);
430 final PrivateKey privKey = kf.generatePrivate(privSpec);
431 return new KeyPair(pubKey, privKey);
432 }
433 catch (NoSuchAlgorithmException ex) {
434 IOException ioex = new IOException();
435 ioex.initCause(ex);
436 throw ioex;
437 }
438 catch (InvalidKeySpecException ex) {
439 IOException ioex = new IOException("invalid keyspec");
440 ioex.initCause(ex);
441 throw ioex;
442 }
443 }
391 } 444 }