001: /*_############################################################################
002: _##
003: _## SNMP4J - PrivAES.java
004: _##
005: _## Copyright (C) 2003-2008 Frank Fock and Jochen Katz (SNMP4J.org)
006: _##
007: _## Licensed under the Apache License, Version 2.0 (the "License");
008: _## you may not use this file except in compliance with the License.
009: _## You may obtain a copy of the License at
010: _##
011: _## http://www.apache.org/licenses/LICENSE-2.0
012: _##
013: _## Unless required by applicable law or agreed to in writing, software
014: _## distributed under the License is distributed on an "AS IS" BASIS,
015: _## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: _## See the License for the specific language governing permissions and
017: _## limitations under the License.
018: _##
019: _##########################################################################*/
020:
021: package org.snmp4j.security;
022:
023: import javax.crypto.*;
024:
025: import org.snmp4j.log.*;
026: import javax.crypto.spec.IvParameterSpec;
027: import javax.crypto.spec.SecretKeySpec;
028: import org.snmp4j.smi.OctetString;
029:
030: /**
031: * Base class for PrivAES128, PrivAES192 and PrivAES256.
032: *
033: * This class uses AES in CFB mode to encrypt the data. The protocol
034: * is defined in draft-blumenthal-aes-usm-08.txt.
035: *
036: * @author Jochen Katz
037: * @version 1.9
038: */
039: public abstract class PrivAES implements PrivacyProtocol {
040:
041: private static final int DECRYPT_PARAMS_LENGTH = 8;
042:
043: private static final LogAdapter logger = LogFactory
044: .getLogger(PrivAES.class);
045: private int keyBytes;
046: protected Salt salt;
047:
048: /**
049: * Constructor.
050: *
051: * @param keyBytes
052: * Length of key, must be 16, 24 or 32.
053: * @throws IllegalArgumentException
054: * if keyBytes is illegal
055: */
056: public PrivAES(int keyBytes) {
057: if ((keyBytes != 16) && (keyBytes != 24) && (keyBytes != 32)) {
058: throw new IllegalArgumentException(
059: "Only 128, 192 and 256 bit AES is allowed. Requested ("
060: + (8 * keyBytes) + ").");
061: }
062: this .keyBytes = keyBytes;
063: this .salt = Salt.getInstance();
064: }
065:
066: public byte[] encrypt(byte[] unencryptedData, int offset,
067: int length, byte[] encryptionKey, long engineBoots,
068: long engineTime, DecryptParams decryptParams) {
069:
070: byte[] initVect = new byte[16];
071: long my_salt = salt.getNext();
072:
073: if (encryptionKey.length < keyBytes) {
074: throw new IllegalArgumentException("Needed key length is "
075: + keyBytes + ". Got only " + encryptionKey.length
076: + ".");
077: }
078:
079: if ((decryptParams.array == null)
080: || (decryptParams.length < DECRYPT_PARAMS_LENGTH)) {
081: decryptParams.array = new byte[DECRYPT_PARAMS_LENGTH];
082: }
083: decryptParams.length = DECRYPT_PARAMS_LENGTH;
084: decryptParams.offset = 0;
085:
086: /* Set IV as engine_boots + engine_time + salt */
087: initVect[0] = (byte) ((engineBoots >> 24) & 0xFF);
088: initVect[1] = (byte) ((engineBoots >> 16) & 0xFF);
089: initVect[2] = (byte) ((engineBoots >> 8) & 0xFF);
090: initVect[3] = (byte) ((engineBoots) & 0xFF);
091: initVect[4] = (byte) ((engineTime >> 24) & 0xFF);
092: initVect[5] = (byte) ((engineTime >> 16) & 0xFF);
093: initVect[6] = (byte) ((engineTime >> 8) & 0xFF);
094: initVect[7] = (byte) ((engineTime) & 0xFF);
095: for (int i = 56, j = 8; i >= 0; i -= 8, j++) {
096: initVect[j] = (byte) ((my_salt >> i) & 0xFF);
097: }
098: for (int i = 0; i < 8; i++) {
099: decryptParams.array[i] = initVect[i + 8];
100: }
101: if (logger.isDebugEnabled()) {
102: logger.debug("initVect is " + asHex(initVect));
103: }
104:
105: // allocate space for encrypted text
106: byte[] encryptedData = null;
107: try {
108: // now do CFB encryption of the plaintext
109: Cipher alg = Cipher.getInstance("AES/CFB/NoPadding");
110: SecretKeySpec key = new SecretKeySpec(encryptionKey, 0,
111: keyBytes, "AES");
112: IvParameterSpec ivSpec = new IvParameterSpec(initVect);
113: alg.init(Cipher.ENCRYPT_MODE, key, ivSpec);
114: encryptedData = alg
115: .doFinal(unencryptedData, offset, length);
116:
117: if (logger.isDebugEnabled()) {
118: logger.debug("aes encrypt: Data to encrypt "
119: + asHex(unencryptedData));
120:
121: logger.debug("aes encrypt: used key "
122: + asHex(encryptionKey));
123:
124: logger.debug("aes encrypt: created privacy_params "
125: + asHex(decryptParams.array));
126:
127: logger.debug("aes encrypt: encrypted Data "
128: + asHex(encryptedData));
129: }
130: } catch (Exception e) {
131: logger.error("Encrypt Exception " + e);
132: }
133:
134: return encryptedData;
135: }
136:
137: public byte[] decrypt(byte[] cryptedData, int offset, int length,
138: byte[] decryptionKey, long engineBoots, long engineTime,
139: DecryptParams decryptParams) {
140:
141: byte[] initVect = new byte[16];
142:
143: if (decryptionKey.length < keyBytes) {
144: throw new IllegalArgumentException("Needed key length is "
145: + keyBytes + ". Got only " + decryptionKey.length
146: + ".");
147: }
148:
149: /* Set IV as engine_boots + engine_time + decrypt params */
150: initVect[0] = (byte) ((engineBoots >> 24) & 0xFF);
151: initVect[1] = (byte) ((engineBoots >> 16) & 0xFF);
152: initVect[2] = (byte) ((engineBoots >> 8) & 0xFF);
153: initVect[3] = (byte) ((engineBoots) & 0xFF);
154: initVect[4] = (byte) ((engineTime >> 24) & 0xFF);
155: initVect[5] = (byte) ((engineTime >> 16) & 0xFF);
156: initVect[6] = (byte) ((engineTime >> 8) & 0xFF);
157: initVect[7] = (byte) ((engineTime) & 0xFF);
158: for (int i = 0; i < 8; i++) {
159: initVect[i + 8] = decryptParams.array[i
160: + decryptParams.offset];
161:
162: }
163: if (logger.isDebugEnabled()) {
164: logger.debug("initVect is " + asHex(initVect));
165: }
166:
167: byte[] decryptedData = null;
168: try {
169: // now do CFB decryption of the crypted data
170: Cipher alg = Cipher.getInstance("AES/CFB/NoPadding");
171: SecretKeySpec key = new SecretKeySpec(decryptionKey, 0,
172: keyBytes, "AES");
173: IvParameterSpec ivSpec = new IvParameterSpec(initVect);
174: alg.init(Cipher.DECRYPT_MODE, key, ivSpec);
175: decryptedData = alg.doFinal(cryptedData, offset, length);
176:
177: if (logger.isDebugEnabled()) {
178: logger.debug("aes decrypt: Data to decrypt "
179: + asHex(cryptedData));
180:
181: logger.debug("aes decrypt: used key "
182: + asHex(decryptionKey));
183:
184: logger.debug("aes decrypt: used privacy_params "
185: + asHex(decryptParams.array));
186:
187: logger.debug("aes decrypt: decrypted Data "
188: + asHex(decryptedData));
189: }
190: } catch (Exception e) {
191: logger.error("Decrypt Exception " + e);
192: }
193:
194: return decryptedData;
195: }
196:
197: public int getEncryptedLength(int scopedPDULength) {
198: return scopedPDULength;
199: }
200:
201: /**
202: * Turns array of bytes into string
203: *
204: * @param buf Array of bytes to convert to hex string
205: * @return Generated hex string
206: */
207: public static String asHex(byte buf[]) {
208: return new OctetString(buf).toHexString();
209: }
210:
211: public int getMinKeyLength() {
212: return keyBytes;
213: }
214:
215: public int getMaxKeyLength() {
216: return getMinKeyLength();
217: }
218:
219: public int getDecryptParamsLength() {
220: return DECRYPT_PARAMS_LENGTH;
221: }
222:
223: public byte[] extendShortKey(byte[] shortKey, OctetString password,
224: byte[] engineID, AuthenticationProtocol authProtocol) {
225: // we have to extend the key, currently only the AES draft
226: // defines this algorithm, so this may have to be changed for other
227: // privacy protocols
228: byte[] extKey = new byte[getMinKeyLength()];
229: int length = shortKey.length;
230: for (int i = 0; i < length; i++) {
231: extKey[i] = shortKey[i];
232: }
233:
234: while (length < extKey.length) {
235: byte[] hash = authProtocol.hash(extKey, 0, length);
236:
237: if (hash == null) {
238: return null;
239: }
240: int bytesToCopy = extKey.length - length;
241: if (bytesToCopy > authProtocol.getDigestLength()) {
242: bytesToCopy = authProtocol.getDigestLength();
243: }
244: for (int i = 0; i < bytesToCopy; i++) {
245: extKey[length + i] = hash[i];
246: }
247:
248: length += bytesToCopy;
249: }
250: return extKey;
251: }
252:
253: }
|