1 package org.bouncycastle.openssl; 2 3 import org.bouncycastle.crypto.PBEParametersGenerator; 4 import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator; 5 import org.bouncycastle.crypto.params.KeyParameter; 6 7 import javax.crypto.Cipher; 8 import javax.crypto.SecretKey; 9 import javax.crypto.spec.IvParameterSpec; 10 import javax.crypto.spec.RC2ParameterSpec; 11 import java.io.IOException; 12 import java.security.Key; 13 import java.security.spec.AlgorithmParameterSpec; 14 15 final class PEMUtilities 16 { 17 static byte[] crypt( 18 boolean encrypt, 19 String provider, 20 byte[] bytes, 21 char[] password, 22 String dekAlgName, 23 byte[] iv) 24 throws IOException 25 { 26 AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv); 27 String alg; 28 String blockMode = "CBC"; 29 String padding = "PKCS5Padding"; 30 Key sKey; 31 32 33 // Figure out block mode and padding. 34 if (dekAlgName.endsWith("-CFB")) 35 { 36 blockMode = "CFB"; 37 padding = "NoPadding"; 38 } 39 if (dekAlgName.endsWith("-ECB") || 40 "DES-EDE".equals(dekAlgName) || 41 "DES-EDE3".equals(dekAlgName)) 42 { 43 // ECB is actually the default (though seldom used) when OpenSSL 44 // uses DES-EDE (des2) or DES-EDE3 (des3). 45 blockMode = "ECB"; 46 paramSpec = null; 47 } 48 if (dekAlgName.endsWith("-OFB")) 49 { 50 blockMode = "OFB"; 51 padding = "NoPadding"; 52 } 53 54 55 // Figure out algorithm and key size. 56 if (dekAlgName.startsWith("DES-EDE")) 57 { 58 alg = "DESede"; 59 // "DES-EDE" is actually des2 in OpenSSL-speak! 60 // "DES-EDE3" is des3. 61 boolean des2 = !dekAlgName.startsWith("DES-EDE3"); 62 sKey = getKey(password, alg, 24, iv, des2); 63 } 64 else if (dekAlgName.startsWith("DES-")) 65 { 66 alg = "DES"; 67 sKey = getKey(password, alg, 8, iv); 68 } 69 else if (dekAlgName.startsWith("BF-")) 70 { 71 alg = "Blowfish"; 72 sKey = getKey(password, alg, 16, iv); 73 } 74 else if (dekAlgName.startsWith("RC2-")) 75 { 76 alg = "RC2"; 77 int keyBits = 128; 78 if (dekAlgName.startsWith("RC2-40-")) 79 { 80 keyBits = 40; 81 } 82 else if (dekAlgName.startsWith("RC2-64-")) 83 { 84 keyBits = 64; 85 } 86 sKey = getKey(password, alg, keyBits / 8, iv); 87 if (paramSpec == null) // ECB block mode 88 { 89 paramSpec = new RC2ParameterSpec(keyBits); 90 } 91 else 92 { 93 paramSpec = new RC2ParameterSpec(keyBits, iv); 94 } 95 } 96 else if (dekAlgName.startsWith("AES-")) 97 { 98 alg = "AES"; 99 byte[] salt = iv; 100 if (salt.length > 8) 101 { 102 salt = new byte[8]; 103 System.arraycopy(iv, 0, salt, 0, 8); 104 } 105 106 int keyBits; 107 if (dekAlgName.startsWith("AES-128-")) 108 { 109 keyBits = 128; 110 } 111 else if (dekAlgName.startsWith("AES-192-")) 112 { 113 keyBits = 192; 114 } 115 else if (dekAlgName.startsWith("AES-256-")) 116 { 117 keyBits = 256; 118 } 119 else 120 { 121 throw new EncryptionException("unknown AES encryption with private key"); 122 } 123 sKey = getKey(password, "AES", keyBits / 8, salt); 124 } 125 else 126 { 127 throw new EncryptionException("unknown encryption with private key"); 128 } 129 130 String transformation = alg + "/" + blockMode + "/" + padding; 131 132 try 133 { 134 Cipher c = Cipher.getInstance(transformation, provider); 135 int mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; 136 137 if (paramSpec == null) // ECB block mode 138 { 139 c.init(mode, sKey); 140 } 141 else 142 { 143 c.init(mode, sKey, paramSpec); 144 } 145 return c.doFinal(bytes); 146 } 147 catch (Exception e) 148 { 149 throw new EncryptionException("exception using cipher - please check password and data.", e); 150 } 151 } 152 153 private static SecretKey getKey( 154 char[] password, 155 String algorithm, 156 int keyLength, 157 byte[] salt) 158 { 159 return getKey(password, algorithm, keyLength, salt, false); 160 } 161 162 private static SecretKey getKey( 163 char[] password, 164 String algorithm, 165 int keyLength, 166 byte[] salt, 167 boolean des2) 168 { 169 OpenSSLPBEParametersGenerator pGen = new OpenSSLPBEParametersGenerator(); 170 171 pGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt); 172 173 KeyParameter keyParam; 174 keyParam = (KeyParameter) pGen.generateDerivedParameters(keyLength * 8); 175 byte[] key = keyParam.getKey(); 176 if (des2 && key.length >= 24) 177 { 178 // For DES2, we must copy first 8 bytes into the last 8 bytes. 179 System.arraycopy(key, 0, key, 16, 8); 180 } 181 return new javax.crypto.spec.SecretKeySpec(key, algorithm); 182 } 183 } 184