DESedeWrapEngine.java revision 7dad97b63c47edea4e3afb374dcd00c7b7a1bdd4
1package org.bouncycastle.crypto.engines; 2 3import java.security.SecureRandom; 4 5import org.bouncycastle.crypto.CipherParameters; 6import org.bouncycastle.crypto.Digest; 7import org.bouncycastle.crypto.InvalidCipherTextException; 8import org.bouncycastle.crypto.Wrapper; 9// BEGIN android-changed 10import org.bouncycastle.crypto.digests.AndroidDigestFactory; 11// END android-changed 12import org.bouncycastle.crypto.modes.CBCBlockCipher; 13import org.bouncycastle.crypto.params.KeyParameter; 14import org.bouncycastle.crypto.params.ParametersWithIV; 15import org.bouncycastle.crypto.params.ParametersWithRandom; 16// BEGIN android-removed 17// import org.bouncycastle.crypto.util.DigestFactory; 18// END android-removed 19import org.bouncycastle.util.Arrays; 20 21/** 22 * Wrap keys according to 23 * <A HREF="https://www.ietf.org/rfc/rfc3217.txt"> 24 * RFC 3217</A>. 25 * <p> 26 * Note: 27 * <ul> 28 * <li>if you are using this to wrap triple-des keys you need to set the 29 * parity bits on the key and, if it's a two-key triple-des key, pad it 30 * yourself. 31 * </ul> 32 */ 33public class DESedeWrapEngine 34 implements Wrapper 35{ 36 /** Field engine */ 37 private CBCBlockCipher engine; 38 39 /** Field param */ 40 private KeyParameter param; 41 42 /** Field paramPlusIV */ 43 private ParametersWithIV paramPlusIV; 44 45 /** Field iv */ 46 private byte[] iv; 47 48 /** Field forWrapping */ 49 private boolean forWrapping; 50 51 /** Field IV2 */ 52 private static final byte[] IV2 = { (byte) 0x4a, (byte) 0xdd, (byte) 0xa2, 53 (byte) 0x2c, (byte) 0x79, (byte) 0xe8, 54 (byte) 0x21, (byte) 0x05 }; 55 56 // 57 // checksum digest 58 // 59 // BEGIN android-changed 60 Digest sha1 = AndroidDigestFactory.getSHA1(); 61 // END android-changed 62 byte[] digest = new byte[20]; 63 64 /** 65 * Method init 66 * 67 * @param forWrapping true if for wrapping, false otherwise. 68 * @param param necessary parameters, may include KeyParameter, ParametersWithRandom, and ParametersWithIV 69 */ 70 public void init(boolean forWrapping, CipherParameters param) 71 { 72 73 this.forWrapping = forWrapping; 74 this.engine = new CBCBlockCipher(new DESedeEngine()); 75 76 SecureRandom sr; 77 if (param instanceof ParametersWithRandom) 78 { 79 ParametersWithRandom pr = (ParametersWithRandom) param; 80 param = pr.getParameters(); 81 sr = pr.getRandom(); 82 } 83 else 84 { 85 sr = new SecureRandom(); 86 } 87 88 if (param instanceof KeyParameter) 89 { 90 this.param = (KeyParameter)param; 91 92 if (this.forWrapping) 93 { 94 95 // Hm, we have no IV but we want to wrap ?!? 96 // well, then we have to create our own IV. 97 this.iv = new byte[8]; 98 sr.nextBytes(iv); 99 100 this.paramPlusIV = new ParametersWithIV(this.param, this.iv); 101 } 102 } 103 else if (param instanceof ParametersWithIV) 104 { 105 this.paramPlusIV = (ParametersWithIV)param; 106 this.iv = this.paramPlusIV.getIV(); 107 this.param = (KeyParameter)this.paramPlusIV.getParameters(); 108 109 if (this.forWrapping) 110 { 111 if ((this.iv == null) || (this.iv.length != 8)) 112 { 113 throw new IllegalArgumentException("IV is not 8 octets"); 114 } 115 } 116 else 117 { 118 throw new IllegalArgumentException( 119 "You should not supply an IV for unwrapping"); 120 } 121 } 122 } 123 124 /** 125 * Method getAlgorithmName 126 * 127 * @return the algorithm name "DESede". 128 */ 129 public String getAlgorithmName() 130 { 131 return "DESede"; 132 } 133 134 /** 135 * Method wrap 136 * 137 * @param in byte array containing the encoded key. 138 * @param inOff off set into in that the data starts at. 139 * @param inLen length of the data. 140 * @return the wrapped bytes. 141 */ 142 public byte[] wrap(byte[] in, int inOff, int inLen) 143 { 144 if (!forWrapping) 145 { 146 throw new IllegalStateException("Not initialized for wrapping"); 147 } 148 149 byte keyToBeWrapped[] = new byte[inLen]; 150 151 System.arraycopy(in, inOff, keyToBeWrapped, 0, inLen); 152 153 // Compute the CMS Key Checksum, (section 5.6.1), call this CKS. 154 byte[] CKS = calculateCMSKeyChecksum(keyToBeWrapped); 155 156 // Let WKCKS = WK || CKS where || is concatenation. 157 byte[] WKCKS = new byte[keyToBeWrapped.length + CKS.length]; 158 159 System.arraycopy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.length); 160 System.arraycopy(CKS, 0, WKCKS, keyToBeWrapped.length, CKS.length); 161 162 // Encrypt WKCKS in CBC mode using KEK as the key and IV as the 163 // initialization vector. Call the results TEMP1. 164 165 int blockSize = engine.getBlockSize(); 166 167 if (WKCKS.length % blockSize != 0) 168 { 169 throw new IllegalStateException("Not multiple of block length"); 170 } 171 172 engine.init(true, paramPlusIV); 173 174 byte TEMP1[] = new byte[WKCKS.length]; 175 176 for (int currentBytePos = 0; currentBytePos != WKCKS.length; currentBytePos += blockSize) 177 { 178 engine.processBlock(WKCKS, currentBytePos, TEMP1, currentBytePos); 179 } 180 181 // Let TEMP2 = IV || TEMP1. 182 byte[] TEMP2 = new byte[this.iv.length + TEMP1.length]; 183 184 System.arraycopy(this.iv, 0, TEMP2, 0, this.iv.length); 185 System.arraycopy(TEMP1, 0, TEMP2, this.iv.length, TEMP1.length); 186 187 // Reverse the order of the octets in TEMP2 and call the result TEMP3. 188 byte[] TEMP3 = reverse(TEMP2); 189 190 // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector 191 // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the desired 192 // result. It is 40 octets long if a 168 bit key is being wrapped. 193 ParametersWithIV param2 = new ParametersWithIV(this.param, IV2); 194 195 this.engine.init(true, param2); 196 197 for (int currentBytePos = 0; currentBytePos != TEMP3.length; currentBytePos += blockSize) 198 { 199 engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos); 200 } 201 202 return TEMP3; 203 } 204 205 /** 206 * Method unwrap 207 * 208 * @param in byte array containing the wrapped key. 209 * @param inOff off set into in that the data starts at. 210 * @param inLen length of the data. 211 * @return the unwrapped bytes. 212 * @throws InvalidCipherTextException 213 */ 214 public byte[] unwrap(byte[] in, int inOff, int inLen) 215 throws InvalidCipherTextException 216 { 217 if (forWrapping) 218 { 219 throw new IllegalStateException("Not set for unwrapping"); 220 } 221 222 if (in == null) 223 { 224 throw new InvalidCipherTextException("Null pointer as ciphertext"); 225 } 226 227 final int blockSize = engine.getBlockSize(); 228 if (inLen % blockSize != 0) 229 { 230 throw new InvalidCipherTextException("Ciphertext not multiple of " + blockSize); 231 } 232 233 /* 234 // Check if the length of the cipher text is reasonable given the key 235 // type. It must be 40 bytes for a 168 bit key and either 32, 40, or 236 // 48 bytes for a 128, 192, or 256 bit key. If the length is not supported 237 // or inconsistent with the algorithm for which the key is intended, 238 // return error. 239 // 240 // we do not accept 168 bit keys. it has to be 192 bit. 241 int lengthA = (estimatedKeyLengthInBit / 8) + 16; 242 int lengthB = estimatedKeyLengthInBit % 8; 243 244 if ((lengthA != keyToBeUnwrapped.length) || (lengthB != 0)) { 245 throw new XMLSecurityException("empty"); 246 } 247 */ 248 249 // Decrypt the cipher text with TRIPLedeS in CBC mode using the KEK 250 // and an initialization vector (IV) of 0x4adda22c79e82105. Call the output TEMP3. 251 ParametersWithIV param2 = new ParametersWithIV(this.param, IV2); 252 253 this.engine.init(false, param2); 254 255 byte TEMP3[] = new byte[inLen]; 256 257 for (int currentBytePos = 0; currentBytePos != inLen; currentBytePos += blockSize) 258 { 259 engine.processBlock(in, inOff + currentBytePos, TEMP3, currentBytePos); 260 } 261 262 // Reverse the order of the octets in TEMP3 and call the result TEMP2. 263 byte[] TEMP2 = reverse(TEMP3); 264 265 // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets. 266 this.iv = new byte[8]; 267 268 byte[] TEMP1 = new byte[TEMP2.length - 8]; 269 270 System.arraycopy(TEMP2, 0, this.iv, 0, 8); 271 System.arraycopy(TEMP2, 8, TEMP1, 0, TEMP2.length - 8); 272 273 // Decrypt TEMP1 using TRIPLedeS in CBC mode using the KEK and the IV 274 // found in the previous step. Call the result WKCKS. 275 this.paramPlusIV = new ParametersWithIV(this.param, this.iv); 276 277 this.engine.init(false, this.paramPlusIV); 278 279 byte[] WKCKS = new byte[TEMP1.length]; 280 281 for (int currentBytePos = 0; currentBytePos != WKCKS.length; currentBytePos += blockSize) 282 { 283 engine.processBlock(TEMP1, currentBytePos, WKCKS, currentBytePos); 284 } 285 286 // Decompose WKCKS. CKS is the last 8 octets and WK, the wrapped key, are 287 // those octets before the CKS. 288 byte[] result = new byte[WKCKS.length - 8]; 289 byte[] CKStoBeVerified = new byte[8]; 290 291 System.arraycopy(WKCKS, 0, result, 0, WKCKS.length - 8); 292 System.arraycopy(WKCKS, WKCKS.length - 8, CKStoBeVerified, 0, 8); 293 294 // Calculate a CMS Key Checksum, (section 5.6.1), over the WK and compare 295 // with the CKS extracted in the above step. If they are not equal, return error. 296 if (!checkCMSKeyChecksum(result, CKStoBeVerified)) 297 { 298 throw new InvalidCipherTextException( 299 "Checksum inside ciphertext is corrupted"); 300 } 301 302 // WK is the wrapped key, now extracted for use in data decryption. 303 return result; 304 } 305 306 /** 307 * Some key wrap algorithms make use of the Key Checksum defined 308 * in CMS [CMS-Algorithms]. This is used to provide an integrity 309 * check value for the key being wrapped. The algorithm is 310 * 311 * - Compute the 20 octet SHA-1 hash on the key being wrapped. 312 * - Use the first 8 octets of this hash as the checksum value. 313 * 314 * For details see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum. 315 * 316 * @param key the key to check, 317 * @return the CMS checksum. 318 * @throws RuntimeException 319 */ 320 private byte[] calculateCMSKeyChecksum( 321 byte[] key) 322 { 323 byte[] result = new byte[8]; 324 325 sha1.update(key, 0, key.length); 326 sha1.doFinal(digest, 0); 327 328 System.arraycopy(digest, 0, result, 0, 8); 329 330 return result; 331 } 332 333 /** 334 * For details see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum 335 * 336 * @param key key to be validated. 337 * @param checksum the checksum. 338 * @return true if okay, false otherwise. 339 */ 340 private boolean checkCMSKeyChecksum( 341 byte[] key, 342 byte[] checksum) 343 { 344 return Arrays.constantTimeAreEqual(calculateCMSKeyChecksum(key), checksum); 345 } 346 347 private static byte[] reverse(byte[] bs) 348 { 349 byte[] result = new byte[bs.length]; 350 for (int i = 0; i < bs.length; i++) 351 { 352 result[i] = bs[bs.length - (i + 1)]; 353 } 354 return result; 355 } 356} 357