001: /*_############################################################################
002: _##
003: _## SNMP4J - Priv3DES.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 org.snmp4j.smi.OID;
024: import org.snmp4j.log.*;
025: import javax.crypto.spec.SecretKeySpec;
026: import javax.crypto.spec.IvParameterSpec;
027: import javax.crypto.Cipher;
028: import org.snmp4j.smi.OctetString;
029:
030: /**
031: * Privacy protocol class for Triple DES (DESEDE).
032: *
033: * This class uses DES-EDE in CBC mode to encrypt the data. The protocol
034: * is defined by the Internet Draft 'Extension to the User-Based Security
035: * Model (USM) to Support Triple-DES EDE in "Outside" CBC Mode'.
036: *
037: * @author Frank Fock, Jochen Katz
038: * @version 1.9
039: * @since 1.9
040: */
041: public class Priv3DES implements PrivacyProtocol {
042:
043: /**
044: * Unique ID of this privacy protocol.
045: */
046: public static final OID ID = new OID("1.3.6.1.6.3.10.1.2.3");
047:
048: private static final int DECRYPT_PARAMS_LENGTH = 8;
049: protected Salt salt;
050:
051: private static final LogAdapter logger = LogFactory
052: .getLogger(Priv3DES.class);
053:
054: public Priv3DES() {
055: this .salt = Salt.getInstance();
056: }
057:
058: public byte[] encrypt(byte[] unencryptedData, int offset,
059: int length, byte[] encryptionKey, long engineBoots,
060: long engineTime, DecryptParams decryptParams) {
061: int mySalt = (int) salt.getNext();
062:
063: if (encryptionKey.length < 32) {
064: logger
065: .error("Wrong Key length: need at least 32 bytes, is "
066: + encryptionKey.length + " bytes.");
067: throw new IllegalArgumentException(
068: "encryptionKey has illegal length "
069: + encryptionKey.length
070: + " (should be at least 32).");
071: }
072:
073: if ((decryptParams.array == null) || (decryptParams.length < 8)) {
074: decryptParams.array = new byte[8];
075: }
076: decryptParams.length = 8;
077: decryptParams.offset = 0;
078:
079: // put salt in decryption_params (sent as priv params)
080: if (logger.isDebugEnabled()) {
081: logger.debug("Preparing decrypt_params.");
082: }
083: for (int i = 0; i < 4; ++i) {
084: decryptParams.array[3 - i] = (byte) (0xFF & (engineBoots >> (8 * i)));
085: decryptParams.array[7 - i] = (byte) (0xFF & (mySalt >> (8 * i)));
086: }
087:
088: byte[] iv = new byte[8];
089:
090: // last eight bytes of key xored with decrypt params are used as iv
091: if (logger.isDebugEnabled()) {
092: logger.debug("Preparing iv for encryption.");
093: }
094: for (int i = 0; i < 8; ++i) {
095: iv[i] = (byte) (encryptionKey[24 + i] ^ decryptParams.array[i]);
096: }
097:
098: byte[] encryptedData = null;
099:
100: try {
101: // now do CBC encryption of the plaintext
102: Cipher alg = Cipher.getInstance("DESede/CBC/NoPadding");
103: SecretKeySpec key = new SecretKeySpec(encryptionKey, 0, 24,
104: "DESede");
105: IvParameterSpec ivSpec = new IvParameterSpec(iv);
106: alg.init(Cipher.ENCRYPT_MODE, key, ivSpec);
107:
108: // allocate space for encrypted text
109: if (length % 8 == 0) {
110: encryptedData = alg.doFinal(unencryptedData, offset,
111: length);
112: } else {
113: if (logger.isDebugEnabled()) {
114: logger.debug("Using padding.");
115: }
116:
117: encryptedData = new byte[8 * ((length / 8) + 1)];
118: byte[] tmp = new byte[8];
119:
120: int encryptedLength = alg.update(unencryptedData,
121: offset, length, encryptedData);
122: encryptedLength += alg.doFinal(tmp, 0,
123: 8 - (length % 8), encryptedData,
124: encryptedLength);
125: }
126: } catch (Exception e) {
127: logger.error(e);
128: if (logger.isDebugEnabled()) {
129: e.printStackTrace();
130: }
131: }
132:
133: if (logger.isDebugEnabled()) {
134: logger.debug("Encryption finished.");
135: }
136: return encryptedData;
137: }
138:
139: public byte[] decrypt(byte[] cryptedData, int offset, int length,
140: byte[] decryptionKey, long engineBoots, long engineTime,
141: DecryptParams decryptParams) {
142: if ((length % 8 != 0) || (length < 8)
143: || (decryptParams.length != 8)) {
144: throw new IllegalArgumentException("Length (" + length
145: + ") is not multiple of 8 or decrypt "
146: + "params has not length 8 ("
147: + decryptParams.length + ").");
148: }
149: if (decryptionKey.length < 32) {
150: logger
151: .error("Wrong Key length: need at least 32 bytes, is "
152: + decryptionKey.length + " bytes.");
153: throw new IllegalArgumentException(
154: "decryptionKey has illegal length "
155: + decryptionKey.length
156: + " (should be at least 32).");
157: }
158:
159: byte[] iv = new byte[8];
160:
161: // last eight bytes of key xored with decrypt params are used as iv
162: for (int i = 0; i < 8; ++i) {
163: iv[i] = (byte) (decryptionKey[24 + i] ^ decryptParams.array[i]);
164: }
165:
166: byte[] decryptedData = null;
167: try {
168: // now do CBC decryption of the crypted data
169: Cipher alg = Cipher.getInstance("DESede/CBC/NoPadding");
170: SecretKeySpec key = new SecretKeySpec(decryptionKey, 0, 24,
171: "DESede");
172: IvParameterSpec ivSpec = new IvParameterSpec(iv);
173: alg.init(Cipher.DECRYPT_MODE, key, ivSpec);
174: decryptedData = alg.doFinal(cryptedData, offset, length);
175: } catch (Exception e) {
176: logger.error(e);
177: if (logger.isDebugEnabled()) {
178: e.printStackTrace();
179: }
180: }
181:
182: return decryptedData;
183: }
184:
185: /**
186: * Gets the OID uniquely identifying the privacy protocol.
187: * @return
188: * an <code>OID</code> instance.
189: */
190: public OID getID() {
191: return (OID) ID.clone();
192: }
193:
194: public int getEncryptedLength(int scopedPDULength) {
195: if (scopedPDULength % 8 == 0) {
196: return scopedPDULength;
197: }
198: return 8 * ((scopedPDULength / 8) + 1);
199: }
200:
201: public int getMinKeyLength() {
202: return 32;
203: }
204:
205: public int getDecryptParamsLength() {
206: return DECRYPT_PARAMS_LENGTH;
207: }
208:
209: public int getMaxKeyLength() {
210: return getMinKeyLength();
211: }
212:
213: public byte[] extendShortKey(byte[] shortKey, OctetString password,
214: byte[] engineID, AuthenticationProtocol authProtocol) {
215: int length = shortKey.length;
216: byte[] extendedKey = new byte[getMinKeyLength()];
217: System.arraycopy(shortKey, 0, extendedKey, 0, shortKey.length);
218:
219: while (length < getMinKeyLength()) {
220: byte[] key = authProtocol.passwordToKey(new OctetString(
221: extendedKey, 0, length), engineID);
222: int copyBytes = Math.min(getMinKeyLength() - length,
223: authProtocol.getDigestLength());
224: System.arraycopy(key, 0, extendedKey, length, copyBytes);
225: length += copyBytes;
226: }
227: return extendedKey;
228: }
229:
230: }
|