001/** 002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 003 * 004 * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved 005 * 006 * The contents of this file are subject to the terms 007 * of the Common Development and Distribution License 008 * (the License). You may not use this file except in 009 * compliance with the License. 010 * 011 * You can obtain a copy of the License at 012 * https://opensso.dev.java.net/public/CDDLv1.0.html or 013 * opensso/legal/CDDLv1.0.txt 014 * See the License for the specific language governing 015 * permission and limitations under the License. 016 * 017 * When distributing Covered Code, include this CDDL 018 * Header Notice in each file and include the License file 019 * at opensso/legal/CDDLv1.0.txt. 020 * If applicable, add the following below the CDDL Header, 021 * with the fields enclosed by brackets [] replaced by 022 * your own identifying information: 023 * "Portions Copyrighted [year] [name of copyright owner]" 024 * 025 * $Id: DataEncryptor.java,v 1.1 2009/02/26 23:58:41 exu Exp $ 026 * 027 */ 028 029package com.sun.identity.security; 030 031import java.util.Map; 032import java.util.HashMap; 033import javax.crypto.Cipher; 034import javax.crypto.KeyGenerator; 035import javax.crypto.SecretKey; 036import java.security.NoSuchAlgorithmException; 037import javax.crypto.NoSuchPaddingException; 038import java.security.Key; 039import java.security.InvalidKeyException; 040import java.io.UnsupportedEncodingException; 041import javax.crypto.SecretKeyFactory; 042import javax.crypto.spec.PBEKeySpec; 043import javax.crypto.spec.PBEParameterSpec; 044 045import com.sun.identity.shared.encode.Base64; 046 047 048/** 049 * This class <code>DataEncryptor</code> is used to encrypt the data 050 * with symmetric and asymmetric keys. 051 * @supported.all.api 052 */ 053public class DataEncryptor { 054 055 private static final String ENCRYPTED_DATA = "EncryptedData"; 056 057 private static final String ENCRYPTED_KEY = "EncryptedKey"; 058 private static final byte[] ___y = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 059 0x01, 0x01 }; 060 private static final int ITERATION_COUNT = 5; 061 private static PBEParameterSpec pbeParameterSpec = new PBEParameterSpec( 062 ___y, ITERATION_COUNT); 063 064 /** 065 * Encrypts the given data with an asymmetric key. The asymmetric 066 * encryption uses symmetric secret key for data encryption and sends 067 * the secret key to the recipient by encrypting the same with given 068 * transport key (publick key). 069 * @param data the data to be encrypted. 070 * @param encryptionAlgorithm the encryption algorithm to be used. 071 * The encryption algorithm must be one of the supported 072 * algorithm by the underlying JCE encryption provider. 073 * Examples of encryption algorithms are "DES", "AES" etc. 074 * @param encryptionStrength the encryption strength for a given 075 * encryption algorithm. 076 * @param encKey the encryption key to be used. For PKI, this 077 * key should be public key of the intended recipient. 078 * @return the encrypted data in Base64 encoded format. 079 */ 080 public static String encryptWithAsymmetricKey(String data, 081 String encryptionAlgorithm, 082 int encryptionStrength, 083 Key encKey) throws Exception { 084 try { 085 KeyGenerator keygen = KeyGenerator.getInstance(encryptionAlgorithm); 086 if(encryptionStrength != 0) { 087 keygen.init(encryptionStrength); 088 } 089 SecretKey sKey = keygen.generateKey(); 090 Cipher cipher = Cipher.getInstance(encryptionAlgorithm); 091 cipher.init(Cipher.ENCRYPT_MODE, sKey); 092 byte[] encData = cipher.doFinal(data.getBytes("UTF-8")); 093 cipher = Cipher.getInstance(encKey.getAlgorithm()); 094 cipher.init(Cipher.WRAP_MODE, encKey); 095 byte[] keyWrap = cipher.wrap(sKey); 096 byte[] encDataPad = wrapKeyWithEncryptedData(encData, keyWrap); 097 return Base64.encode(encDataPad); 098 } catch (NoSuchAlgorithmException nse) { 099 throw new Exception(nse.getMessage()); 100 } catch (NoSuchPaddingException npe) { 101 throw new Exception(npe.getMessage()); 102 } catch (InvalidKeyException ike) { 103 throw new Exception(ike.getMessage()); 104 } catch (UnsupportedEncodingException uae) { 105 throw new Exception(uae.getMessage()); 106 } 107 } 108 109 /** 110 * Decrypts the given data with asymmetric key. 111 * @param data the data to be decrypted. 112 * @param encAlgorithm the encryption algorithm was used for encrypted 113 * data. 114 * @param encKey the private key for decrypting the data. 115 * @return the decrypted data. 116 */ 117 public static String decryptWithAsymmetricKey( 118 String data, String encAlgorithm, Key encKey) throws Exception { 119 120 try { 121 byte[] tmp = Base64.decode(data); 122 Map map = unwrapKeyWithEncodedData(tmp); 123 byte[] encData = (byte[])map.get(ENCRYPTED_DATA); 124 byte[] keyData = (byte[])map.get(ENCRYPTED_KEY); 125 Cipher cipher = Cipher.getInstance(encKey.getAlgorithm()); 126 cipher.init(Cipher.UNWRAP_MODE, encKey); 127 Key secretKey = cipher.unwrap(keyData, encAlgorithm, 128 Cipher.SECRET_KEY); 129 cipher = Cipher.getInstance(encAlgorithm); 130 cipher.init(Cipher.DECRYPT_MODE, secretKey); 131 byte[] decryptedData = cipher.doFinal(encData); 132 return Base64.encode(decryptedData); 133 } catch (NoSuchAlgorithmException nse) { 134 throw new Exception(nse.getMessage()); 135 } catch (InvalidKeyException ike) { 136 throw new Exception(ike.getMessage()); 137 } 138 } 139 140 /** 141 * Encrypts the given data with a symmetric key that was generated 142 * using given shared secret. 143 * @param data the data to be encrypted. 144 * @param encAlgorithm the encryption algorithm to be used. 145 * The encryption algorithm must be one of the supported 146 * algorithm by the underlying JCE encryption provider. 147 * For password based encryptions, the encryption algorithm 148 * PBEWithMD5AndDES is commonly used. 149 * @param secret the shared secret to be used for symmetric encryption. 150 * @return the encrypted data in Base64 encoded format. 151 */ 152 public static String encryptWithSymmetricKey(String data, 153 String encAlgorithm, String secret) throws Exception { 154 try { 155 String algorithm = encAlgorithm; 156 if(!algorithm.startsWith("PBEWith")) { 157 algorithm = "PBEWithMD5And" + encAlgorithm; 158 } 159 SecretKeyFactory skFactory = 160 SecretKeyFactory.getInstance(algorithm); 161 PBEKeySpec pbeKeySpec = new PBEKeySpec(secret.toCharArray()); 162 SecretKey sKey = skFactory.generateSecret(pbeKeySpec); 163 Cipher cipher = Cipher.getInstance(algorithm); 164 cipher.init(Cipher.ENCRYPT_MODE, sKey, pbeParameterSpec ); 165 byte[] encData = cipher.doFinal(data.getBytes("UTF-8")); 166 encData = addPrefix(encData); 167 return Base64.encode(encData); 168 } catch (NoSuchAlgorithmException nse) { 169 throw new Exception(nse.getMessage()); 170 } 171 } 172 173 /** 174 * Decrypts the given data with a symmetric key generated using shared 175 * secret. 176 * @param data the data to be decrypted with symmetric key. 177 * @param encAlgorithm the encryption algorithm was used for 178 * encrypting the data. 179 * @param secret the shared secret to be used for decrypting the data. 180 * @return the decrypted data. 181 */ 182 public static String decryptWithSymmetricKey(String data, 183 String encAlgorithm, String secret) throws Exception { 184 try { 185 String algorithm = encAlgorithm; 186 if(!algorithm.startsWith("PBEWith")) { 187 algorithm = "PBEWithMD5And" + encAlgorithm; 188 } 189 SecretKeyFactory skFactory = 190 SecretKeyFactory.getInstance(algorithm); 191 PBEKeySpec pbeKeySpec = new PBEKeySpec(secret.toCharArray()); 192 SecretKey sKey = skFactory.generateSecret(pbeKeySpec); 193 Cipher cipher = Cipher.getInstance(algorithm); 194 cipher.init(Cipher.DECRYPT_MODE, sKey, pbeParameterSpec); 195 byte[] tmp = Base64.decode(data); 196 byte[] encData = removePrefix(tmp); 197 byte[] decData = cipher.doFinal(encData); 198 return Base64.encode(decData); 199 } catch (NoSuchAlgorithmException nse) { 200 throw new Exception(nse.getMessage()); 201 } 202 } 203 204 private static byte[] addPrefix(byte[] encData) { 205 int length = encData.length; 206 byte[] result = new byte[9 + length]; 207 byte[] encrypted = new String("ENCRYPTED").getBytes(); 208 for (int i=0; i < 9; i++) { 209 result[i] = encrypted[i]; 210 } 211 for (int i=0; i < length; i++) { 212 result[9 + i] = encData[i]; 213 } 214 return result; 215 } 216 217 private static byte[] removePrefix(byte[] data) { 218 int length = data.length - 9; 219 byte[] result = new byte[length]; 220 for (int i=0; i < length; i++) { 221 result[i] = data[9 + i]; 222 } 223 return result; 224 } 225 226 private static byte[] wrapKeyWithEncryptedData(byte[] data, 227 byte[] key) { 228 229 int dataLength = data.length; 230 int keyLength = key.length; 231 byte[] result = new byte[17 + data.length + key.length]; 232 byte[] encrypted = new String("ENCRYPTED").getBytes(); 233 for (int i=0; i < 9; i++) { 234 result[i] = encrypted[i]; 235 } 236 byte[] datasize = intToByteArray(dataLength); 237 for (int i =0; i < 4; i++) { 238 result[i+9] = datasize[i]; 239 } 240 241 for (int i=0; i < dataLength; i++) { 242 result[i+13] = data[i]; 243 } 244 245 byte[] keysize = intToByteArray(keyLength); 246 for (int i =0; i < 4; i++) { 247 result[i+13+dataLength] = keysize[i]; 248 } 249 250 for (int i=0; i < keyLength; i++) { 251 result[i+17+dataLength] = key[i]; 252 } 253 254 return result; 255 } 256 257 private static Map unwrapKeyWithEncodedData(byte[] decodeData) { 258 Map map = new HashMap(); 259 260 byte[] dataLength = new byte[4]; 261 int j = 0; 262 for (int i = 9; i < 13; i++) { 263 dataLength[j] = decodeData[i]; 264 j++; 265 } 266 267 int encDataLength = byteArrayToInt(dataLength); 268 byte[] encData = new byte[encDataLength]; 269 j = 0; 270 for (int i=13; i < encDataLength + 13; i++) { 271 encData[j] = decodeData[i]; 272 j++; 273 } 274 275 map.put(ENCRYPTED_DATA, encData); 276 byte[] keyLen = new byte[4]; 277 int startIndex = 13 + encDataLength; 278 int endIndex = startIndex + 4; 279 j = 0; 280 for (int i=startIndex; i < endIndex; i++) { 281 keyLen[j] = decodeData[i]; 282 j++; 283 } 284 285 int keyDataLength = byteArrayToInt(keyLen); 286 startIndex = startIndex + 4; 287 endIndex = startIndex + keyDataLength; 288 byte[] keyData = new byte[keyDataLength]; 289 j = 0; 290 for (int i=startIndex; i < endIndex; i++) { 291 keyData[j] = decodeData[i]; 292 j++; 293 } 294 map.put(ENCRYPTED_KEY, keyData); 295 return map; 296 } 297 298 299 private static final byte[] intToByteArray(int value) { 300 return new byte[] { 301 (byte)(value >>> 24), 302 (byte)(value >>> 16), 303 (byte)(value >>> 8), 304 (byte)value 305 }; 306 } 307 308 private static final int byteArrayToInt(byte [] b) { 309 return (b[0] << 24) 310 + ((b[1] & 0xFF) << 16) 311 + ((b[2] & 0xFF) << 8) 312 + (b[3] & 0xFF); 313 } 314 315 316 317}