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