001 /*
002 * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.11/src/java/org/apache/commons/ssl/OpenSSL.java $
003 * $Revision: 144 $
004 * $Date: 2009-05-25 11:14:29 -0700 (Mon, 25 May 2009) $
005 *
006 * ====================================================================
007 * Licensed to the Apache Software Foundation (ASF) under one
008 * or more contributor license agreements. See the NOTICE file
009 * distributed with this work for additional information
010 * regarding copyright ownership. The ASF licenses this file
011 * to you under the Apache License, Version 2.0 (the
012 * "License"); you may not use this file except in compliance
013 * with the License. You may obtain a copy of the License at
014 *
015 * http://www.apache.org/licenses/LICENSE-2.0
016 *
017 * Unless required by applicable law or agreed to in writing,
018 * software distributed under the License is distributed on an
019 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
020 * KIND, either express or implied. See the License for the
021 * specific language governing permissions and limitations
022 * under the License.
023 * ====================================================================
024 *
025 * This software consists of voluntary contributions made by many
026 * individuals on behalf of the Apache Software Foundation. For more
027 * information on the Apache Software Foundation, please see
028 * <http://www.apache.org/>.
029 *
030 */
031
032 package org.apache.commons.ssl;
033
034 import org.apache.commons.ssl.util.Hex;
035
036 import javax.crypto.Cipher;
037 import javax.crypto.CipherInputStream;
038 import java.io.*;
039 import java.security.GeneralSecurityException;
040 import java.security.MessageDigest;
041 import java.security.NoSuchAlgorithmException;
042 import java.security.SecureRandom;
043 import java.util.StringTokenizer;
044
045 /**
046 * Class for encrypting or decrypting data with a password (PBE - password
047 * based encryption). Compatible with "openssl enc" unix utility. An OpenSSL
048 * compatible cipher name must be specified along with the password (try "man enc" on a
049 * unix box to see what's possible). Some examples:
050 * <ul><li>des, des3, des-ede3-cbc
051 * <li>aes128, aes192, aes256, aes-256-cbc
052 * <li>rc2, rc4, bf</ul>
053 * <pre>
054 * <em style="color: green;">// Encrypt!</em>
055 * byte[] encryptedData = OpenSSL.encrypt( "des3", password, data );
056 * </pre>
057 * <p/>
058 * If you want to specify a raw key and iv directly (without using PBE), use
059 * the methods that take byte[] key, byte[] iv. Those byte[] arrays can be
060 * the raw binary, or they can be ascii (hex representation: '0' - 'F'). If
061 * you want to use PBE to derive the key and iv, then use the methods that
062 * take char[] password.
063 * <p/>
064 * This class is able to decrypt files encrypted with "openssl" unix utility.
065 * <p/>
066 * The "openssl" unix utility is able to decrypt files encrypted by this class.
067 * <p/>
068 * This class is also able to encrypt and decrypt its own files.
069 *
070 * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@gmail.com</a>
071 * @since 18-Oct-2007
072 */
073 public class OpenSSL {
074
075
076 /**
077 * Decrypts data using a password and an OpenSSL compatible cipher
078 * name.
079 *
080 * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a
081 * unix box to see what's possible). Some examples:
082 * <ul><li>des, des3, des-ede3-cbc
083 * <li>aes128, aes192, aes256, aes-256-cbc
084 * <li>rc2, rc4, bf</ul>
085 * @param pwd password to use for this PBE decryption
086 * @param encrypted byte array to decrypt. Can be raw, or base64.
087 * @return decrypted bytes
088 * @throws IOException problems with encrypted bytes (unlikely!)
089 * @throws GeneralSecurityException problems decrypting
090 */
091 public static byte[] decrypt(String cipher, char[] pwd, byte[] encrypted)
092 throws IOException, GeneralSecurityException {
093 ByteArrayInputStream in = new ByteArrayInputStream(encrypted);
094 InputStream decrypted = decrypt(cipher, pwd, in);
095 return Util.streamToBytes(decrypted);
096 }
097
098 /**
099 * Decrypts data using a password and an OpenSSL compatible cipher
100 * name.
101 *
102 * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a
103 * unix box to see what's possible). Some examples:
104 * <ul><li>des, des3, des-ede3-cbc
105 * <li>aes128, aes192, aes256, aes-256-cbc
106 * <li>rc2, rc4, bf</ul>
107 * @param pwd password to use for this PBE decryption
108 * @param encrypted InputStream to decrypt. Can be raw, or base64.
109 * @return decrypted bytes as an InputStream
110 * @throws IOException problems with InputStream
111 * @throws GeneralSecurityException problems decrypting
112 */
113 public static InputStream decrypt(String cipher, char[] pwd,
114 InputStream encrypted)
115 throws IOException, GeneralSecurityException {
116 CipherInfo cipherInfo = lookup(cipher);
117 boolean salted = false;
118
119 // First 16 bytes of raw binary will hopefully be OpenSSL's
120 // "Salted__[8 bytes of hex]" thing. Might be in Base64, though.
121 byte[] saltLine = Util.streamToBytes(encrypted, 16);
122 if (saltLine.length <= 0) {
123 throw new IOException("encrypted InputStream is empty");
124 }
125 String firstEightBytes = "";
126 if (saltLine.length >= 8) {
127 firstEightBytes = new String(saltLine, 0, 8);
128 }
129 if ("SALTED__".equalsIgnoreCase(firstEightBytes)) {
130 salted = true;
131 } else {
132 // Maybe the reason we didn't find the salt is because we're in
133 // base64.
134 if (Base64.isArrayByteBase64(saltLine)) {
135 InputStream head = new ByteArrayInputStream(saltLine);
136 // Need to put that 16 byte "saltLine" back into the Stream.
137 encrypted = new ComboInputStream(head, encrypted);
138 encrypted = new Base64InputStream(encrypted);
139 saltLine = Util.streamToBytes(encrypted, 16);
140
141 if (saltLine.length >= 8) {
142 firstEightBytes = new String(saltLine, 0, 8);
143 }
144 if ("SALTED__".equalsIgnoreCase(firstEightBytes)) {
145 salted = true;
146 }
147 }
148 }
149
150 byte[] salt = null;
151 if (salted) {
152 salt = new byte[8];
153 System.arraycopy(saltLine, 8, salt, 0, 8);
154 } else {
155 // Encrypted data wasn't salted. Need to put the "saltLine" we
156 // extracted back into the stream.
157 InputStream head = new ByteArrayInputStream(saltLine);
158 encrypted = new ComboInputStream(head, encrypted);
159 }
160
161 int keySize = cipherInfo.keySize;
162 int ivSize = cipherInfo.ivSize;
163 boolean des2 = cipherInfo.des2;
164 DerivedKey dk = deriveKey(pwd, salt, keySize, ivSize, des2);
165 Cipher c = PKCS8Key.generateCipher(
166 cipherInfo.javaCipher, cipherInfo.blockMode, dk, des2, null, true
167 );
168
169 return new CipherInputStream(encrypted, c);
170 }
171
172 /**
173 * Encrypts data using a password and an OpenSSL compatible cipher
174 * name.
175 *
176 * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a
177 * unix box to see what's possible). Some examples:
178 * <ul><li>des, des3, des-ede3-cbc
179 * <li>aes128, aes192, aes256, aes-256-cbc
180 * <li>rc2, rc4, bf</ul>
181 * @param pwd password to use for this PBE encryption
182 * @param data byte array to encrypt
183 * @return encrypted bytes as an array in base64. First 16 bytes include the
184 * special OpenSSL "Salted__" info encoded into base64.
185 * @throws IOException problems with the data byte array
186 * @throws GeneralSecurityException problems encrypting
187 */
188 public static byte[] encrypt(String cipher, char[] pwd, byte[] data)
189 throws IOException, GeneralSecurityException {
190 // base64 is the default output format.
191 return encrypt(cipher, pwd, data, true);
192 }
193
194 /**
195 * Encrypts data using a password and an OpenSSL compatible cipher
196 * name.
197 *
198 * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a
199 * unix box to see what's possible). Some examples:
200 * <ul><li>des, des3, des-ede3-cbc
201 * <li>aes128, aes192, aes256, aes-256-cbc
202 * <li>rc2, rc4, bf</ul>
203 * @param pwd password to use for this PBE encryption
204 * @param data InputStream to encrypt
205 * @return encrypted bytes as an InputStream. First 16 bytes include the
206 * special OpenSSL "Salted__" info encoded into base64.
207 * @throws IOException problems with the data InputStream
208 * @throws GeneralSecurityException problems encrypting
209 */
210 public static InputStream encrypt(String cipher, char[] pwd,
211 InputStream data)
212 throws IOException, GeneralSecurityException {
213 // base64 is the default output format.
214 return encrypt(cipher, pwd, data, true);
215 }
216
217 /**
218 * Encrypts data using a password and an OpenSSL compatible cipher
219 * name.
220 *
221 * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a
222 * unix box to see what's possible). Some examples:
223 * <ul><li>des, des3, des-ede3-cbc
224 * <li>aes128, aes192, aes256, aes-256-cbc
225 * <li>rc2, rc4, bf</ul>
226 * @param pwd password to use for this PBE encryption
227 * @param data byte array to encrypt
228 * @param toBase64 true if resulting InputStream should contain base64,
229 * <br>false if InputStream should contain raw binary.
230 * @return encrypted bytes as an array. First 16 bytes include the
231 * special OpenSSL "Salted__" info.
232 * @throws IOException problems with the data byte array
233 * @throws GeneralSecurityException problems encrypting
234 */
235 public static byte[] encrypt(String cipher, char[] pwd, byte[] data,
236 boolean toBase64)
237 throws IOException, GeneralSecurityException {
238 // we use a salt by default.
239 return encrypt(cipher, pwd, data, toBase64, true);
240 }
241
242 /**
243 * Encrypts data using a password and an OpenSSL compatible cipher
244 * name.
245 *
246 * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a
247 * unix box to see what's possible). Some examples:
248 * <ul><li>des, des3, des-ede3-cbc
249 * <li>aes128, aes192, aes256, aes-256-cbc
250 * <li>rc2, rc4, bf</ul>
251 * @param pwd password to use for this PBE encryption
252 * @param data InputStream to encrypt
253 * @param toBase64 true if resulting InputStream should contain base64,
254 * <br>false if InputStream should contain raw binary.
255 * @return encrypted bytes as an InputStream. First 16 bytes include the
256 * special OpenSSL "Salted__" info.
257 * @throws IOException problems with the data InputStream
258 * @throws GeneralSecurityException problems encrypting
259 */
260 public static InputStream encrypt(String cipher, char[] pwd,
261 InputStream data, boolean toBase64)
262 throws IOException, GeneralSecurityException {
263 // we use a salt by default.
264 return encrypt(cipher, pwd, data, toBase64, true);
265 }
266
267 /**
268 * Encrypts data using a password and an OpenSSL compatible cipher
269 * name.
270 *
271 * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a
272 * unix box to see what's possible). Some examples:
273 * <ul><li>des, des3, des-ede3-cbc
274 * <li>aes128, aes192, aes256, aes-256-cbc
275 * <li>rc2, rc4, bf</ul>
276 * @param pwd password to use for this PBE encryption
277 * @param data byte array to encrypt
278 * @param toBase64 true if resulting InputStream should contain base64,
279 * <br>false if InputStream should contain raw binary.
280 * @param useSalt true if a salt should be used to derive the key.
281 * <br>false otherwise. (Best security practises
282 * always recommend using a salt!).
283 * @return encrypted bytes as an array. First 16 bytes include the
284 * special OpenSSL "Salted__" info if <code>useSalt</code> is true.
285 * @throws IOException problems with the data InputStream
286 * @throws GeneralSecurityException problems encrypting
287 */
288 public static byte[] encrypt(String cipher, char[] pwd, byte[] data,
289 boolean toBase64, boolean useSalt)
290 throws IOException, GeneralSecurityException {
291 ByteArrayInputStream in = new ByteArrayInputStream(data);
292 InputStream encrypted = encrypt(cipher, pwd, in, toBase64, useSalt);
293 return Util.streamToBytes(encrypted);
294 }
295
296 /**
297 * Encrypts data using a password and an OpenSSL compatible cipher
298 * name.
299 *
300 * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a
301 * unix box to see what's possible). Some examples:
302 * <ul><li>des, des3, des-ede3-cbc
303 * <li>aes128, aes192, aes256, aes-256-cbc
304 * <li>rc2, rc4, bf</ul>
305 * @param pwd password to use for this PBE encryption
306 * @param data InputStream to encrypt
307 * @param toBase64 true if resulting InputStream should contain base64,
308 * <br>false if InputStream should contain raw binary.
309 * @param useSalt true if a salt should be used to derive the key.
310 * <br>false otherwise. (Best security practises
311 * always recommend using a salt!).
312 * @return encrypted bytes as an InputStream. First 16 bytes include the
313 * special OpenSSL "Salted__" info if <code>useSalt</code> is true.
314 * @throws IOException problems with the data InputStream
315 * @throws GeneralSecurityException problems encrypting
316 */
317 public static InputStream encrypt(String cipher, char[] pwd,
318 InputStream data, boolean toBase64,
319 boolean useSalt)
320 throws IOException, GeneralSecurityException {
321 CipherInfo cipherInfo = lookup(cipher);
322 byte[] salt = null;
323 if (useSalt) {
324 SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
325 salt = new byte[8];
326 rand.nextBytes(salt);
327 }
328
329 int keySize = cipherInfo.keySize;
330 int ivSize = cipherInfo.ivSize;
331 boolean des2 = cipherInfo.des2;
332 DerivedKey dk = deriveKey(pwd, salt, keySize, ivSize, des2);
333 Cipher c = PKCS8Key.generateCipher(
334 cipherInfo.javaCipher, cipherInfo.blockMode, dk, des2, null, false
335 );
336
337 InputStream cipherStream = new CipherInputStream(data, c);
338
339 if (useSalt) {
340 byte[] saltLine = new byte[16];
341 byte[] salted = "Salted__".getBytes();
342 System.arraycopy(salted, 0, saltLine, 0, salted.length);
343 System.arraycopy(salt, 0, saltLine, salted.length, salt.length);
344 InputStream head = new ByteArrayInputStream(saltLine);
345 cipherStream = new ComboInputStream(head, cipherStream);
346 }
347 if (toBase64) {
348 cipherStream = new Base64InputStream(cipherStream, true);
349 }
350 return cipherStream;
351 }
352
353
354 public static byte[] decrypt(String cipher, byte[] key, byte[] iv,
355 byte[] encrypted)
356 throws IOException, GeneralSecurityException {
357 ByteArrayInputStream in = new ByteArrayInputStream(encrypted);
358 InputStream decrypted = decrypt(cipher, key, iv, in);
359 return Util.streamToBytes(decrypted);
360 }
361
362 public static InputStream decrypt(String cipher, byte[] key, byte[] iv,
363 InputStream encrypted)
364 throws IOException, GeneralSecurityException {
365 CipherInfo cipherInfo = lookup(cipher);
366 byte[] firstLine = Util.streamToBytes(encrypted, 16);
367 if (Base64.isArrayByteBase64(firstLine)) {
368 InputStream head = new ByteArrayInputStream(firstLine);
369 // Need to put that 16 byte "firstLine" back into the Stream.
370 encrypted = new ComboInputStream(head, encrypted);
371 encrypted = new Base64InputStream(encrypted);
372 } else {
373 // Encrypted data wasn't base64. Need to put the "firstLine" we
374 // extracted back into the stream.
375 InputStream head = new ByteArrayInputStream(firstLine);
376 encrypted = new ComboInputStream(head, encrypted);
377 }
378
379 int keySize = cipherInfo.keySize;
380 int ivSize = cipherInfo.ivSize;
381 if (key.length == keySize / 4) // Looks like key is in hex
382 {
383 key = Hex.decode(key);
384 }
385 if (iv.length == ivSize / 4) // Looks like IV is in hex
386 {
387 iv = Hex.decode(iv);
388 }
389 DerivedKey dk = new DerivedKey(key, iv);
390 Cipher c = PKCS8Key.generateCipher(cipherInfo.javaCipher,
391 cipherInfo.blockMode,
392 dk, cipherInfo.des2, null, true);
393 return new CipherInputStream(encrypted, c);
394 }
395
396 public static byte[] encrypt(String cipher, byte[] key, byte[] iv,
397 byte[] data)
398 throws IOException, GeneralSecurityException {
399 return encrypt(cipher, key, iv, data, true);
400 }
401
402 public static byte[] encrypt(String cipher, byte[] key, byte[] iv,
403 byte[] data, boolean toBase64)
404 throws IOException, GeneralSecurityException {
405 ByteArrayInputStream in = new ByteArrayInputStream(data);
406 InputStream encrypted = encrypt(cipher, key, iv, in, toBase64);
407 return Util.streamToBytes(encrypted);
408 }
409
410
411 public static InputStream encrypt(String cipher, byte[] key, byte[] iv,
412 InputStream data)
413 throws IOException, GeneralSecurityException {
414 return encrypt(cipher, key, iv, data, true);
415 }
416
417 public static InputStream encrypt(String cipher, byte[] key, byte[] iv,
418 InputStream data, boolean toBase64)
419 throws IOException, GeneralSecurityException {
420 CipherInfo cipherInfo = lookup(cipher);
421 int keySize = cipherInfo.keySize;
422 int ivSize = cipherInfo.ivSize;
423 if (key.length == keySize / 4) {
424 key = Hex.decode(key);
425 }
426 if (iv.length == ivSize / 4) {
427 iv = Hex.decode(iv);
428 }
429 DerivedKey dk = new DerivedKey(key, iv);
430 Cipher c = PKCS8Key.generateCipher(cipherInfo.javaCipher,
431 cipherInfo.blockMode,
432 dk, cipherInfo.des2, null, false);
433
434 InputStream cipherStream = new CipherInputStream(data, c);
435 if (toBase64) {
436 cipherStream = new Base64InputStream(cipherStream, true);
437 }
438 return cipherStream;
439 }
440
441
442 public static DerivedKey deriveKey(char[] password, byte[] salt,
443 int keySize, boolean des2)
444 throws NoSuchAlgorithmException {
445 return deriveKey(password, salt, keySize, 0, des2);
446 }
447
448 public static DerivedKey deriveKey(char[] password, byte[] salt,
449 int keySize, int ivSize, boolean des2)
450 throws NoSuchAlgorithmException {
451 if (des2) {
452 keySize = 128;
453 }
454 MessageDigest md = MessageDigest.getInstance("MD5");
455 byte[] pwdAsBytes = new byte[password.length];
456 for (int i = 0; i < password.length; i++) {
457 pwdAsBytes[i] = (byte) password[i];
458 }
459
460 md.reset();
461 byte[] keyAndIv = new byte[(keySize / 8) + (ivSize / 8)];
462 if (salt == null || salt.length == 0) {
463 // Unsalted! Bad idea!
464 salt = null;
465 }
466 byte[] result;
467 int currentPos = 0;
468 while (currentPos < keyAndIv.length) {
469 md.update(pwdAsBytes);
470 if (salt != null) {
471 // First 8 bytes of salt ONLY! That wasn't obvious to me
472 // when using AES encrypted private keys in "Traditional
473 // SSLeay Format".
474 //
475 // Example:
476 // DEK-Info: AES-128-CBC,8DA91D5A71988E3D4431D9C2C009F249
477 //
478 // Only the first 8 bytes are salt, but the whole thing is
479 // re-used again later as the IV. MUCH gnashing of teeth!
480 md.update(salt, 0, 8);
481 }
482 result = md.digest();
483 int stillNeed = keyAndIv.length - currentPos;
484 // Digest gave us more than we need. Let's truncate it.
485 if (result.length > stillNeed) {
486 byte[] b = new byte[stillNeed];
487 System.arraycopy(result, 0, b, 0, b.length);
488 result = b;
489 }
490 System.arraycopy(result, 0, keyAndIv, currentPos, result.length);
491 currentPos += result.length;
492 if (currentPos < keyAndIv.length) {
493 // Next round starts with a hash of the hash.
494 md.reset();
495 md.update(result);
496 }
497 }
498 if (des2) {
499 keySize = 192;
500 byte[] buf = new byte[keyAndIv.length + 8];
501 // Make space where 3rd key needs to go (16th - 24th bytes):
502 System.arraycopy(keyAndIv, 0, buf, 0, 16);
503 if (ivSize > 0) {
504 System.arraycopy(keyAndIv, 16, buf, 24, keyAndIv.length - 16);
505 }
506 keyAndIv = buf;
507 // copy first 8 bytes into last 8 bytes to create 2DES key.
508 System.arraycopy(keyAndIv, 0, keyAndIv, 16, 8);
509 }
510 if (ivSize == 0) {
511 // if ivSize == 0, then "keyAndIv" array is actually all key.
512
513 // Must be "Traditional SSLeay Format" encrypted private key in
514 // PEM. The "salt" in its entirety (not just first 8 bytes) will
515 // probably be re-used later as the IV (initialization vector).
516 return new DerivedKey(keyAndIv, salt);
517 } else {
518 byte[] key = new byte[keySize / 8];
519 byte[] iv = new byte[ivSize / 8];
520 System.arraycopy(keyAndIv, 0, key, 0, key.length);
521 System.arraycopy(keyAndIv, key.length, iv, 0, iv.length);
522 return new DerivedKey(key, iv);
523 }
524 }
525
526
527 public static class CipherInfo {
528 public final String javaCipher;
529 public final String blockMode;
530 public final int keySize;
531 public final int ivSize;
532 public final boolean des2;
533
534 public CipherInfo(String javaCipher, String blockMode, int keySize,
535 int ivSize, boolean des2) {
536 this.javaCipher = javaCipher;
537 this.blockMode = blockMode;
538 this.keySize = keySize;
539 this.ivSize = ivSize;
540 this.des2 = des2;
541 }
542
543 public String toString() {
544 return javaCipher + "/" + blockMode + " " + keySize + "bit des2=" + des2;
545 }
546 }
547
548 /**
549 * Converts the way OpenSSL names its ciphers into a Java-friendly naming.
550 *
551 * @param openSSLCipher OpenSSL cipher name, e.g. "des3" or "des-ede3-cbc".
552 * Try "man enc" on a unix box to see what's possible.
553 * @return CipherInfo object with the Java-friendly cipher information.
554 */
555 public static CipherInfo lookup(String openSSLCipher) {
556 openSSLCipher = openSSLCipher.trim();
557 if (openSSLCipher.charAt(0) == '-') {
558 openSSLCipher = openSSLCipher.substring(1);
559 }
560 String javaCipher = openSSLCipher.toUpperCase();
561 String blockMode = "CBC";
562 int keySize = -1;
563 int ivSize = 64;
564 boolean des2 = false;
565
566
567 StringTokenizer st = new StringTokenizer(openSSLCipher, "-");
568 if (st.hasMoreTokens()) {
569 javaCipher = st.nextToken().toUpperCase();
570 if (st.hasMoreTokens()) {
571 // Is this the middle token? Or the last token?
572 String tok = st.nextToken();
573 if (st.hasMoreTokens()) {
574 try {
575 keySize = Integer.parseInt(tok);
576 }
577 catch (NumberFormatException nfe) {
578 // I guess 2nd token isn't an integer
579 String upper = tok.toUpperCase();
580 if (upper.startsWith("EDE3")) {
581 javaCipher = "DESede";
582 } else if (upper.startsWith("EDE")) {
583 javaCipher = "DESede";
584 des2 = true;
585 }
586 }
587 blockMode = st.nextToken().toUpperCase();
588 } else {
589 try {
590 keySize = Integer.parseInt(tok);
591 }
592 catch (NumberFormatException nfe) {
593 // It's the last token, so must be mode (usually "CBC").
594 blockMode = tok.toUpperCase();
595 if (blockMode.startsWith("EDE3")) {
596 javaCipher = "DESede";
597 blockMode = "ECB";
598 } else if (blockMode.startsWith("EDE")) {
599 javaCipher = "DESede";
600 blockMode = "ECB";
601 des2 = true;
602 }
603 }
604 }
605 }
606 }
607 if (javaCipher.startsWith("BF")) {
608 javaCipher = "Blowfish";
609 } else if (javaCipher.startsWith("TWOFISH")) {
610 javaCipher = "Twofish";
611 ivSize = 128;
612 } else if (javaCipher.startsWith("IDEA")) {
613 javaCipher = "IDEA";
614 } else if (javaCipher.startsWith("CAST6")) {
615 javaCipher = "CAST6";
616 ivSize = 128;
617 } else if (javaCipher.startsWith("CAST")) {
618 javaCipher = "CAST5";
619 } else if (javaCipher.startsWith("GOST")) {
620 keySize = 256;
621 } else if (javaCipher.startsWith("DESX")) {
622 javaCipher = "DESX";
623 } else if ("DES3".equals(javaCipher)) {
624 javaCipher = "DESede";
625 } else if ("DES2".equals(javaCipher)) {
626 javaCipher = "DESede";
627 des2 = true;
628 } else if (javaCipher.startsWith("RIJNDAEL")) {
629 javaCipher = "Rijndael";
630 ivSize = 128;
631 } else if (javaCipher.startsWith("SEED")) {
632 javaCipher = "SEED";
633 ivSize = 128;
634 } else if (javaCipher.startsWith("SERPENT")) {
635 javaCipher = "Serpent";
636 ivSize = 128;
637 } else if (javaCipher.startsWith("Skipjack")) {
638 javaCipher = "Skipjack";
639 ivSize = 128;
640 } else if (javaCipher.startsWith("RC6")) {
641 javaCipher = "RC6";
642 ivSize = 128;
643 } else if (javaCipher.startsWith("TEA")) {
644 javaCipher = "TEA";
645 } else if (javaCipher.startsWith("XTEA")) {
646 javaCipher = "XTEA";
647 } else if (javaCipher.startsWith("AES")) {
648 if (javaCipher.startsWith("AES128")) {
649 keySize = 128;
650 } else if (javaCipher.startsWith("AES192")) {
651 keySize = 192;
652 } else if (javaCipher.startsWith("AES256")) {
653 keySize = 256;
654 }
655 javaCipher = "AES";
656 ivSize = 128;
657 } else if (javaCipher.startsWith("CAMELLIA")) {
658 if (javaCipher.startsWith("CAMELLIA128")) {
659 keySize = 128;
660 } else if (javaCipher.startsWith("CAMELLIA192")) {
661 keySize = 192;
662 } else if (javaCipher.startsWith("CAMELLIA256")) {
663 keySize = 256;
664 }
665 javaCipher = "CAMELLIA";
666 ivSize = 128;
667 }
668 if (keySize == -1) {
669 if (javaCipher.startsWith("DESede")) {
670 keySize = 192;
671 } else if (javaCipher.startsWith("DES")) {
672 keySize = 64;
673 } else {
674 // RC2, RC4, RC5 and Blowfish ?
675 keySize = 128;
676 }
677 }
678 return new CipherInfo(javaCipher, blockMode, keySize, ivSize, des2);
679 }
680
681
682 /**
683 * @param args command line arguments: [password] [cipher] [file-to-decrypt]
684 * <br>[cipher] == OpenSSL cipher name, e.g. "des3" or "des-ede3-cbc".
685 * Try "man enc" on a unix box to see what's possible.
686 * @throws IOException problems with the [file-to-decrypt]
687 * @throws GeneralSecurityException decryption problems
688 */
689 public static void main(String[] args)
690 throws IOException, GeneralSecurityException {
691 if (args.length < 3) {
692 System.out.println(Version.versionString());
693 System.out.println("Pure-java utility to decrypt files previously encrypted by \'openssl enc\'");
694 System.out.println();
695 System.out.println("Usage: java -cp commons-ssl.jar org.apache.commons.ssl.OpenSSL [args]");
696 System.out.println(" [args] == [password] [cipher] [file-to-decrypt]");
697 System.out.println(" [cipher] == des, des3, des-ede3-cbc, aes256, rc2, rc4, bf, bf-cbc, etc...");
698 System.out.println(" Try 'man enc' on a unix box to see what's possible.");
699 System.out.println();
700 System.out.println("This utility can handle base64 or raw, salted or unsalted.");
701 System.out.println();
702 System.exit(1);
703 }
704 char[] password = args[0].toCharArray();
705
706 InputStream in = new FileInputStream(args[2]);
707 in = decrypt(args[1], password, in);
708
709 // in = encrypt( args[ 1 ], pwdAsBytes, in, true );
710
711 in = new BufferedInputStream(in);
712 BufferedOutputStream bufOut = new BufferedOutputStream( System.out );
713 Util.pipeStream(in, bufOut, false);
714 bufOut.flush();
715 System.out.flush();
716 }
717
718 }