Frames | No Frames |
1: /* TripleDESKeyWrap.java -- FIXME: briefly describe file purpose 2: Copyright (C) 2006 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package gnu.javax.crypto.kwa; 40: 41: import gnu.java.security.Registry; 42: import gnu.java.security.hash.Sha160; 43: import gnu.javax.crypto.assembly.Assembly; 44: import gnu.javax.crypto.assembly.Cascade; 45: import gnu.javax.crypto.assembly.Direction; 46: import gnu.javax.crypto.assembly.Stage; 47: import gnu.javax.crypto.assembly.Transformer; 48: import gnu.javax.crypto.assembly.TransformerException; 49: import gnu.javax.crypto.cipher.IBlockCipher; 50: import gnu.javax.crypto.cipher.TripleDES; 51: import gnu.javax.crypto.mode.IMode; 52: import gnu.javax.crypto.mode.ModeFactory; 53: 54: import java.security.InvalidKeyException; 55: import java.security.SecureRandom; 56: import java.util.Arrays; 57: import java.util.HashMap; 58: import java.util.Map; 59: 60: /** 61: * The GNU implementation of the Triple DES Key Wrap Algorithm as described in 62: * [1]. 63: * <p> 64: * <b>IMPORTANT</b>: This class is NOT thread safe. 65: * <p> 66: * References: 67: * <ol> 68: * <li><a href="http://www.rfc-archive.org/getrfc.php?rfc=3217">Triple-DES and 69: * RC2 Key Wrapping</a>.</li> 70: * <li><a href="http://www.w3.org/TR/xmlenc-core/">XML Encryption Syntax and 71: * Processing</a>.</li> 72: * </ol> 73: */ 74: public class TripleDESKeyWrap 75: extends BaseKeyWrappingAlgorithm 76: { 77: private static final byte[] DEFAULT_IV = new byte[] { 78: (byte) 0x4A, (byte) 0xDD, (byte) 0xA2, (byte) 0x2C, 79: (byte) 0x79, (byte) 0xE8, (byte) 0x21, (byte) 0x05 }; 80: 81: private Assembly asm; 82: private HashMap asmAttributes = new HashMap(); 83: private HashMap modeAttributes = new HashMap(); 84: private Sha160 sha = new Sha160(); 85: private SecureRandom rnd; 86: 87: public TripleDESKeyWrap() 88: { 89: super(Registry.TRIPLEDES_KWA); 90: } 91: 92: protected void engineInit(Map attributes) throws InvalidKeyException 93: { 94: rnd = (SecureRandom) attributes.get(IKeyWrappingAlgorithm.SOURCE_OF_RANDOMNESS); 95: IMode des3CBC = ModeFactory.getInstance(Registry.CBC_MODE, new TripleDES(), 8); 96: Stage des3CBCStage = Stage.getInstance(des3CBC, Direction.FORWARD); 97: Cascade cascade = new Cascade(); 98: Object modeNdx = cascade.append(des3CBCStage); 99: 100: asmAttributes.put(modeNdx, modeAttributes); 101: 102: asm = new Assembly(); 103: asm.addPreTransformer(Transformer.getCascadeTransformer(cascade)); 104: 105: modeAttributes.put(IBlockCipher.KEY_MATERIAL, 106: attributes.get(KEY_ENCRYPTION_KEY_MATERIAL)); 107: asmAttributes.put(Assembly.DIRECTION, Direction.FORWARD); 108: } 109: 110: protected byte[] engineWrap(byte[] in, int inOffset, int length) 111: { 112: // The same key wrap algorithm is used for both Two-key Triple-DES and 113: // Three-key Triple-DES keys. When a Two-key Triple-DES key is to be 114: // wrapped, a third DES key with the same value as the first DES key is 115: // created. Thus, all wrapped Triple-DES keys include three DES keys. 116: if (length != 16 && length != 24) 117: throw new IllegalArgumentException("Only 2- and 3-key Triple DES keys are alowed"); 118: 119: byte[] CEK = new byte[24]; 120: if (length == 16) 121: { 122: System.arraycopy(in, inOffset, CEK, 0, 16); 123: System.arraycopy(in, inOffset, CEK, 16, 8); 124: } 125: else 126: System.arraycopy(in, inOffset, CEK, 0, 24); 127: 128: // TODO: check for the following: 129: // However, a Two-key Triple-DES key MUST NOT be used to wrap a Three- 130: // key Triple-DES key that is comprised of three unique DES keys. 131: 132: // 1. Set odd parity for each of the DES key octets comprising the 133: // Three-Key Triple-DES key that is to be wrapped, call the result 134: // CEK. 135: TripleDES.adjustParity(CEK, 0); 136: 137: // 2. Compute an 8 octet key checksum value on CEK as described above in 138: // Section 2, call the result ICV. 139: sha.update(CEK); 140: byte[] hash = sha.digest(); 141: byte[] ICV = new byte[8]; 142: System.arraycopy(hash, 0, ICV, 0, 8); 143: 144: // 3. Let CEKICV = CEK || ICV. 145: byte[] CEKICV = new byte[CEK.length + ICV.length]; 146: System.arraycopy(CEK, 0, CEKICV, 0, CEK.length); 147: System.arraycopy(ICV, 0, CEKICV, CEK.length, ICV.length); 148: 149: // 4. Generate 8 octets at random, call the result IV. 150: byte[] IV = new byte[8]; 151: nextRandomBytes(IV); 152: 153: // 5. Encrypt CEKICV in CBC mode using the key-encryption key. Use the 154: // random value generated in the previous step as the initialization 155: // vector (IV). Call the ciphertext TEMP1. 156: modeAttributes.put(IMode.IV, IV); 157: asmAttributes.put(Assembly.DIRECTION, Direction.FORWARD); 158: byte[] TEMP1; 159: try 160: { 161: asm.init(asmAttributes); 162: TEMP1 = asm.lastUpdate(CEKICV); 163: } 164: catch (TransformerException x) 165: { 166: throw new RuntimeException(x); 167: } 168: 169: // 6. Let TEMP2 = IV || TEMP1. 170: byte[] TEMP2 = new byte[IV.length + TEMP1.length]; 171: System.arraycopy(IV, 0, TEMP2, 0, IV.length); 172: System.arraycopy(TEMP1, 0, TEMP2, IV.length, TEMP1.length); 173: 174: // 7. Reverse the order of the octets in TEMP2. That is, the most 175: // significant (first) octet is swapped with the least significant 176: // (last) octet, and so on. Call the result TEMP3. 177: byte[] TEMP3 = new byte[TEMP2.length]; 178: for (int i = 0, j = TEMP2.length - 1; i < TEMP2.length; i++, j--) 179: TEMP3[j] = TEMP2[i]; 180: 181: // 8. Encrypt TEMP3 in CBC mode using the key-encryption key. Use an 182: // initialization vector (IV) of 0x4adda22c79e82105. The ciphertext 183: // is 40 octets long. 184: modeAttributes.put(IMode.IV, DEFAULT_IV); 185: asmAttributes.put(Assembly.DIRECTION, Direction.FORWARD); 186: byte[] result; 187: try 188: { 189: asm.init(asmAttributes); 190: result = asm.lastUpdate(TEMP3); 191: } 192: catch (TransformerException x) 193: { 194: throw new RuntimeException(x); 195: } 196: return result; 197: } 198: 199: protected byte[] engineUnwrap(byte[] in, int inOffset, int length) 200: throws KeyUnwrappingException 201: { 202: // 1. If the wrapped key is not 40 octets, then error. 203: if (length != 40) 204: throw new IllegalArgumentException("length MUST be 40"); 205: 206: // 2. Decrypt the wrapped key in CBC mode using the key-encryption key. 207: // Use an initialization vector (IV) of 0x4adda22c79e82105. Call the 208: // output TEMP3. 209: modeAttributes.put(IMode.IV, DEFAULT_IV); 210: asmAttributes.put(Assembly.DIRECTION, Direction.REVERSED); 211: byte[] TEMP3; 212: try 213: { 214: asm.init(asmAttributes); 215: TEMP3 = asm.lastUpdate(in, inOffset, 40); 216: } 217: catch (TransformerException x) 218: { 219: throw new RuntimeException(x); 220: } 221: 222: // 3. Reverse the order of the octets in TEMP3. That is, the most 223: // significant (first) octet is swapped with the least significant 224: // (last) octet, and so on. Call the result TEMP2. 225: byte[] TEMP2 = new byte[40]; 226: for (int i = 0, j = 40 - 1; i < 40; i++, j--) 227: TEMP2[j] = TEMP3[i]; 228: 229: // 4. Decompose TEMP2 into IV and TEMP1. IV is the most significant 230: // (first) 8 octets, and TEMP1 is the least significant (last) 32 231: // octets. 232: byte[] IV = new byte[8]; 233: byte[] TEMP1 = new byte[32]; 234: System.arraycopy(TEMP2, 0, IV, 0, 8); 235: System.arraycopy(TEMP2, 8, TEMP1, 0, 32); 236: 237: // 5. Decrypt TEMP1 in CBC mode using the key-encryption key. Use the 238: // IV value from the previous step as the initialization vector. 239: // Call the ciphertext CEKICV. 240: modeAttributes.put(IMode.IV, IV); 241: asmAttributes.put(Assembly.DIRECTION, Direction.REVERSED); 242: byte[] CEKICV; 243: try 244: { 245: asm.init(asmAttributes); 246: CEKICV = asm.lastUpdate(TEMP1, 0, 32); 247: } 248: catch (TransformerException x) 249: { 250: throw new RuntimeException(x); 251: } 252: 253: // 6. Decompose CEKICV into CEK and ICV. CEK is the most significant 254: // (first) 24 octets, and ICV is the least significant (last) 8 255: // octets. 256: byte[] CEK = new byte[24]; 257: byte[] ICV = new byte[8]; 258: System.arraycopy(CEKICV, 0, CEK, 0, 24); 259: System.arraycopy(CEKICV, 24, ICV, 0, 8); 260: 261: // 7. Compute an 8 octet key checksum value on CEK as described above in 262: // Section 2. If the computed key checksum value does not match the 263: // decrypted key checksum value, ICV, then error. 264: sha.update(CEK); 265: byte[] hash = sha.digest(); 266: byte[] computedICV = new byte[8]; 267: System.arraycopy(hash, 0, computedICV, 0, 8); 268: if (! Arrays.equals(ICV, computedICV)) 269: throw new KeyUnwrappingException("ICV and computed ICV MUST match"); 270: 271: // 8. Check for odd parity each of the DES key octets comprising CEK. 272: // If parity is incorrect, then error. 273: if (! TripleDES.isParityAdjusted(CEK, 0)) 274: throw new KeyUnwrappingException("Triple-DES key parity MUST be adjusted"); 275: 276: // 9. Use CEK as a Triple-DES key. 277: return CEK; 278: } 279: 280: /** 281: * Fills the designated byte array with random data. 282: * 283: * @param buffer the byte array to fill with random data. 284: */ 285: private void nextRandomBytes(byte[] buffer) 286: { 287: if (rnd != null) 288: rnd.nextBytes(buffer); 289: else 290: getDefaultPRNG().nextBytes(buffer); 291: } 292: }