View Javadoc
1   // NAME
2   //      $RCSfile: SnmpContextv3Basis.java,v $
3   // DESCRIPTION
4   //      [given below in javadoc format]
5   // DELTA
6   //      $Revision: 3.17 $
7   // CREATED
8   //      $Date: 2009/03/05 15:51:42 $
9   // COPYRIGHT
10  //      Westhawk Ltd
11  // TO DO
12  //
13  
14  /*
15   * Copyright (C) 2005 - 2006 by Westhawk Ltd
16   * <a href="www.westhawk.co.uk">www.westhawk.co.uk</a>
17   *
18   * Permission to use, copy, modify, and distribute this software
19   * for any purpose and without fee is hereby granted, provided
20   * that the above copyright notices appear in all copies and that
21   * both the copyright notice and this permission notice appear in
22   * supporting documentation.
23   * This software is provided "as is" without express or implied
24   * warranty.
25   */
26  
27  package uk.co.westhawk.snmp.stack;
28  
29  /*-
30   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
31   * SNMP Java Client
32   * ჻჻჻჻჻჻
33   * Copyright 2023 MetricsHub, Westhawk
34   * ჻჻჻჻჻჻
35   * This program is free software: you can redistribute it and/or modify
36   * it under the terms of the GNU Lesser General Public License as
37   * published by the Free Software Foundation, either version 3 of the
38   * License, or (at your option) any later version.
39   *
40   * This program is distributed in the hope that it will be useful,
41   * but WITHOUT ANY WARRANTY; without even the implied warranty of
42   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
43   * GNU General Lesser Public License for more details.
44   *
45   * You should have received a copy of the GNU General Lesser Public
46   * License along with this program.  If not, see
47   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
48   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
49   */
50  
51  import uk.co.westhawk.snmp.beans.UsmDiscoveryBean;
52  import uk.co.westhawk.snmp.event.RequestPduListener;
53  import uk.co.westhawk.snmp.pdu.DiscoveryPdu;
54  import uk.co.westhawk.snmp.util.SnmpUtilities;
55  
56  import java.io.ByteArrayInputStream;
57  import java.io.IOException;
58  import java.util.Arrays;
59  import java.util.Enumeration;
60  import java.util.Hashtable;
61  import java.util.function.BiFunction;
62  
63  /**
64   * This class contains the basis for the SNMP v3 contexts that is needed 
65   * by every PDU to send a SNMP v3 request.
66   *
67   * <p>
68   * This class will perform the v3 discovery of the SNMP engine ID and
69   * time line if necessary. This is done with the classes
70   * <code>TimeWindow</code> and <code>UsmDiscoveryBean</code>.
71   * </p>
72   *
73   * <p>
74   * Now that the stack can send traps and receive requests, 
75   * it needs to be able to act as an
76   * authoritative SNMP engine. This is done via the interface UsmAgent.
77   * The DefaultUsmAgent is not guaranteed to work; agents (or rather 
78   * authoritative engines) <em>should</em> provide a better implementation.
79   * </p>
80   *
81   * <p>
82   * This class will use the User Security Model (USM) as described in 
83   * <a href="http://www.ietf.org/rfc/rfc3414.txt">SNMP-USER-BASED-SM-MIB</a>.
84   * See also <a href="http://www.ietf.org/rfc/rfc3411.txt">RFC 3411</a>.
85   * </p>
86   *
87   * <p>
88   * It is advised to set all the properties of this class before any PDU,
89   * using this class, is sent. 
90   * All properties are being used to encode the message. Some properties are 
91   * being used to decode the Response or Report PDU. 
92   * When any of these last properties were changed in between flight there 
93   * is a possibility the decoding fails, causing a
94   * <code>DecodingException</code>. 
95   * </p>
96   * 
97   * <p>
98   * <code>destroy()</code> should be called when the context is no longer
99   * used. This is the only way the threads will be stopped and garbage
100  * collected.
101  * </p>
102  *
103  * @see SnmpContextv3Face
104  * @see SnmpContextv3Pool
105  * @see TimeWindow
106  * @see UsmAgent
107  * @see DefaultUsmAgent
108  * @see #setUsmAgent(UsmAgent)
109  * @see UsmDiscoveryBean
110  *
111  * @since 4_14
112  * @author <a href="mailto:snmp@westhawk.co.uk">Birgit Arkesteijn</a>
113  * @version $Revision: 3.17 $ $Date: 2009/03/05 15:51:42 $
114  */
115 public abstract class SnmpContextv3Basis extends AbstractSnmpContext
116         implements SnmpContextv3Face, Cloneable {
117     private static final String version_id = "@(#)$Id: SnmpContextv3Basis.java,v 3.17 2009/03/05 15:51:42 birgita Exp $ Copyright Westhawk Ltd";
118 
119     public static final int AES128_KEY_LENGTH = 16;
120     public static final int AES192_KEY_LENGTH = 24;
121     public static final int AES256_KEY_LENGTH = 32;
122 
123     protected String userName = DEFAULT_USERNAME;
124     protected boolean useAuthentication = false;
125     protected String userAuthenticationPassword;
126     protected byte[] userAuthKeyMD5 = null;
127     protected byte[] userAuthKeySHA1 = null;
128     protected byte[] userAuthKeySHA256 = null;
129     protected byte[] userAuthKeySHA512 = null;
130     protected byte[] userAuthKeySHA224 = null;
131     protected byte[] userAuthKeySHA384 = null;
132     protected int authenticationProtocol = MD5_PROTOCOL;
133     protected int privacyProtocol = DES_ENCRYPT;
134     protected boolean usePrivacy = false;
135     protected String userPrivacyPassword;
136     protected byte[] userPrivKeyMD5 = null;
137     protected byte[] userPrivKeySHA1 = null;
138     protected byte[] userPrivKeySHA256 = null;
139     protected byte[] userPrivKeySHA512 = null;
140     protected byte[] userPrivKeySHA224 = null;
141     protected byte[] userPrivKeySHA384 = null;
142     protected byte[] contextEngineId = new byte[0];
143     protected String contextName = DEFAULT_CONTEXT_NAME;
144     protected UsmAgent usmAgent = null;
145 
146     private Hashtable msgIdHash = new Hashtable(MAXPDU);
147     private static int next_id = 1;
148 
149     /**
150      * Constructor.
151      *
152      * @param host The host to which the PDU will be sent
153      * @param port The port where the SNMP server will be
154      * @see AbstractSnmpContext#AbstractSnmpContext(String, int)
155      */
156     public SnmpContextv3Basis(String host, int port) throws IOException {
157         this(host, port, null, STANDARD_SOCKET);
158     }
159 
160     /**
161      * Constructor.
162      * Parameter typeSocketA should be either STANDARD_SOCKET, TCP_SOCKET or a
163      * fully qualified classname.
164      *
165      * @param host        The host to which the Pdu will be sent
166      * @param port        The port where the SNMP server will be
167      * @param typeSocketA The local address the server will bind to
168      *
169      * @see AbstractSnmpContext#AbstractSnmpContext(String, int, String)
170      */
171     public SnmpContextv3Basis(String host, int port, String typeSocketA)
172             throws IOException {
173         this(host, port, null, typeSocketA);
174     }
175 
176     /**
177      * Constructor.
178      * Parameter typeSocketA should be either STANDARD_SOCKET, TCP_SOCKET or a
179      * fully qualified classname.
180      *
181      * @param host        The host to which the PDU will be sent
182      * @param port        The port where the SNMP server will be
183      * @param bindAddress The local address the server will bind to
184      * @param typeSocketA The type of socket to use.
185      *
186      * @see AbstractSnmpContext#AbstractSnmpContext(String, int, String)
187      * @see SnmpContextBasisFace#STANDARD_SOCKET
188      * @see SnmpContextBasisFace#TCP_SOCKET
189      * @since 4_14
190      */
191     public SnmpContextv3Basis(String host, int port, String bindAddress, String typeSocketA)
192             throws IOException {
193         super(host, port, bindAddress, typeSocketA);
194 
195         if (TimeWindow.getCurrent() == null) {
196             TimeWindow timew = new TimeWindow();
197         }
198         setUsmAgent(createUsmAgent());
199     }
200 
201     public int getVersion() {
202         return SnmpConstants.SNMP_VERSION_3;
203     }
204 
205     /**
206      * Returns the username.
207      *
208      * @return the username
209      */
210     public String getUserName() {
211         return userName;
212     }
213 
214     /**
215      * Sets the username.
216      * This username will be used for all PDUs sent with this context.
217      * The username corresponds to the 'msgUserName' in
218      * <a href="http://www.ietf.org/rfc/rfc3414.txt">SNMP-USER-BASED-SM-MIB</a>.
219      * The default value is "initial".
220      *
221      * @param newUserName The new username
222      * @see #DEFAULT_USERNAME
223      */
224     public void setUserName(String newUserName) {
225         userName = newUserName;
226     }
227 
228     /**
229      * Returns if authentication is used or not.
230      * By default no authentication will be used.
231      *
232      * @return true if authentication is used, false if not
233      */
234     public boolean isUseAuthentication() {
235         return useAuthentication;
236     }
237 
238     /**
239      * Sets whether authentication has to be used.
240      * By default no authentication will be used.
241      *
242      * @param newUseAuthentication The use of authentication
243      */
244     public void setUseAuthentication(boolean newUseAuthentication) {
245         useAuthentication = newUseAuthentication;
246     }
247 
248     /**
249      * Returns the user authentication password.
250      * This password will be transformed into the user authentication secret key.
251      *
252      * @return The user authentication password
253      */
254     public String getUserAuthenticationPassword() {
255         return userAuthenticationPassword;
256     }
257 
258     /**
259      * Sets the user authentication password.
260      * This password will be transformed into the user authentication secret
261      * key. A user MUST set this password.
262      *
263      * @param newUserAuthPassword The user authentication password
264      */
265     public void setUserAuthenticationPassword(String newUserAuthPassword) {
266         if (newUserAuthPassword != null
267                 &&
268                 newUserAuthPassword.equals(userAuthenticationPassword) == false) {
269             userAuthenticationPassword = newUserAuthPassword;
270             userAuthKeyMD5 = null;
271             userAuthKeySHA1 = null;
272             userAuthKeySHA256 = null;
273             userAuthKeySHA512 = null;
274             userAuthKeySHA224 = null;
275             userAuthKeySHA384 = null;
276         }
277     }
278 
279     /**
280      * Sets the protocol to be used for authentication.
281      * This can either be MD5 or SHA-1.
282      * By default MD5 will be used.
283      *
284      * @param protocol The authentication protocol to be used
285      * @see #MD5_PROTOCOL
286      * @see #SHA1_PROTOCOL
287      */
288     public void setAuthenticationProtocol(int protocol)
289             throws IllegalArgumentException {
290         if (AUTH_PROTOCOLS.contains(protocol)) {
291             if (protocol != authenticationProtocol) {
292                 authenticationProtocol = protocol;
293             }
294         } else {
295             throw new IllegalArgumentException("Authentication Protocol "
296                     + "should be MD5 or SHA1 or SHA256 or SHA512 or SHA224 or SHA384");
297         }
298     }
299 
300     /**
301      * Returns the protocol to be used for authentication.
302      * This can either be MD5 or SHA-1.
303      * By default MD5 will be used.
304      *
305      * @return The authentication protocol to be used
306      * @see #MD5_PROTOCOL
307      * @see #SHA1_PROTOCOL
308      */
309     public int getAuthenticationProtocol() {
310         return authenticationProtocol;
311     }
312 
313     /**
314      * Sets the protocol to be used for privacy.
315      * This can either be DES or AES.
316      * By default DES will be used.
317      *
318      * @param protocol The privacy protocol to be used
319      * @see SnmpContextv3Face#AES_ENCRYPT
320      * @see SnmpContextv3Face#DES_ENCRYPT
321      */
322     public void setPrivacyProtocol(int protocol)
323             throws IllegalArgumentException {
324         if (PRIVACY_PROTOCOLS.contains(protocol)) {
325             if (protocol != privacyProtocol) {
326                 privacyProtocol = protocol;
327             }
328         } else {
329             throw new IllegalArgumentException("Privacy Encryption "
330                     + "should be AES, AES192, AES256 or DES");
331         }
332     }
333 
334     /**
335      * Returns the protocol to be used for privacy.
336      * This can either be DES or AES.
337      * By default DES will be used.
338      *
339      * @return The privacy protocol to be used
340      * @see SnmpContextv3Face#AES_ENCRYPT
341      * @see SnmpContextv3Face#DES_ENCRYPT
342      */
343     public int getPrivacyProtocol() {
344         return privacyProtocol;
345     }
346 
347     byte[] getAuthenticationPasswordKeyMD5() {
348         if (userAuthKeyMD5 == null) {
349             userAuthKeyMD5 = SnmpUtilities.passwordToKeyMD5(userAuthenticationPassword);
350         }
351         return userAuthKeyMD5;
352     }
353 
354     byte[] getAuthenticationPasswordKeySHA1() {
355         if (userAuthKeySHA1 == null) {
356             userAuthKeySHA1 = SnmpUtilities.passwordToKeySHA1(userAuthenticationPassword);
357         }
358         return userAuthKeySHA1;
359     }
360 
361     /**
362      * Returns the authentication password key for SHA256.
363      * 
364      * @return the authentication password key for SHA256
365      */
366     byte[] getAuthenticationPasswordKeySHA256() {
367         if (userAuthKeySHA256 == null) {
368             userAuthKeySHA256 = SnmpUtilities.passwordToKeySHA256(userAuthenticationPassword);
369         }
370         return userAuthKeySHA256;
371     }
372 
373     /**
374      * Returns the authentication password key for SHA-384.
375      *
376      * @return the authentication password key for SHA-384
377      */
378     byte[] getAuthenticationPasswordKeySHA384() {
379         if (userAuthKeySHA384 == null) {
380             userAuthKeySHA384 = SnmpUtilities.passwordToKeySHA384(userAuthenticationPassword);
381         }
382         return userAuthKeySHA384;
383     }
384 
385     /**
386      * Returns the authentication password key for SHA-224.
387      *
388      * @return the authentication password key for SHA-224
389      */
390     byte[] getAuthenticationPasswordKeySHA224() {
391         if (userAuthKeySHA224 == null) {
392             userAuthKeySHA224 = SnmpUtilities.passwordToKeySHA224(userAuthenticationPassword);
393         }
394         return userAuthKeySHA224;
395     }
396 
397     /**
398      * Returns the authentication password key for SHa512.
399      *
400      * @return the authentication password key for SHA512
401      */
402     byte[] getAuthenticationPasswordKeySHA512() {
403         if (userAuthKeySHA512 == null) {
404             userAuthKeySHA512 = SnmpUtilities.passwordToKeySHA512(userAuthenticationPassword);
405         }
406         return userAuthKeySHA512;
407     }
408 
409     byte[] getPrivacyPasswordKeyMD5() {
410         if (userPrivKeyMD5 == null) {
411             userPrivKeyMD5 = SnmpUtilities.passwordToKeyMD5(userPrivacyPassword);
412         }
413         return userPrivKeyMD5;
414     }
415 
416     byte[] getPrivacyPasswordKeySHA1() {
417         if (userPrivKeySHA1 == null) {
418             userPrivKeySHA1 = SnmpUtilities.passwordToKeySHA1(userPrivacyPassword);
419         }
420         return userPrivKeySHA1;
421     }
422 
423     /**
424      * Returns the privacy password key for SHA256.
425      * 
426      * @return the privacy password key for SHA256
427      */
428     byte[] getPrivacyPasswordKeySHA256() {
429         if (userPrivKeySHA256 == null) {
430             userPrivKeySHA256 = SnmpUtilities.passwordToKeySHA256(userPrivacyPassword);
431         }
432         return userPrivKeySHA256;
433     }
434 
435     /**
436      * Returns the privacy password key for SHA-224.
437      *
438      * @return the privacy password key for SHA-224
439      */
440     byte[] getPrivacyPasswordKeySHA224() {
441         if (userPrivKeySHA224 == null) {
442             userPrivKeySHA224 = SnmpUtilities.passwordToKeySHA224(userPrivacyPassword);
443         }
444         return userPrivKeySHA224;
445     }
446 
447     /**
448      * Returns the privacy password key for SHA-384.
449      *
450      * @return the privacy password key for SHA-384
451      */
452     byte[] getPrivacyPasswordKeySHA384() {
453         if (userPrivKeySHA384 == null) {
454             userPrivKeySHA384 = SnmpUtilities.passwordToKeySHA384(userPrivacyPassword);
455         }
456         return userPrivKeySHA384;
457     }
458 
459     /**
460      * Returns the privacy password key for SHA512.
461      *
462      * @return the privacy password key for SHA512
463      */
464     byte[] getPrivacyPasswordKeySHA512() {
465         if (userPrivKeySHA512 == null) {
466             userPrivKeySHA512 = SnmpUtilities.passwordToKeySHA512(userPrivacyPassword);
467         }
468         return userPrivKeySHA512;
469     }
470 
471     /**
472      * Returns if privacy is used or not.
473      * By default privacy is not used.
474      *
475      * @return true if privacy is used, false if not
476      */
477     public boolean isUsePrivacy() {
478         return usePrivacy;
479     }
480 
481     /**
482      * Sets whether privacy has to be used.
483      * By default privacy is not used.
484      * Note, privacy (encryption) without authentication is not allowed.
485      *
486      * @param newUsePrivacy The use of privacy
487      */
488     public void setUsePrivacy(boolean newUsePrivacy) {
489         usePrivacy = newUsePrivacy;
490     }
491 
492     /**
493      * Returns the user privacy password.
494      * This password will be transformed into the user privacy secret key.
495      *
496      * @return The user privacy password
497      */
498     public String getUserPrivacyPassword() {
499         return userPrivacyPassword;
500     }
501 
502     /**
503      * Sets the user privacy password.
504      * This password will be transformed into the user privacy secret
505      * key. A user <em>must</em> set this password in order to use privacy.
506      *
507      * @param newUserPrivacyPassword The user privacy password
508      */
509     public void setUserPrivacyPassword(String newUserPrivacyPassword) {
510         if (newUserPrivacyPassword != null
511                 &&
512                 newUserPrivacyPassword.equals(userPrivacyPassword) == false) {
513             userPrivacyPassword = newUserPrivacyPassword;
514             userPrivKeyMD5 = null;
515             userPrivKeySHA1 = null;
516             userPrivKeySHA256 = null;
517             userPrivKeySHA512 = null;
518             userPrivKeySHA224 = null;
519             userPrivKeySHA384 = null;
520         }
521     }
522 
523     /**
524      * Sets the contextEngineID.
525      * See <a href="http://www.ietf.org/rfc/rfc3411.txt">RFC 3411</a>.
526      *
527      * A contextEngineID uniquely
528      * identifies an SNMP entity that may realize an instance of a context
529      * with a particular contextName.
530      * 
531      * <p>
532      * Note, when the stack is an authoritative engine, this parameter should
533      * equal the UsmAgent.getSnmpEngineId(). See the StackUsage
534      * documentation for an explanation.
535      * </p>
536      *
537      * <p>
538      * If the contextEngineID is of length zero, the encoder will use the
539      * (discovered)
540      * snmpEngineId.
541      * </p>
542      *
543      * @see UsmAgent#getSnmpEngineId()
544      * @param newContextEngineId The contextEngineID
545      */
546     public void setContextEngineId(byte[] newContextEngineId)
547             throws IllegalArgumentException {
548         if (newContextEngineId != null) {
549             contextEngineId = newContextEngineId;
550         } else {
551             throw new IllegalArgumentException("contextEngineId is null");
552         }
553     }
554 
555     /**
556      * Returns the contextEngineID.
557      *
558      * @return The contextEngineID
559      */
560     public byte[] getContextEngineId() {
561         return contextEngineId;
562     }
563 
564     /**
565      * Sets the contextName.
566      * See <a href="http://www.ietf.org/rfc/rfc3411.txt">RFC 3411</a>.
567      *
568      * A contextName is used to name a context. Each contextName MUST be
569      * unique within an SNMP entity.
570      * By default this is "" (the empty String).
571      *
572      * @param newContextName The contextName
573      * @see #DEFAULT_CONTEXT_NAME
574      */
575     public void setContextName(String newContextName) {
576         contextName = newContextName;
577     }
578 
579     /**
580      * Returns the contextName.
581      *
582      * @return The contextName
583      */
584     public String getContextName() {
585         return contextName;
586     }
587 
588     /**
589      * Adds a discovery pdu. This method adds the PDU (without checking if
590      * discovery is needed).
591      *
592      * @param pdu the discovery pdu
593      * @return pdu is succesful added
594      * @see AbstractSnmpContext#addPdu(Pdu)
595      * @see #addPdu(Pdu)
596      */
597     public boolean addDiscoveryPdu(DiscoveryPdu pdu)
598             throws IOException, PduException {
599         // since this is a DiscoveryPdu we do not check for discovery :-)
600         return this.addPdu(pdu, false);
601     }
602 
603     /**
604      * Adds a PDU. This method adds the PDU and blocks until it has all the
605      * discovery parameters it needs.
606      *
607      * @param pdu the PDU
608      * @return pdu is succesful added
609      * @see AbstractSnmpContext#addPdu(Pdu)
610      * @see #addDiscoveryPdu(DiscoveryPdu)
611      */
612     public boolean addPdu(Pdu pdu)
613             throws IOException, PduException {
614         return this.addPdu(pdu, true);
615     }
616 
617     /**
618      * Creates the USM agent.
619      * 
620      * @see DefaultUsmAgent
621      * @see #isAuthoritative
622      */
623     protected UsmAgent createUsmAgent() {
624         return new DefaultUsmAgent();
625     }
626 
627     /**
628      * Sets the UsmAgent, needed when this stack is used as authoritative
629      * SNMP engine. This interface provides authentiation details, like its
630      * clock and its Engine ID.
631      * 
632      * @see DefaultUsmAgent
633      * @param agent The USM authoritative interface
634      * @since 4_14
635      */
636     public void setUsmAgent(UsmAgent agent) {
637         usmAgent = agent;
638     }
639 
640     /**
641      * Returns the UsmAgent.
642      * 
643      * @see #setUsmAgent
644      * @since 4_14
645      */
646     public UsmAgent getUsmAgent() {
647         return usmAgent;
648     }
649 
650     /**
651      * Adds a PDU. This method adds the PDU and checks if discovery is
652      * needed depending on the parameter <code>checkDiscovery</code>.
653      * If discovery is needed this method will block until it has done so.
654      * Discovery is only needed if the stack is non authoritative.
655      *
656      * <p>
657      * This method stores the SNMPv3 msgId and PDU
658      * request id in a Hashtable.
659      * Since the encoding only happens once and every retry sends the same
660      * encoded packet, only one msgId is used.
661      * </p>
662      *
663      * @param pdu            the PDU
664      * @param checkDiscovery check if discovery is needed
665      * @return pdu is succesful added
666      * @see AbstractSnmpContext#addPdu(Pdu)
667      * @see #addDiscoveryPdu(DiscoveryPdu)
668      * @see #addPdu(Pdu)
669      */
670     protected boolean addPdu(Pdu pdu, boolean checkDiscovery)
671             throws IOException, PduException {
672         // TODO, when sending response or report, the msgId should be set!
673         Integer msgId = pdu.snmpv3MsgId;
674         if (msgId == null) {
675             msgId = new Integer(next_id++);
676         } else if (pdu.isExpectingResponse() == true) {
677             // generate a new msgId, even if this is already set. The user
678             // could be adding the same PDU more than once to the
679             // context.
680             msgId = new Integer(next_id++);
681         }
682         pdu.snmpv3MsgId = msgId;
683 
684         msgIdHash.put(msgId, new Integer(pdu.req_id));
685         if (AsnObject.debug > 6) {
686             System.out.println(getClass().getName() + ".addPdu(): msgId="
687                     + msgId.toString() + ", Pdu reqId=" + pdu.req_id);
688         }
689 
690         if (checkDiscovery == true && isAuthoritative(pdu.getMsgType()) == false) {
691             discoverIfNeeded(pdu);
692         }
693 
694         boolean added = super.addPdu(pdu);
695         return added;
696     }
697 
698     /**
699      * Removes a PDU. This removes the PDU from the AbstractSnmpContext and
700      * clears the link with the SNMPv3 msgId.
701      *
702      * @param rid the PDU request id
703      * @return whether the PDU has been successfully removed
704      * @see AbstractSnmpContext#removePdu(int)
705      */
706     public synchronized boolean removePdu(int rid) {
707         boolean removed = super.removePdu(rid);
708         if (removed) {
709             Enumeration keys = msgIdHash.keys();
710             Integer msgIdI = null;
711             boolean found = false;
712             while (keys.hasMoreElements() && found == false) {
713                 msgIdI = (Integer) keys.nextElement();
714                 Integer pduIdI = (Integer) msgIdHash.get(msgIdI);
715                 found = (pduIdI.intValue() == rid);
716             }
717             if (found) {
718                 msgIdHash.remove(msgIdI);
719             }
720         }
721         return removed;
722     }
723 
724     /**
725      * Encodes a discovery PDU packet. This methods encodes without checking
726      * if the discovery parameters are all known.
727      */
728     public byte[] encodeDiscoveryPacket(byte msg_type, int rId, int errstat,
729             int errind, Enumeration ve, Object obj)
730             throws IOException, EncodingException {
731         String engineId = "";
732         TimeWindow tWindow = TimeWindow.getCurrent();
733         if (tWindow.isSnmpEngineIdKnown(getSendToHostAddress(), hostPort) == true) {
734             engineId = tWindow.getSnmpEngineId(getSendToHostAddress(), hostPort);
735         }
736         TimeWindowNode node = new TimeWindowNode(engineId, 0, 0);
737 
738         return actualEncodePacket(msg_type, rId, errstat, errind, ve, node,
739                 obj);
740     }
741 
742     /**
743      * Encodes a PDU. This is for internal use only and should
744      * NOT be called by the developer.
745      * This is called by the the PDU itself and is added to the interface to
746      * cover the different kind of Contexts.
747      *
748      * <p>
749      * When the stack is
750      * </p>
751      * <ul>
752      * <li>
753      * authoritative, the timeline details are retrieved from the UsmAgent.
754      * </li>
755      * <li>
756      * non authoritative, this methods first checks if all the discovery
757      * parameters are known;
758      * <ul>
759      * <li>
760      * If so, it encodes and returns the bytes.
761      * </li>
762      * <li>
763      * If not, it will throw an EncodingException.
764      * </li>
765      * </ul>
766      * </li>
767      * </ul>
768      *
769      * @see #isAuthoritative(byte)
770      * @param msg_type The message type
771      * @param rId      The message id
772      * @param errstat  The error status
773      * @param errind   The error index
774      * @param ve       The varbind list
775      * @param obj      Additional object (only used in SNMPv3)
776      * @return The encoded packet
777      */
778     public byte[] encodePacket(byte msg_type, int rId, int errstat,
779             int errind, Enumeration ve, Object obj)
780             throws IOException, EncodingException {
781         TimeWindowNode node = null;
782         if (isDestroyed == true) {
783             throw new EncodingException("Context can no longer be used, since it is already destroyed");
784         } else {
785             TimeWindow tWindow = TimeWindow.getCurrent();
786             if (isAuthoritative(msg_type) == true) {
787                 usmAgent.setSnmpContext(this);
788                 if (usmAgent.getSnmpEngineId() == null) {
789                     throw new EncodingException("UsmAgent "
790                             + usmAgent.getClass().getName()
791                             + " should provide Engine ID!");
792                 }
793                 tWindow.updateTimeWindow(usmAgent.getSnmpEngineId(),
794                         usmAgent.getSnmpEngineBoots(), usmAgent.getSnmpEngineTime(),
795                         this.isUseAuthentication());
796                 node = tWindow.getTimeLine(usmAgent.getSnmpEngineId());
797             } else {
798                 if (tWindow.isSnmpEngineIdKnown(getSendToHostAddress(), hostPort) == false) {
799                     throw new EncodingException("Engine ID of host "
800                             + getSendToHostAddress()
801                             + ", port " + hostPort
802                             + " is unknown (rId="
803                             + rId + "). Perform discovery.");
804                 }
805                 String engineId = tWindow.getSnmpEngineId(getSendToHostAddress(), hostPort);
806                 node = new TimeWindowNode(engineId, 0, 0);
807 
808                 if (isUseAuthentication()) {
809                     if (tWindow.isTimeLineKnown(engineId) == true) {
810                         node = tWindow.getTimeLine(engineId);
811                     } else {
812                         throw new EncodingException("Time Line of Engine ID of host "
813                                 + getSendToHostAddress() + ", port " + hostPort + " is unknown. "
814                                 + "Perform discovery.");
815                     }
816                 }
817             }
818         }
819         return actualEncodePacket(msg_type, rId, errstat, errind, ve, node,
820                 obj);
821     }
822 
823     /**
824      * Checks the sanity of the context and returns an error message when it
825      * is not correct.
826      */
827     protected String checkContextSanity() {
828         String ret = null;
829         if (usePrivacy == true) {
830             if (userPrivacyPassword == null) {
831                 ret = "userPrivacyPassword is null, but usePrivacy is true";
832             } else if (userPrivacyPassword.length() == 0) {
833                 ret = "userPrivacyPassword is empty, but usePrivacy is true";
834             } else if (useAuthentication == false) {
835                 ret = "useAuthentication is false, but usePrivacy is true";
836             }
837         }
838 
839         if (useAuthentication == true) {
840             if (userAuthenticationPassword == null) {
841                 ret = "userAuthenticationPassword is null, but useAuthentication is true";
842             } else if (userAuthenticationPassword.length() == 0) {
843                 ret = "userAuthenticationPassword is empty, but useAuthentication is true";
844             }
845         }
846         return ret;
847     }
848 
849     /**
850      * Does the actual encoding.
851      *
852      * @see #encodeDiscoveryPacket
853      * @see #encodePacket
854      */
855     protected byte[] actualEncodePacket(byte msg_type, int rId, int errstat,
856             int errind, Enumeration ve, TimeWindowNode node, Object obj)
857             throws IOException, EncodingException {
858         AsnEncoderv3 enc = new AsnEncoderv3();
859         String msg = checkContextSanity();
860         if (msg != null) {
861             throw new EncodingException(msg);
862         }
863 
864         int msgId = ((Integer) obj).intValue();
865         if (AsnObject.debug > 6) {
866             System.out.println(getClass().getName() + ".actualEncodePacket(): msgId="
867                     + msgId + ", Pdu reqId=" + rId);
868         }
869         byte[] packet = enc.EncodeSNMPv3(this, msgId, node,
870                 msg_type, rId, errstat, errind, ve);
871 
872         return packet;
873     }
874 
875     /**
876      * Processes an incoming SNMP v3 response.
877      */
878     protected void processIncomingResponse(ByteArrayInputStream in)
879             throws DecodingException, IOException {
880         AsnDecoderv3 rpdu = new AsnDecoderv3();
881         // don't have to check for context sanity here: if the request was
882         // fine, so should be the response
883         byte[] bu = null;
884         // need to duplicate the message for V3 to rewrite
885         int nb = in.available();
886         bu = new byte[nb];
887         in.read(bu);
888         in = new ByteArrayInputStream(bu);
889 
890         AsnSequence asnTopSeq = rpdu.DecodeSNMPv3(in);
891         int msgId = rpdu.getMessageId(asnTopSeq);
892         Integer rid = (Integer) msgIdHash.get(new Integer(msgId));
893         if (rid != null) {
894             if (AsnObject.debug > 6) {
895                 System.out.println(getClass().getName() + ".processIncomingResponse(): msgId="
896                         + msgId + ", Pdu reqId=" + rid);
897             }
898             Pdu pdu = getPdu(rid);
899             try {
900                 AsnPduSequence pduSeq = rpdu.processSNMPv3(this, asnTopSeq, bu, false);
901                 if (pduSeq != null) {
902                     // got a message
903                     Integer rid2 = new Integer(pduSeq.getReqId());
904                     if (AsnObject.debug > 6) {
905                         System.out.println(getClass().getName() + ".processIncomingResponse():"
906                                 + " rid2=" + rid2);
907                     }
908 
909                     Pdu newPdu = null;
910                     if (rid2.intValue() != rid.intValue()) {
911                         newPdu = getPdu(rid2);
912                         if (AsnObject.debug > 3) {
913                             System.out.println(getClass().getName() + ".processIncomingResponse(): "
914                                     + "pduReqId of msgId (" + rid.intValue()
915                                     + ") != pduReqId of Pdu (" + rid2.intValue()
916                                     + ")");
917                         }
918                         if (newPdu == null) {
919                             if (AsnObject.debug > 3) {
920                                 System.out.println(getClass().getName() + ".processIncomingResponse(): "
921                                         + "Using pduReqId of msgId (" + rid.intValue() + ")");
922                             }
923                         }
924                     }
925 
926                     if (newPdu != null) {
927                         pdu = newPdu;
928                     }
929                 } else {
930                     if (AsnObject.debug > 6) {
931                         System.out.println(getClass().getName() + ".processIncomingResponse():"
932                                 + " pduSeq is null.");
933                     }
934                 }
935 
936                 if (pdu != null) {
937                     pdu.fillin(pduSeq);
938                 } else {
939                     if (AsnObject.debug > 6) {
940                         System.out.println(getClass().getName() + ".processIncomingResponse(): No Pdu with reqid "
941                                 + rid.intValue());
942                     }
943                 }
944             } catch (DecodingException exc) {
945                 if (pdu != null) {
946                     pdu.setErrorStatus(AsnObject.SNMP_ERR_DECODING_EXC, exc);
947                     pdu.fillin(null);
948                 } else {
949                     throw exc;
950                 }
951             }
952         } else {
953             if (AsnObject.debug > 3) {
954                 System.out.println(getClass().getName() + ".processIncomingResponse(): Pdu of msgId " + msgId
955                         + " is already answered");
956             }
957             rid = new Integer(-1);
958         }
959     }
960 
961     /**
962      * Returns if we send this PDU in authoritative role or not.
963      * The engine who sends a Response, a Trapv2 or a Report is
964      * authoritative.
965      *
966      * @since 4_14
967      * @return true if authoritative, false if not.
968      */
969     // Note: for when adding INFORM
970     // When sending an INFORM, the receiver is the authoritative engine, so
971     // the INFORM does NOT have to be added to this list!
972     protected boolean isAuthoritative(byte msg_type) {
973         return (msg_type == AsnObject.GET_RSP_MSG
974                 ||
975                 msg_type == AsnObject.TRPV2_REQ_MSG
976                 ||
977                 msg_type == AsnObject.GET_RPRT_MSG);
978     }
979 
980     void discoverIfNeeded(Pdu pdu)
981             throws IOException, PduException {
982         UsmDiscoveryBean discBean = null;
983         boolean isNeeded = false;
984 
985         TimeWindow tWindow = TimeWindow.getCurrent();
986         String engineId = tWindow.getSnmpEngineId(getSendToHostAddress(), hostPort);
987         if (engineId == null) {
988             isNeeded = true;
989             discBean = new UsmDiscoveryBean(
990                     getSendToHostAddress(), hostPort, bindAddr, typeSocket);
991             discBean.setRetryIntervals(pdu.getRetryIntervals());
992         }
993 
994         if (isUseAuthentication()) {
995             if (isNeeded) {
996                 discBean.setAuthenticationDetails(userName,
997                         userAuthenticationPassword, authenticationProtocol);
998             } else if (tWindow.isTimeLineKnown(engineId) == false) {
999                 isNeeded = true;
1000                 discBean = new UsmDiscoveryBean(
1001                         getSendToHostAddress(), hostPort, bindAddr, typeSocket);
1002                 discBean.setAuthenticationDetails(userName,
1003                         userAuthenticationPassword, authenticationProtocol);
1004                 discBean.setRetryIntervals(pdu.getRetryIntervals());
1005             }
1006 
1007             if (isNeeded && isUsePrivacy()) {
1008                 discBean.setPrivacyDetails(userPrivacyPassword, privacyProtocol);
1009             }
1010         }
1011 
1012         if (isNeeded) {
1013             discBean.startDiscovery();
1014 
1015         }
1016 
1017         // If contextEngineId is null or of length zero, set
1018         // it to the snmpEngineId.
1019         if (contextEngineId == null || contextEngineId.length == 0) {
1020             engineId = tWindow.getSnmpEngineId(getSendToHostAddress(), hostPort);
1021             setContextEngineId(SnmpUtilities.toBytes(engineId));
1022         }
1023     }
1024 
1025     /**
1026      * Adds the specified request pdu listener to receive PDUs on the
1027      * specified listening context that matches this context.
1028      * This method will call usmAgent.setSnmpContext(this).
1029      *
1030      * <p>
1031      * Don't use the TCP_SOCKET when listening for request PDUs. It doesn't
1032      * provide functionality to send a response back.
1033      * </p>
1034      *
1035      * @see AbstractSnmpContext#addRequestPduListener(RequestPduListener,
1036      *      ListeningContextPool)
1037      *
1038      * @param l        The request PDU listener
1039      * @param lcontext The listening context
1040      */
1041     public void addRequestPduListener(RequestPduListener l, ListeningContextPool lcontext)
1042             throws IOException {
1043         super.addRequestPduListener(l, lcontext);
1044 
1045         usmAgent.setSnmpContext(this);
1046         TimeWindow tWindow = TimeWindow.getCurrent();
1047         if (usmAgent.getSnmpEngineId() == null) {
1048             throw new IOException("UsmAgent "
1049                     + usmAgent.getClass().getName()
1050                     + " should provide Engine ID!");
1051         }
1052         tWindow.setSnmpEngineId(usmAgent.MYFAKEHOSTNAME, hostPort, usmAgent.getSnmpEngineId());
1053         tWindow.updateTimeWindow(usmAgent.getSnmpEngineId(),
1054                 usmAgent.getSnmpEngineBoots(), usmAgent.getSnmpEngineTime(),
1055                 this.isUseAuthentication());
1056     }
1057 
1058     /**
1059      * Copies all parameters into another SnmpContextv3.
1060      */
1061     public Object cloneParameters(SnmpContextv3Face clContext) {
1062         clContext.setUserName(new String(userName));
1063         clContext.setUseAuthentication(useAuthentication);
1064         if (userAuthenticationPassword != null) {
1065             clContext.setUserAuthenticationPassword(
1066                     new String(userAuthenticationPassword));
1067         }
1068         clContext.setAuthenticationProtocol(authenticationProtocol);
1069 
1070         clContext.setUsePrivacy(usePrivacy);
1071         if (userPrivacyPassword != null) {
1072             clContext.setUserPrivacyPassword(new String(userPrivacyPassword));
1073         }
1074         clContext.setPrivacyProtocol(privacyProtocol);
1075 
1076         clContext.setContextName(new String(contextName));
1077 
1078         int l = contextEngineId.length;
1079         byte[] newContextEngineId = new byte[l];
1080         System.arraycopy(contextEngineId, 0, newContextEngineId, 0, l);
1081         clContext.setContextEngineId(newContextEngineId);
1082 
1083         clContext.setUsmAgent(usmAgent);
1084         return clContext;
1085     }
1086 
1087     /**
1088      * Returns the hash key. This key is built out of all properties. It
1089      * serves as key for a hashtable of (v3) contexts.
1090      *
1091      * @since 4_14
1092      * @return The hash key
1093      */
1094     public String getHashKey() {
1095         StringBuffer buffer = new StringBuffer();
1096         buffer.append(hostname);
1097         buffer.append("_").append(hostPort);
1098         buffer.append("_").append(bindAddr);
1099         buffer.append("_").append(typeSocket);
1100         buffer.append("_").append(useAuthentication);
1101         buffer.append("_").append(PROTOCOL_NAMES[authenticationProtocol]);
1102         buffer.append("_").append(PROTOCOL_NAMES[privacyProtocol]);
1103         buffer.append("_").append(userAuthenticationPassword);
1104         buffer.append("_").append(userName);
1105         buffer.append("_").append(usePrivacy);
1106         buffer.append("_").append(userPrivacyPassword);
1107         buffer.append("_").append(SnmpUtilities.toHexString(contextEngineId));
1108         buffer.append("_").append(contextName);
1109         buffer.append("_v").append(getVersion());
1110 
1111         return buffer.toString();
1112     }
1113 
1114     /**
1115      * Returns a string representation of the object.
1116      * 
1117      * @return The string
1118      */
1119     public String toString() {
1120         StringBuffer buffer = new StringBuffer(getClass().getName() + "[");
1121         buffer.append("host=").append(hostname);
1122         buffer.append(", sendToHost=").append(getSendToHostAddress());
1123         buffer.append(", port=").append(hostPort);
1124         buffer.append(", bindAddress=").append(bindAddr);
1125         buffer.append(", socketType=").append(typeSocket);
1126         buffer.append(", contextEngineId=").append(SnmpUtilities.toHexString(contextEngineId));
1127         buffer.append(", contextName=").append(contextName);
1128         buffer.append(", userName=").append(userName);
1129         buffer.append(", useAuthentication=").append(useAuthentication);
1130         buffer.append(", authenticationProtocol=").append(PROTOCOL_NAMES[authenticationProtocol]);
1131         buffer.append(", userAuthenticationPassword=").append(userAuthenticationPassword);
1132         buffer.append(", usePrivacy=").append(usePrivacy);
1133         buffer.append(", privacyProtocol=").append(PROTOCOL_NAMES[privacyProtocol]);
1134         buffer.append(", userPrivacyPassword=").append(userPrivacyPassword);
1135         buffer.append(", #trapListeners=").append(trapSupport.getListenerCount());
1136         buffer.append(", #pduListeners=").append(pduSupport.getListenerCount());
1137         buffer.append("]");
1138         return buffer.toString();
1139     }
1140 
1141     /**
1142      * Generates the privacy key based on the authentication protocol.
1143      *
1144      * @param engineId               The SNMP engine ID.
1145      * @param authenticationProtocol The authentication protocol.
1146      * @param privacyProtocol        The privacyProtocol.
1147      * @return The generated privacy key.
1148      */
1149 	protected byte[] generatePrivacyKey(String engineId, int authenticationProtocol, int privacyProtocol) {
1150 		byte[] derivedPrivacyKey;
1151 		byte[] localizedPrivacyKey = null;
1152 		byte[] localizedPrivacyKeyBase;
1153 		switch (authenticationProtocol) {
1154 			case MD5_PROTOCOL:
1155 				if (privacyProtocol != AES_ENCRYPT && privacyProtocol != DES_ENCRYPT) {
1156 					throw new IllegalArgumentException(
1157 							"Unsupported privacy protocol for MD5: " + PROTOCOL_NAMES[privacyProtocol]);
1158 				}
1159 				derivedPrivacyKey = getPrivacyPasswordKeyMD5();
1160 				return SnmpUtilities.getLocalizedKeyMD5(derivedPrivacyKey, engineId);
1161 			case SHA1_PROTOCOL:
1162 				if (privacyProtocol != AES192_ENCRYPT && privacyProtocol != DES_ENCRYPT) {
1163 					throw new IllegalArgumentException(
1164 							"Unsupported privacy protocol for SHA1: " + PROTOCOL_NAMES[privacyProtocol]);
1165 				}
1166 				derivedPrivacyKey = getPrivacyPasswordKeySHA1();
1167 				return SnmpUtilities.getLocalizedKeySHA1(derivedPrivacyKey, engineId);
1168 			case SHA224_PROTOCOL: {
1169 				derivedPrivacyKey = getPrivacyPasswordKeySHA224();
1170 				localizedPrivacyKeyBase = SnmpUtilities.getLocalizedKeySHA224(derivedPrivacyKey, engineId);
1171 				switch (privacyProtocol) {
1172 				case AES_ENCRYPT:
1173 					return Arrays.copyOf(localizedPrivacyKeyBase, AES128_KEY_LENGTH);
1174 				case AES192_ENCRYPT:
1175 					return Arrays.copyOf(localizedPrivacyKeyBase, AES192_KEY_LENGTH);
1176 				case DES_ENCRYPT:
1177 					return localizedPrivacyKeyBase;
1178 				default:
1179 					throw new IllegalArgumentException(
1180 							"Unsupported privacy protocol for SHA224: " + PROTOCOL_NAMES[privacyProtocol]);
1181 				}
1182 			}
1183 			case SHA256_PROTOCOL: {
1184 				derivedPrivacyKey = getPrivacyPasswordKeySHA256();
1185 				localizedPrivacyKey = deriveKey(engineId, derivedPrivacyKey, SnmpUtilities::getLocalizedKeySHA256,
1186 						privacyProtocol);
1187 				return localizedPrivacyKey;
1188 			}
1189 			case SHA384_PROTOCOL: {
1190 				derivedPrivacyKey = getPrivacyPasswordKeySHA384();
1191 				localizedPrivacyKey = deriveKey(engineId, derivedPrivacyKey, SnmpUtilities::getLocalizedKeySHA384,
1192 						privacyProtocol);
1193 				return localizedPrivacyKey;
1194 			}
1195 			case SHA512_PROTOCOL: {
1196 				derivedPrivacyKey = getPrivacyPasswordKeySHA512();
1197 				localizedPrivacyKey = deriveKey(engineId, derivedPrivacyKey, SnmpUtilities::getLocalizedKeySHA512,
1198 						privacyProtocol);
1199 				return localizedPrivacyKey;
1200 			}
1201 			default:
1202 				throw new IllegalArgumentException("Unsupported authentication protocol: " + authenticationProtocol);
1203 			}
1204 	}
1205     
1206     /**
1207      * Derives a final privacy key
1208      *
1209      * @param engineId          The SNMP engine ID.
1210      * @param derivedPrivacyKey The SNMP engine ID.
1211      * @param localizeKey         A function that localizes the key using the derived key and engine ID.
1212      * @param privacyProtocol   The privacyProtocol
1213      * @return
1214      */
1215     private byte[] deriveKey(
1216     		String engineId,
1217     		byte[] derivedPrivacyKey,
1218     		BiFunction<byte[],
1219     		String, byte[]> localizeKey,
1220     		int privacyProtocol) {
1221         byte[] localizedPrivacyKeyBase = localizeKey.apply(derivedPrivacyKey, engineId);
1222         switch (privacyProtocol) {
1223             case AES_ENCRYPT:  return Arrays.copyOf(localizedPrivacyKeyBase, AES128_KEY_LENGTH);
1224             case AES192_ENCRYPT: return Arrays.copyOf(localizedPrivacyKeyBase, AES192_KEY_LENGTH);
1225             case AES256_ENCRYPT:  return Arrays.copyOf(localizedPrivacyKeyBase, AES256_KEY_LENGTH);
1226             case DES_ENCRYPT:
1227 				return localizedPrivacyKeyBase;
1228             default:
1229                 throw new IllegalArgumentException("Unsupported privacy protocol: " + PROTOCOL_NAMES[privacyProtocol]);
1230         }
1231      }
1232  
1233 
1234     /**
1235      * Computes the fingerprint for the given SNMP message.
1236      *
1237      * @param snmpEngineId           The SNMP engine ID.
1238      * @param authenticationProtocol The authentication protocol.
1239      * @param computedFingerprint    The computed fingerprint.
1240      * @param message                The SNMP message.
1241      * @return The computed fingerprint.
1242      */
1243     protected byte[] computeFingerprint(String snmpEngineId, int authenticationProtocol, byte[] computedFingerprint,
1244             byte[] message) {
1245         if (authenticationProtocol == MD5_PROTOCOL) {
1246             byte[] passwKey = getAuthenticationPasswordKeyMD5();
1247             byte[] authkey = SnmpUtilities.getLocalizedKeyMD5(passwKey, snmpEngineId);
1248             computedFingerprint = SnmpUtilities.getFingerPrintMD5(authkey, message);
1249         } else if (authenticationProtocol == SHA1_PROTOCOL) {
1250             byte[] passwKey = getAuthenticationPasswordKeySHA1();
1251             byte[] authkey = SnmpUtilities.getLocalizedKeySHA1(passwKey, snmpEngineId);
1252             computedFingerprint = SnmpUtilities.getFingerPrintSHA1(authkey, message);
1253         } else if (authenticationProtocol == SHA256_PROTOCOL) {
1254             byte[] passwKey = getAuthenticationPasswordKeySHA256();
1255             byte[] authkey = SnmpUtilities.getLocalizedKeySHA256(passwKey, snmpEngineId);
1256             computedFingerprint = SnmpUtilities.getFingerPrintSHA256(authkey, message);
1257         } else if (authenticationProtocol == SHA512_PROTOCOL) {
1258             byte[] passwKey = getAuthenticationPasswordKeySHA512();
1259             byte[] authkey = SnmpUtilities.getLocalizedKeySHA512(passwKey, snmpEngineId);
1260             computedFingerprint = SnmpUtilities.getFingerPrintSHA512(authkey, message);
1261         } else if (authenticationProtocol == SHA224_PROTOCOL) {
1262             byte[] passwKey = getAuthenticationPasswordKeySHA224();
1263             byte[] authkey = SnmpUtilities.getLocalizedKeySHA224(passwKey, snmpEngineId);
1264             computedFingerprint = SnmpUtilities.getFingerPrintSHA224(authkey, message);
1265         } else if (authenticationProtocol == SHA384_PROTOCOL) {
1266             byte[] passwKey = getAuthenticationPasswordKeySHA384();
1267             byte[] authkey = SnmpUtilities.getLocalizedKeySHA384(passwKey, snmpEngineId);
1268             computedFingerprint = SnmpUtilities.getFingerPrintSHA384(authkey, message);
1269         }
1270         return computedFingerprint;
1271     }
1272 
1273 }