1 package org.bouncycastle.openssl; 2 3 import java.io.IOException; 4 import java.security.Key; 5 import java.security.Provider; 6 import java.security.Security; 7 import java.security.spec.AlgorithmParameterSpec; 8 import java.util.HashMap; 9 import java.util.HashSet; 10 import java.util.Map; 11 import java.util.Set; 12 13 import javax.crypto.Cipher; 14 import javax.crypto.SecretKey; 15 import javax.crypto.spec.IvParameterSpec; 16 import javax.crypto.spec.RC2ParameterSpec; 17 import javax.crypto.spec.SecretKeySpec; 18 19 import org.bouncycastle.asn1.DERObjectIdentifier; 20 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; 21 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 22 import org.bouncycastle.crypto.PBEParametersGenerator; 23 import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator; 24 import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; 25 import org.bouncycastle.crypto.params.KeyParameter; 26 27 final class PEMUtilities 28 { 29 private static final Map KEYSIZES = new HashMap(); 30 private static final Set PKCS5_SCHEME_1 = new HashSet(); 31 private static final Set PKCS5_SCHEME_2 = new HashSet(); 32 33 static 34 { 35 PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC); 36 PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC); 37 PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC); 38 PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC); 39 PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC); 40 PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC); 41 42 PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.id_PBES2); 43 PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.des_EDE3_CBC); 44 PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes128_CBC); 45 PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes192_CBC); 46 PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes256_CBC); 47 48 // BEGIN android-changed 49 KEYSIZES.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integer.valueOf(192)); 50 KEYSIZES.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), Integer.valueOf(128)); 51 KEYSIZES.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), Integer.valueOf(192)); 52 KEYSIZES.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), Integer.valueOf(256)); 53 // END android-changed 54 } 55 56 static int getKeySize(String algorithm) 57 { 58 if (!KEYSIZES.containsKey(algorithm)) 59 { 60 throw new IllegalStateException("no key size for algorithm: " + algorithm); 61 } 62 63 return ((Integer)KEYSIZES.get(algorithm)).intValue(); 64 } 65 66 static boolean isPKCS5Scheme1(DERObjectIdentifier algOid) 67 { 68 return PKCS5_SCHEME_1.contains(algOid); 69 } 70 71 static boolean isPKCS5Scheme2(DERObjectIdentifier algOid) 72 { 73 return PKCS5_SCHEME_2.contains(algOid); 74 } 75 76 static boolean isPKCS12(DERObjectIdentifier algOid) 77 { 78 return algOid.getId().startsWith(PKCSObjectIdentifiers.pkcs_12PbeIds.getId()); 79 } 80 81 static SecretKey generateSecretKeyForPKCS5Scheme2(String algorithm, char[] password, byte[] salt, int iterationCount) 82 { 83 PBEParametersGenerator generator = new PKCS5S2ParametersGenerator(); 84 85 generator.init( 86 PBEParametersGenerator.PKCS5PasswordToBytes(password), 87 salt, 88 iterationCount); 89 90 return new SecretKeySpec(((KeyParameter)generator.generateDerivedParameters(PEMUtilities.getKeySize(algorithm))).getKey(), algorithm); 91 } 92 93 static byte[] crypt( 94 boolean encrypt, 95 String provider, 96 byte[] bytes, 97 char[] password, 98 String dekAlgName, 99 byte[] iv) 100 throws IOException 101 { 102 Provider prov = null; 103 if (provider != null) 104 { 105 prov = Security.getProvider(provider); 106 if (prov == null) 107 { 108 throw new EncryptionException("cannot find provider: " + provider); 109 } 110 } 111 112 return crypt(encrypt, prov, bytes, password, dekAlgName, iv); 113 } 114 115 static byte[] crypt( 116 boolean encrypt, 117 Provider provider, 118 byte[] bytes, 119 char[] password, 120 String dekAlgName, 121 byte[] iv) 122 throws IOException 123 { 124 AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv); 125 String alg; 126 String blockMode = "CBC"; 127 String padding = "PKCS5Padding"; 128 Key sKey; 129 130 // Figure out block mode and padding. 131 if (dekAlgName.endsWith("-CFB")) 132 { 133 blockMode = "CFB"; 134 padding = "NoPadding"; 135 } 136 if (dekAlgName.endsWith("-ECB") || 137 "DES-EDE".equals(dekAlgName) || 138 "DES-EDE3".equals(dekAlgName)) 139 { 140 // ECB is actually the default (though seldom used) when OpenSSL 141 // uses DES-EDE (des2) or DES-EDE3 (des3). 142 blockMode = "ECB"; 143 paramSpec = null; 144 } 145 if (dekAlgName.endsWith("-OFB")) 146 { 147 blockMode = "OFB"; 148 padding = "NoPadding"; 149 } 150 151 152 // Figure out algorithm and key size. 153 if (dekAlgName.startsWith("DES-EDE")) 154 { 155 alg = "DESede"; 156 // "DES-EDE" is actually des2 in OpenSSL-speak! 157 // "DES-EDE3" is des3. 158 boolean des2 = !dekAlgName.startsWith("DES-EDE3"); 159 sKey = getKey(password, alg, 24, iv, des2); 160 } 161 else if (dekAlgName.startsWith("DES-")) 162 { 163 alg = "DES"; 164 sKey = getKey(password, alg, 8, iv); 165 } 166 else if (dekAlgName.startsWith("BF-")) 167 { 168 alg = "Blowfish"; 169 sKey = getKey(password, alg, 16, iv); 170 } 171 else if (dekAlgName.startsWith("RC2-")) 172 { 173 alg = "RC2"; 174 int keyBits = 128; 175 if (dekAlgName.startsWith("RC2-40-")) 176 { 177 keyBits = 40; 178 } 179 else if (dekAlgName.startsWith("RC2-64-")) 180 { 181 keyBits = 64; 182 } 183 sKey = getKey(password, alg, keyBits / 8, iv); 184 if (paramSpec == null) // ECB block mode 185 { 186 paramSpec = new RC2ParameterSpec(keyBits); 187 } 188 else 189 { 190 paramSpec = new RC2ParameterSpec(keyBits, iv); 191 } 192 } 193 else if (dekAlgName.startsWith("AES-")) 194 { 195 alg = "AES"; 196 byte[] salt = iv; 197 if (salt.length > 8) 198 { 199 salt = new byte[8]; 200 System.arraycopy(iv, 0, salt, 0, 8); 201 } 202 203 int keyBits; 204 if (dekAlgName.startsWith("AES-128-")) 205 { 206 keyBits = 128; 207 } 208 else if (dekAlgName.startsWith("AES-192-")) 209 { 210 keyBits = 192; 211 } 212 else if (dekAlgName.startsWith("AES-256-")) 213 { 214 keyBits = 256; 215 } 216 else 217 { 218 throw new EncryptionException("unknown AES encryption with private key"); 219 } 220 sKey = getKey(password, "AES", keyBits / 8, salt); 221 } 222 else 223 { 224 throw new EncryptionException("unknown encryption with private key"); 225 } 226 227 String transformation = alg + "/" + blockMode + "/" + padding; 228 229 try 230 { 231 Cipher c = Cipher.getInstance(transformation, provider); 232 int mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; 233 234 if (paramSpec == null) // ECB block mode 235 { 236 c.init(mode, sKey); 237 } 238 else 239 { 240 c.init(mode, sKey, paramSpec); 241 } 242 return c.doFinal(bytes); 243 } 244 catch (Exception e) 245 { 246 throw new EncryptionException("exception using cipher - please check password and data.", e); 247 } 248 } 249 250 private static SecretKey getKey( 251 char[] password, 252 String algorithm, 253 int keyLength, 254 byte[] salt) 255 { 256 return getKey(password, algorithm, keyLength, salt, false); 257 } 258 259 private static SecretKey getKey( 260 char[] password, 261 String algorithm, 262 int keyLength, 263 byte[] salt, 264 boolean des2) 265 { 266 OpenSSLPBEParametersGenerator pGen = new OpenSSLPBEParametersGenerator(); 267 268 pGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt); 269 270 KeyParameter keyParam; 271 keyParam = (KeyParameter) pGen.generateDerivedParameters(keyLength * 8); 272 byte[] key = keyParam.getKey(); 273 if (des2 && key.length >= 24) 274 { 275 // For DES2, we must copy first 8 bytes into the last 8 bytes. 276 System.arraycopy(key, 0, key, 16, 8); 277 } 278 return new javax.crypto.spec.SecretKeySpec(key, algorithm); 279 } 280 } 281