001/* 002 * $HeadURL: file:///opt/dev/not-yet-commons-ssl-SVN-repo/tags/commons-ssl-0.3.17/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 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.*; 039import java.security.GeneralSecurityException; 040import java.security.MessageDigest; 041import java.security.NoSuchAlgorithmException; 042import java.security.SecureRandom; 043import 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 */ 073public 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}