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