001: /*_############################################################################
002: _##
003: _## SNMP4J - SecurityProtocols.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 java.io.Serializable;
024: import org.snmp4j.smi.OID;
025: import java.io.InputStream;
026: import java.util.Properties;
027: import java.util.Enumeration;
028: import org.snmp4j.log.*;
029: import java.io.IOException;
030: import java.util.Hashtable;
031: import org.snmp4j.smi.OctetString;
032: import org.snmp4j.SNMP4JSettings;
033:
034: /**
035: * The <code>SecurityProtocols</code> class holds all authentication and
036: * privacy protocols for a SNMP entity.
037: * <p>
038: * To register security protocols other than the default, set the system
039: * property {@link #SECURITY_PROTOCOLS_PROPERTIES} to a customized version
040: * of the <code>SecurityProtocols.properties</code> file. The path has to
041: * be specified relatively to this class.
042: *
043: * @author Jochen Katz & Frank Fock
044: * @version 1.9
045: */
046: public class SecurityProtocols implements Serializable {
047:
048: private static final long serialVersionUID = 3800474900139635836L;
049:
050: private java.util.Hashtable authProtocols;
051: private java.util.Hashtable privProtocols;
052:
053: public static final String SECURITY_PROTOCOLS_PROPERTIES = "org.snmp4j.securityProtocols";
054: private static final String SECURITY_PROTOCOLS_PROPERTIES_DEFAULT = "SecurityProtocols.properties";
055: private static final LogAdapter logger = LogFactory
056: .getLogger(SecurityProtocols.class);
057:
058: private static SecurityProtocols instance = null;
059: private int maxAuthDigestLength = 0;
060: private int maxPrivDecryptParamsLength = 0;
061:
062: protected SecurityProtocols() {
063: authProtocols = new Hashtable(5);
064: privProtocols = new Hashtable(5);
065: }
066:
067: /**
068: * Get an instance of class SecurityProtocols.
069: *
070: * @return the globally used SecurityProtocols object.
071: */
072: public static SecurityProtocols getInstance() {
073: if (instance == null) {
074: instance = new SecurityProtocols();
075: }
076: return instance;
077: }
078:
079: /**
080: * Set the <code>SecurityProtocols</code>
081: * @param securityProtocols SecurityProtocols
082: */
083: public static void setSecurityProtocols(
084: SecurityProtocols securityProtocols) {
085: SecurityProtocols.instance = securityProtocols;
086: }
087:
088: /**
089: * Add the default SecurityProtocols.
090: *
091: * The names of the SecurityProtocols to add are read from a
092: * properties file.
093: *
094: * @throws InternalError if the properties file cannot be opened/read.
095: */
096: public synchronized void addDefaultProtocols() {
097: if (SNMP4JSettings.isExtensibilityEnabled()) {
098: String secProtocols = System.getProperty(
099: SECURITY_PROTOCOLS_PROPERTIES,
100: SECURITY_PROTOCOLS_PROPERTIES_DEFAULT);
101: InputStream is = SecurityProtocols.class
102: .getResourceAsStream(secProtocols);
103: if (is == null) {
104: throw new InternalError("Could not read '"
105: + secProtocols + "' from classpath!");
106: }
107: Properties props = new Properties();
108: try {
109: props.load(is);
110: for (Enumeration en = props.propertyNames(); en
111: .hasMoreElements();) {
112: String className = (String) en.nextElement();
113: try {
114: Class c = Class.forName(className);
115: Object proto = c.newInstance();
116: if (proto instanceof AuthenticationProtocol) {
117: addAuthenticationProtocol((AuthenticationProtocol) proto);
118: } else if (proto instanceof PrivacyProtocol) {
119: addPrivacyProtocol((PrivacyProtocol) proto);
120: } else {
121: logger
122: .error("Failed to register security protocol because it does "
123: + "not implement required interfaces: "
124: + className);
125: }
126: } catch (Exception cnfe) {
127: logger.error(cnfe);
128: throw new InternalError(cnfe.toString());
129: }
130: }
131: } catch (IOException iox) {
132: String txt = "Could not read '" + secProtocols + "': "
133: + iox.getMessage();
134: logger.error(txt);
135: throw new InternalError(txt);
136: } finally {
137: try {
138: is.close();
139: } catch (IOException ex) {
140: // ignore
141: logger.warn(ex);
142: }
143: }
144: } else {
145: addAuthenticationProtocol(new AuthMD5());
146: addAuthenticationProtocol(new AuthSHA());
147: addPrivacyProtocol(new PrivDES());
148: addPrivacyProtocol(new PrivAES128());
149: addPrivacyProtocol(new PrivAES192());
150: addPrivacyProtocol(new PrivAES256());
151: }
152: }
153:
154: /**
155: * Add the given {@link AuthenticationProtocol}. If an authentication protocol
156: * with the supplied ID already exists, the supplied authentication protocol
157: * will not be added and the security protocols will not be unchang.
158: *
159: * @param auth
160: * the AuthenticationProtocol to add (an existing authentication protcol
161: * with <code>auth</code>'s ID remains unchanged).
162: */
163: public synchronized void addAuthenticationProtocol(
164: AuthenticationProtocol auth) {
165: if (authProtocols.get(auth.getID()) == null) {
166: authProtocols.put(auth.getID(), auth);
167: if (auth.getDigestLength() > maxAuthDigestLength) {
168: maxAuthDigestLength = auth.getDigestLength();
169: }
170: }
171: }
172:
173: /**
174: * Get the {@link AuthenticationProtocol} with the given ID.
175: *
176: * @param id
177: * The unique ID (specified as {@link OID}) of the AuthenticationProtocol.
178: * @return
179: * the AuthenticationProtocol object if it was added before,
180: * or null if not.
181: */
182: public AuthenticationProtocol getAuthenticationProtocol(OID id) {
183: if (id == null) {
184: return null;
185: }
186: return (AuthenticationProtocol) authProtocols.get(id);
187: }
188:
189: /**
190: * Remove the given {@link AuthenticationProtocol}.
191: *
192: * @param auth The protocol to remove
193: */
194: public void removeAuthenticationProtocol(AuthenticationProtocol auth) {
195: authProtocols.remove(auth.getID());
196: }
197:
198: /**
199: * Add the given {@link PrivacyProtocol}. If a privacy protocol
200: * with the supplied ID already exists, the supplied privacy protocol
201: * will not be added and the security protocols will not be changed.
202: *
203: * @param priv
204: * the PrivacyProtocol to add (an existing privacy protcol
205: * with <code>priv</code>'s ID remains unchanged).
206: */
207: public synchronized void addPrivacyProtocol(PrivacyProtocol priv) {
208: if (privProtocols.get(priv.getID()) == null) {
209: privProtocols.put(priv.getID(), priv);
210: if (priv.getDecryptParamsLength() > maxPrivDecryptParamsLength) {
211: maxPrivDecryptParamsLength = priv
212: .getDecryptParamsLength();
213: }
214: }
215: }
216:
217: /**
218: * Get the PrivacyProtocol with the given ID.
219: *
220: * @param id
221: * The unique ID (specified as {@link OID}) of the PrivacyProtocol.
222: * @return
223: * the {@link PrivacyProtocol} object if it was added before,
224: * or null if not.
225: */
226: public PrivacyProtocol getPrivacyProtocol(OID id) {
227: if (id == null) {
228: return null;
229: }
230: return (PrivacyProtocol) privProtocols.get(id);
231: }
232:
233: /**
234: * Remove the given {@link PrivacyProtocol}.
235: *
236: * @param priv The protocol to remove
237: */
238: public void removePrivacyProtocol(PrivacyProtocol priv) {
239: privProtocols.remove(priv.getID());
240: }
241:
242: /**
243: * Generates the localized key for the given password and engine id for the
244: * authentication protocol specified by the supplied OID.
245: *
246: * @param authProtocolID
247: * an <code>OID</code> identifying the authentication protocol to
248: * use.
249: * @param passwordString
250: * the authentication pass phrase.
251: * @param engineID
252: * the engine ID of the authoritative engine.
253: * @return
254: * the localized authentication key.
255: */
256: public byte[] passwordToKey(OID authProtocolID,
257: OctetString passwordString, byte[] engineID) {
258:
259: AuthenticationProtocol protocol = (AuthenticationProtocol) authProtocols
260: .get(authProtocolID);
261: if (protocol == null) {
262: return null;
263: }
264: return protocol.passwordToKey(passwordString, engineID);
265: }
266:
267: /**
268: * Generates the localized key for the given password and engine id for the
269: * privacy protocol specified by the supplied OID.
270: *
271: * @param privProtocolID
272: * an <code>OID</code> identifying the privacy protocol the key should
273: * be created for.
274: * @param authProtocolID
275: * an <code>OID</code> identifying the authentication protocol to use.
276: * @param passwordString
277: * the authentication pass phrase.
278: * @param engineID
279: * the engine ID of the authoritative engine.
280: * @return
281: * the localized privacy key.
282: */
283: public byte[] passwordToKey(OID privProtocolID, OID authProtocolID,
284: OctetString passwordString, byte[] engineID) {
285:
286: AuthenticationProtocol authProtocol = (AuthenticationProtocol) authProtocols
287: .get(authProtocolID);
288: if (authProtocol == null) {
289: return null;
290: }
291: PrivacyProtocol privProtocol = (PrivacyProtocol) privProtocols
292: .get(privProtocolID);
293: if (privProtocol == null) {
294: return null;
295: }
296: byte[] key = authProtocol.passwordToKey(passwordString,
297: engineID);
298:
299: if (key == null) {
300: return null;
301: }
302: if (key.length >= privProtocol.getMinKeyLength()) {
303: if (key.length > privProtocol.getMaxKeyLength()) {
304: // truncate key
305: byte[] truncatedKey = new byte[privProtocol
306: .getMaxKeyLength()];
307: System.arraycopy(key, 0, truncatedKey, 0, privProtocol
308: .getMaxKeyLength());
309: return truncatedKey;
310: }
311: return key;
312: }
313: // extend key if necessary
314: byte[] extKey = privProtocol.extendShortKey(key,
315: passwordString, engineID, authProtocol);
316: return extKey;
317: }
318:
319: /**
320: * Gets the maximum authentication key length of the all known
321: * authentication protocols.
322: * @return
323: * the maximum authentication key length of all authentication protocols
324: * that have been added to this <code>SecurityProtocols</code>
325: * instance.
326: */
327: public int getMaxAuthDigestLength() {
328: return maxAuthDigestLength;
329: }
330:
331: /**
332: * Gets the maximum privacy key length of the currently known
333: * privacy protocols.
334: * @return
335: * the maximum privacy key length of all privacy protocols
336: * that have been added to this <code>SecurityProtocols</code>
337: * instance.
338: */
339: public int getMaxPrivDecryptParamsLength() {
340: return maxPrivDecryptParamsLength;
341: }
342:
343: /**
344: * Limits the supplied key value to the specified maximum length
345: * @param key
346: * the key to truncate.
347: * @param maxKeyLength
348: * the maximum length of the returned key.
349: * @return
350: * the truncated key with a length of
351: * <code>min(key.length, maxKeyLength)</code>.
352: * @since 1.9
353: */
354: public byte[] truncateKey(byte[] key, int maxKeyLength) {
355: byte[] truncatedNewKey = new byte[Math.min(maxKeyLength,
356: key.length)];
357: System.arraycopy(key, 0, truncatedNewKey, 0,
358: truncatedNewKey.length);
359: return truncatedNewKey;
360: }
361:
362: }
|