/// <summary> /// Generate privacy key from authentication password and engine id /// </summary> /// <returns>Privacy key on success or null on failure</returns> public byte[] GeneratePrivacyKey() { if (USM.Authentication == AuthenticationDigests.None) { return(null); } if (USM.Privacy == PrivacyProtocols.None) { return(null); } if (USM.PrivacySecret == null || USM.PrivacySecret.Length <= 0) { return(null); } IAuthenticationDigest authProto = SnmpSharpNet.Authentication.GetInstance(USM.Authentication); if (authProto != null) { IPrivacyProtocol privProto = SnmpSharpNet.PrivacyProtocol.GetInstance(USM.Privacy); if (privProto != null) { return(privProto.PasswordToKey(USM.PrivacySecret, USM.EngineId, authProto)); } } return(null); }
/// <summary>Generate privacy key from authentication password and engine id</summary> /// <returns>Privacy key on success or null on failure</returns> public byte[] GeneratePrivacyKey() { if (userSecurityModel.Authentication == AuthenticationDigests.None) { return(null); } if (userSecurityModel.Privacy == EPrivacyProtocols.None) { return(null); } if (userSecurityModel.PrivacySecret == null || userSecurityModel.PrivacySecret.Length <= 0) { return(null); } IAuthenticationDigest authProto = Authentication.GetInstance(userSecurityModel.Authentication); if (authProto != null) { IPrivacyProtocol privProto = PrivacyProtocol.GetInstance(userSecurityModel.Privacy); if (privProto != null) { return(privProto.PasswordToKey(userSecurityModel.PrivacySecret, userSecurityModel.EngineId, authProto)); } } return(null); }
/// <summary> /// Decode SNMP version 3 packet. This method will perform authentication check and decode privacy protected <see cref="ScopedPdu"/>. This method will /// not check for the timeliness of the packet, correct engine boot value or engine id because it does not have a reference to the engine time prior to this call. /// </summary> /// <param name="berBuffer">BER encoded SNMP version 3 packet buffer</param> /// <param name="length">Buffer length</param> public override int Decode(byte[] berBuffer, int length) { byte[] pkey = null; byte[] akey = null; if (MsgFlags.Authentication && USM.EngineId.Length > 0) { IAuthenticationDigest auth = Authentication.GetInstance(USM.Authentication); if (auth == null) { throw new SnmpException(SnmpException.UnsupportedNoAuthPriv, "Invalid authentication protocol."); } akey = auth.PasswordToKey(USM.AuthenticationSecret, USM.EngineId); if (MsgFlags.Privacy && USM.EngineId.Length > 0) { IPrivacyProtocol privacyProtocol = PrivacyProtocol.GetInstance(USM.Privacy); if (privacyProtocol == null) { throw new SnmpException(SnmpException.UnsupportedPrivacyProtocol, "Specified privacy protocol is not supported."); } pkey = privacyProtocol.PasswordToKey(USM.PrivacySecret, USM.EngineId, auth); } } return(Decode(berBuffer, length, akey, pkey)); }
/// <summary> /// Authenticate SNMP version 3 message. /// /// Before calling this member, entire SNMP version 3 packet needs to be encoded. After authentication /// process is completed, authenticationParameters value in the USM header is updated and SNMPv3 packet /// needs to be re-encoded to include it in the BER encoded stream prior to transmission. /// </summary> /// <param name="authKey">Authentication key (not password)</param> /// <param name="wholePacket">SNMP version 3 BER encoded packet.</param> public void Authenticate(byte[] authKey, ref MutableByte wholePacket) { IAuthenticationDigest authProto = SnmpSharpNet.Authentication.GetInstance(_authentication); byte[] authParam = authProto.authenticate(authKey, wholePacket); _authenticationParameters = new OctetString(authParam); }
/// <summary> /// Build cached authentication and privacy encryption keys if they are appropriate for the selected security mode. /// </summary> /// <remarks> /// This method should be called after discovery process has been completed and all security related values /// have been set. For noAuthNoPriv, none of the keys are generated. authNoPriv will result in authentication /// key cached. authPriv will generate authentication and privacy keys. /// /// For successful key caching you need to set both relevant protocols and secret values. /// </remarks> public void BuildCachedSecurityKeys() { _authenticationKey = _privacyKey = null; if (_engineId == null || _engineId.Length <= 0) { return; } if (_authenticationSecret == null || _authenticationSecret.Length <= 0) { return; } if (_authenticationProtocol != AuthenticationDigests.None) { IAuthenticationDigest authProto = SnmpSharpNet.Authentication.GetInstance(_authenticationProtocol); if (authProto != null) { _authenticationKey = authProto.PasswordToKey(_authenticationSecret, _engineId); if (_privacyProtocol != PrivacyProtocols.None && _privacySecret != null && _privacySecret.Length > 0) { IPrivacyProtocol privProto = SnmpSharpNet.PrivacyProtocol.GetInstance(_privacyProtocol); if (privProto != null) { _privacyKey = privProto.PasswordToKey(_privacySecret, _engineId, authProto); } } } } }
/// <summary> /// Some protocols support a method to extend the encryption or decryption key when supplied key /// is too short. /// </summary> /// <param name="shortKey">Key that needs to be extended</param> /// <param name="password">Privacy password as configured on the SNMP agent.</param> /// <param name="engineID">Authoritative engine id. Value is retrieved as part of SNMP v3 discovery procedure</param> /// <param name="authProtocol">Authentication protocol class instance cast as <see cref="IAuthenticationDigest" /></param> /// <returns>Extended key value</returns> public byte[] ExtendShortKey(byte[] shortKey, byte[] password, byte[] engineID, IAuthenticationDigest authProtocol) { var extKey = new byte[MinimumKeyLength]; var lastKeyBuf = new byte[shortKey.Length]; Array.Copy(shortKey, lastKeyBuf, shortKey.Length); var keyLen = shortKey.Length > MinimumKeyLength ? MinimumKeyLength : shortKey.Length; Array.Copy(shortKey, extKey, keyLen); while (keyLen < MinimumKeyLength) { var tmpBuf = authProtocol.PasswordToKey(lastKeyBuf, engineID); if (tmpBuf == null) { return(null); } if (tmpBuf.Length <= MinimumKeyLength - keyLen) { Array.Copy(tmpBuf, 0, extKey, keyLen, tmpBuf.Length); keyLen += tmpBuf.Length; } else { Array.Copy(tmpBuf, 0, extKey, keyLen, MinimumKeyLength - keyLen); keyLen += MinimumKeyLength - keyLen; } lastKeyBuf = new byte[tmpBuf.Length]; Array.Copy(tmpBuf, lastKeyBuf, tmpBuf.Length); } return(extKey); }
/// <summary>Build cached authentication and privacy encryption keys if they are appropriate for the selected security mode.</summary> /// <remarks> /// This method should be called after discovery process has been completed and all security related values /// have been set. For noAuthNoPriv, none of the keys are generated. authNoPriv will result in authentication /// key cached. authPriv will generate authentication and privacy keys. /// /// For successful key caching you need to set both relevant protocols and secret values. /// </remarks> public void BuildCachedSecurityKeys() { authenticationKey = privacyKey = null; if (engineId == null || engineId.Length <= 0) { return; } if (authenticationSecret == null || authenticationSecret.Length <= 0) { return; } if (authenticationProtocol != AuthenticationDigests.None) { IAuthenticationDigest authProto = Security.Authentication.GetInstance(authenticationProtocol); if (authProto != null) { authenticationKey = authProto.PasswordToKey(authenticationSecret, engineId); if (privacyProtocol != EPrivacyProtocols.None && privacySecret != null && privacySecret.Length > 0) { IPrivacyProtocol privProto = PrivacyProtocol.GetInstance(privacyProtocol); if (privProto != null) { privacyKey = privProto.PasswordToKey(privacySecret, engineId, authProto); } } } } }
/// <summary>Encode SNMP version 3 packet</summary> /// <remarks> /// Before encoding the packet into a byte array you need to ensure all required information is /// set. Examples of required information is request type, Vbs (Oid + values pairs), USM settings including /// SecretName, authentication method and secret (if needed), privacy method and secret (if needed), etc. /// </remarks> /// <returns>Byte array BER encoded SNMP packet.</returns> public override byte[] Encode() { byte[] pkey = null; byte[] akey = null; if (messageFlags.Authentication && userSecurityModel.EngineId.Length > 0) { IAuthenticationDigest auth = Authentication.GetInstance(userSecurityModel.Authentication); if (auth == null) { throw new SnmpException(SnmpException.EErrorCode.UnsupportedNoAuthPriv, "Invalid authentication protocol."); } akey = auth.PasswordToKey(userSecurityModel.AuthenticationSecret, userSecurityModel.EngineId); if (messageFlags.Privacy && userSecurityModel.EngineId.Length > 0) { IPrivacyProtocol privacyProtocol = PrivacyProtocol.GetInstance(userSecurityModel.Privacy); if (privacyProtocol == null) { throw new SnmpException(SnmpException.EErrorCode.UnsupportedPrivacyProtocol, "Specified privacy protocol is not supported."); } pkey = privacyProtocol.PasswordToKey(userSecurityModel.PrivacySecret, userSecurityModel.EngineId, auth); } } return(Encode(akey, pkey)); }
/// <summary> /// Authenticate SNMP version 3 message. /// /// Before calling this member, entire SNMP version 3 packet needs to be encoded. After authentication /// process is completed, authenticationParameters value in the USM header is updated and SNMPv3 packet /// needs to be re-encoded to include it in the BER encoded stream prior to transmission. /// </summary> /// <param name="wholePacket">SNMP version 3 BER encoded packet.</param> public void Authenticate(ref MutableByte wholePacket) { if (_authentication != AuthenticationDigests.None) { IAuthenticationDigest authProto = SnmpSharpNet.Authentication.GetInstance(_authentication); byte[] authParam = authProto.authenticate(AuthenticationSecret, EngineId.ToArray(), wholePacket); _authenticationParameters = new OctetString(authParam); } }
/// <summary> /// Encrypt ScopedPdu using DES encryption protocol /// </summary> /// <param name="unencryptedData">Unencrypted ScopedPdu byte array</param> /// <param name="offset">Offset to start encryption</param> /// <param name="length">Length of data to encrypt</param> /// <param name="key">Encryption key. Key has to be at least 32 bytes is length</param> /// <param name="engineBoots">Authoritative engine boots value</param> /// <param name="engineTime">Authoritative engine time value. Not used for DES</param> /// <param name="privacyParameters"> /// Privacy parameters out buffer. This field will be filled in with information /// required to decrypt the information. Output length of this field is 8 bytes and space has to be reserved /// in the USM header to store this information /// </param> /// <param name="authDigest">Authentication digest class reference. Not used by DES and can be null.</param> /// <returns>Encrypted byte array</returns> /// <exception cref="ArgumentOutOfRangeException"> /// Thrown when encryption key is null or length of the encryption key is too /// short. /// </exception> public byte[] Encrypt(byte[] unencryptedData, int offset, int length, byte[] key, int engineBoots, int engineTime, out byte[] privacyParameters, IAuthenticationDigest authDigest) { if (key == null || key.Length < MinimumKeyLength) { throw new ArgumentOutOfRangeException("encryptionKey", "Encryption key length has to 32 bytes or more."); } privacyParameters = GetSalt(engineBoots); var iv = GetIV(key, privacyParameters); // DES uses 8 byte keys but we need 16 to encrypt ScopedPdu. Get first 8 bytes and use them as encryption key var outKey = GetKey(key); var div = (int)Math.Floor(length / 8.0); if (length % 8 != 0) { div += 1; } var newLength = div * 8; var result = new byte[newLength]; var buffer = new byte[newLength]; var inbuffer = new byte[8]; var cipherText = iv; var posIn = 0; var posResult = 0; Buffer.BlockCopy(unencryptedData, offset, buffer, 0, length); DES des = new DESCryptoServiceProvider(); des.Mode = CipherMode.ECB; des.Padding = PaddingMode.None; var transform = des.CreateEncryptor(outKey, null); for (var b = 0; b < div; b++) { for (var i = 0; i < 8; i++) { inbuffer[i] = (byte)(buffer[posIn] ^ cipherText[i]); posIn++; } transform.TransformBlock(inbuffer, 0, inbuffer.Length, cipherText, 0); Buffer.BlockCopy(cipherText, 0, result, posResult, cipherText.Length); posResult += cipherText.Length; } des.Clear(); return(result); }
/// <summary> /// Convert privacy password into encryption key using packet authentication hash. /// </summary> /// <param name="secret">Privacy user secret</param> /// <param name="engineId">Authoritative engine id of the snmp agent</param> /// <param name="authProtocol">Authentication protocol</param> /// <returns>Encryption key</returns> /// <exception cref="SnmpPrivacyException">Thrown when key size is shorter then MinimumKeyLength</exception> public byte[] PasswordToKey(byte[] secret, byte[] engineId, IAuthenticationDigest authProtocol) { // RFC 3414 - password length is minimum of 8 bytes long if (secret == null || secret.Length < 8) { throw new SnmpPrivacyException("Invalid privacy secret length."); } byte[] encryptionKey = authProtocol.PasswordToKey(secret, engineId); return(encryptionKey); }
/// <summary> /// Authenticate incoming packet /// </summary> /// <param name="authKey">Authentication key (not password)</param> /// <param name="wholePacket">Received BER encoded SNMP version 3 packet</param> /// <returns>True if packet is successfully authenticated, otherwise false.</returns> public bool IsAuthentic(byte[] authKey, MutableByte wholePacket) { if (_authentication != AuthenticationDigests.None) { IAuthenticationDigest authProto = SnmpSharpNet.Authentication.GetInstance(_authentication); if (authProto != null) { return(authProto.authenticateIncomingMsg(authKey, _authenticationParameters, wholePacket)); } } return(false); // Nothing to authenticate }
/// <summary> /// Convert privacy password into encryption key using packet authentication hash. /// </summary> /// <param name="secret">Privacy user secret</param> /// <param name="engineId">Authoritative engine id of the snmp agent</param> /// <param name="authProtocol">Authentication protocol</param> /// <returns>Encryption key</returns> /// <exception cref="SnmpPrivacyException">Thrown when key size is shorter then MinimumKeyLength</exception> public byte[] PasswordToKey(byte[] secret, byte[] engineId, IAuthenticationDigest authProtocol) { if (secret == null || secret.Length < 8) { throw new SnmpPrivacyException("Invalid privacy secret length."); } byte[] key = authProtocol.PasswordToKey(secret, engineId); if (key.Length < MinimumKeyLength) { key = ExtendShortKey(key, secret, engineId, authProtocol); } return(key); }
/// <summary>Authenticate incoming packet</summary> /// <param name="wholePacket">Received BER encoded SNMP version 3 packet</param> /// <returns>True if packet is successfully authenticated, otherwise false.</returns> public bool IsAuthentic(MutableByte wholePacket) { if (authentication != AuthenticationDigests.None) { IAuthenticationDigest authProto = Security.Authentication.GetInstance(authentication); if (authProto != null) { return(authProto.AuthenticateIncomingMessage(AuthenticationSecret, engineId, authenticationParameters, wholePacket)); } } return(false); // Nothing to authenticate }
/// <summary> /// Generate authentication key from authentication password and engine id /// </summary> /// <returns>Authentication key on success or null on failure</returns> public byte[] GenerateAuthenticationKey() { if (_userSecurityModel.EngineId == null || _userSecurityModel.EngineId.Length <= 0) return null; if (_userSecurityModel.AuthenticationSecret == null || _userSecurityModel.AuthenticationSecret.Length <= 0) return null; if (_userSecurityModel.Authentication != AuthenticationDigests.None) { IAuthenticationDigest authProto = Authentication.GetInstance(_userSecurityModel.Authentication); if (authProto != null) { return authProto.PasswordToKey(_userSecurityModel.AuthenticationSecret, _userSecurityModel.EngineId); } } return null; }
/// <summary> /// Extends the encryption key if key size returned by PasswordToKey is less then minimum /// required by the encryption protocol. /// </summary> /// <remarks> /// There is no need to call this method in a user application becuase PasswordToKey() method will /// make the call if password it generates is too short. /// </remarks> /// <param name="shortKey">Encryption key</param> /// <param name="password">Privacy password</param> /// <param name="engineID">Authoritative engine id</param> /// <param name="authProtocol">Authentication protocol class instance</param> /// <returns>unaltered shortKey value</returns> public byte[] ExtendShortKey(byte[] shortKey, byte[] password, byte[] engineID, IAuthenticationDigest authProtocol) { var length = shortKey.Length; var extendedKey = new byte[MinimumKeyLength]; Buffer.BlockCopy(shortKey, 0, extendedKey, 0, shortKey.Length); while (length < MinimumKeyLength) { var key = authProtocol.PasswordToKey(shortKey, engineID); var copyBytes = Math.Min(MaximumKeyLength - length, authProtocol.DigestLength); Buffer.BlockCopy(key, 0, extendedKey, length, copyBytes); length += copyBytes; } return(extendedKey); }
/// <summary> /// Generate authentication key from authentication password and engine id /// </summary> /// <returns>Authentication key on success or null on failure</returns> public byte[] GenerateAuthenticationKey() { if (USM.EngineId == null || USM.EngineId.Length <= 0) { return(null); } if (USM.AuthenticationSecret == null || USM.AuthenticationSecret.Length <= 0) { return(null); } if (USM.Authentication != AuthenticationDigests.None) { IAuthenticationDigest authProto = SnmpSharpNet.Authentication.GetInstance(USM.Authentication); if (authProto != null) { return(authProto.PasswordToKey(USM.AuthenticationSecret, USM.EngineId)); } } return(null); }
/// <summary> /// Encrypt ScopedPdu using TripleDES encryption protocol /// </summary> /// <param name="unencryptedData">Unencrypted ScopedPdu byte array</param> /// <param name="offset">Offset to start encryption</param> /// <param name="length">Length of data to encrypt</param> /// <param name="key">Encryption key. Key has to be at least 32 bytes is length</param> /// <param name="engineBoots">Authoritative engine boots value</param> /// <param name="engineTime">Authoritative engine time value.</param> /// <param name="privacyParameters"> /// Privacy parameters out buffer. This field will be filled in with information /// required to decrypt the information. Output length of this field is 8 bytes and space has to be reserved /// in the USM header to store this information /// </param> /// <param name="authDigest">Authentication digest class reference. Used by TripleDES.</param> /// <returns>Encrypted byte array</returns> /// <exception cref="ArgumentOutOfRangeException"> /// Thrown when encryption key is null or length of the encryption key is too /// short. /// </exception> public byte[] Encrypt(byte[] unencryptedData, int offset, int length, byte[] key, int engineBoots, int engineTime, out byte[] privacyParameters, IAuthenticationDigest authDigest) { privacyParameters = GetSalt(engineBoots); var privParamHash = authDigest.ComputeHash(privacyParameters, 0, privacyParameters.Length); privacyParameters = new byte[8]; Buffer.BlockCopy(privParamHash, 0, privacyParameters, 0, 8); var iv = GetIV(key, privacyParameters); byte[] encryptedData = null; try { TripleDES tdes = new TripleDESCryptoServiceProvider(); tdes.Mode = CipherMode.CBC; tdes.Padding = PaddingMode.None; // normalize key - generated key is 32 bytes long, we need 24 bytes to encrypt var normKey = new byte[24]; Buffer.BlockCopy(key, 0, normKey, 0, normKey.Length); var transform = tdes.CreateEncryptor(normKey, iv); if (length % 8 == 0) { encryptedData = transform.TransformFinalBlock(unencryptedData, offset, length); } else { var tmpbuffer = new byte[8 * (length / 8 + 1)]; Buffer.BlockCopy(unencryptedData, offset, tmpbuffer, 0, length); encryptedData = transform.TransformFinalBlock(tmpbuffer, 0, tmpbuffer.Length); } } catch (Exception ex) { throw new SnmpPrivacyException(ex, "Exception was thrown while TripleDES privacy protocol was encrypting data\r\n" + ex); } return(encryptedData); }
/// <summary> /// Convert privacy password into encryption key using packet authentication hash. /// </summary> /// <param name="secret">Privacy user secret/password</param> /// <param name="engineId">Authoritative engine id of the SNMP agent</param> /// <param name="authProtocol">Authentication protocol</param> /// <returns>Encryption key</returns> /// <exception cref="SnmpPrivacyException">Thrown when user secret/password is shorter then MinimumKeyLength</exception> public byte[] PasswordToKey(byte[] secret, byte[] engineId, IAuthenticationDigest authProtocol) { // RFC 3414 - password length is minimum of 8 bytes long if (secret == null || secret.Length < 8) throw new SnmpPrivacyException("Invalid privacy secret length."); byte[] encryptionKey = authProtocol.PasswordToKey(secret, engineId); if( encryptionKey.Length < MinimumKeyLength ) encryptionKey = ExtendShortKey(encryptionKey, secret, engineId, authProtocol); return encryptionKey; }
/// <summary> /// Extends the encryption key if key size returned by PasswordToKey is less then minimum /// required by the encryption protocol. /// </summary> /// <remarks> /// There is no need to call this method in a user application becuase PasswordToKey() method will /// make the call if password it generates is too short. /// </remarks> /// <param name="shortKey">Encryption key</param> /// <param name="password">Privacy password</param> /// <param name="engineID">Authoritative engine id</param> /// <param name="authProtocol">Authentication protocol class instance</param> /// <returns>unaltered shortKey value</returns> public byte[] ExtendShortKey(byte[] shortKey, byte[] password, byte[] engineID, IAuthenticationDigest authProtocol) { int length = shortKey.Length; byte[] extendedKey = new byte[MinimumKeyLength]; Buffer.BlockCopy(shortKey, 0, extendedKey, 0, shortKey.Length); while (length < MinimumKeyLength) { byte[] key = authProtocol.PasswordToKey(shortKey, engineID); int copyBytes = Math.Min(MaximumKeyLength - length, authProtocol.DigestLength); Buffer.BlockCopy(key, 0, extendedKey, length, copyBytes); length += copyBytes; } return extendedKey; }
/// <summary> /// Encrypt ScopedPdu using TripleDES encryption protocol /// </summary> /// <param name="unencryptedData">Unencrypted ScopedPdu byte array</param> /// <param name="offset">Offset to start encryption</param> /// <param name="length">Length of data to encrypt</param> /// <param name="key">Encryption key. Key has to be at least 32 bytes is length</param> /// <param name="engineBoots">Authoritative engine boots value</param> /// <param name="engineTime">Authoritative engine time value.</param> /// <param name="privacyParameters">Privacy parameters out buffer. This field will be filled in with information /// required to decrypt the information. Output length of this field is 8 bytes and space has to be reserved /// in the USM header to store this information</param> /// <param name="authDigest">Authentication digest class reference. Used by TripleDES.</param> /// <returns>Encrypted byte array</returns> /// <exception cref="ArgumentOutOfRangeException">Thrown when encryption key is null or length of the encryption key is too short.</exception> public byte[] Encrypt(byte[] unencryptedData, int offset, int length, byte[] key, int engineBoots, int engineTime, out byte[] privacyParameters, IAuthenticationDigest authDigest) { privacyParameters = GetSalt(engineBoots); byte[] privParamHash = authDigest.ComputeHash(privacyParameters, 0, privacyParameters.Length); privacyParameters = new byte[8]; Buffer.BlockCopy(privParamHash, 0, privacyParameters, 0, 8); byte[] iv = GetIV(key, privacyParameters); byte[] encryptedData = null; try { TripleDES tdes = new TripleDESCryptoServiceProvider(); tdes.Mode = CipherMode.CBC; tdes.Padding = PaddingMode.None; // normalize key - generated key is 32 bytes long, we need 24 bytes to encrypt byte[] normKey = new byte[24]; Buffer.BlockCopy(key, 0, normKey, 0, normKey.Length); ICryptoTransform transform = tdes.CreateEncryptor(normKey, iv); if ((length % 8) == 0) { encryptedData = transform.TransformFinalBlock(unencryptedData, offset, length); } else { byte[] tmpbuffer = new byte[8 * ((length / 8) + 1)]; Buffer.BlockCopy(unencryptedData, offset, tmpbuffer, 0, length); encryptedData = transform.TransformFinalBlock(tmpbuffer, 0, tmpbuffer.Length); } } catch (Exception ex) { throw new SnmpPrivacyException(ex, "Exception was thrown while TripleDES privacy protocol was encrypting data\r\n" + ex.ToString()); } return encryptedData; }
/// <summary> /// Convert privacy password into encryption key using packet authentication hash. /// </summary> /// <param name="secret">Privacy user secret</param> /// <param name="engineId">Authoritative engine id of the snmp agent</param> /// <param name="authProtocol">Authentication protocol</param> /// <returns>Encryption key</returns> /// <exception cref="SnmpPrivacyException">Thrown when key size is shorter then MinimumKeyLength</exception> public byte[] PasswordToKey(byte[] secret, byte[] engineId, IAuthenticationDigest authProtocol) { if (secret == null || secret.Length < 8) throw new SnmpPrivacyException("Invalid privacy secret length."); byte[] key = authProtocol.PasswordToKey(secret, engineId); if (key.Length < MinimumKeyLength) { key = ExtendShortKey(key, secret, engineId, authProtocol); } return key; }
/// <summary> /// Decode SNMP version 3 packet. This method will perform authentication check and decode privacy protected <see cref="ScopedPdu"/>. This method will /// not check for the timeliness of the packet, correct engine boot value or engine id because it does not have a reference to the engine time prior to this call. /// </summary> /// <param name="berBuffer">BER encoded SNMP version 3 packet buffer</param> /// <param name="length">Buffer length</param> public override int decode(byte[] berBuffer, int length) { MutableByte buffer = new MutableByte(berBuffer, length); int offset = 0; // let base class parse first sequence and SNMP version number offset = base.decode(buffer, length); // check for correct SNMP protocol version if (_protocolVersion != (int)SnmpVersion.Ver3) { throw new SnmpInvalidVersionException("Expecting SNMP version 3."); } // now grab the global message data sequence header information int len = 0; byte asnType = AsnType.ParseHeader(buffer, ref offset, out len); // check that packet size can accommodate the length specified in the header if (len > (buffer.Length - offset)) { throw new OverflowException("Packet is too small to contain the data described in the header."); } // retrieve message id offset = _messageId.decode(buffer, offset); // max message size offset = _maxMessageSize.decode(buffer, offset); // message flags offset = _msgFlags.decode(buffer, offset); // verify that a valid authentication/privacy configuration is present in the packet if (_msgFlags.Authentication == false && _msgFlags.Privacy == true) { throw new SnmpException(SnmpException.UnsupportedNoAuthPriv, "SNMP version 3 noAuthPriv security combination is not supported."); } // security model code offset = _securityModel.decode(buffer, offset); // we only support USM. code = 0x03 if (_securityModel.Value != _userSecurityModel.Type) { throw new SnmpException(SnmpException.UnsupportedSecurityModel, "Class only support SNMP Version 3 User Security Model."); } // parse user security model offset = _userSecurityModel.decode(buffer, offset); // Authenticate message if authentication flag is set and packet is not a discovery packet if (_msgFlags.Authentication && _userSecurityModel.EngineId.Length > 0) { // Authenticate packet if (_userSecurityModel.AuthenticationParameters.Length != 12) { throw new SnmpAuthenticationException("Invalid authentication parameter field length."); } if (!_userSecurityModel.IsAuthentic(buffer)) { throw new SnmpAuthenticationException("Authentication of the incoming packet failed."); } } // Decode ScopedPdu if it is privacy protected and packet is not a discovery packet if (_msgFlags.Privacy && _userSecurityModel.EngineId.Length > 0) { IPrivacyProtocol privacyProtocol = PrivacyProtocol.GetInstance(_userSecurityModel.Privacy); if (privacyProtocol == null) { throw new SnmpException(SnmpException.UnsupportedPrivacyProtocol, "Privacy protocol requested is not supported."); } if (_userSecurityModel.PrivacyParameters.Length != privacyProtocol.PrivacyParametersLength) { throw new SnmpException(SnmpException.InvalidPrivacyParameterLength, "Invalid privacy parameters field length."); } // build privacy key IAuthenticationDigest auth = Authentication.GetInstance(_userSecurityModel.Authentication); if (auth == null) { throw new SnmpException(SnmpException.UnsupportedNoAuthPriv, "Invalid authentication protocol."); } byte[] pkey = auth.PasswordToKey(_userSecurityModel.PrivacySecret, _userSecurityModel.EngineId); // extend key if it is too short if (pkey.Length < privacyProtocol.MinimumKeyLength) { pkey = privacyProtocol.ExtendShortKey(pkey, new OctetString(_userSecurityModel.PrivacySecret), _userSecurityModel.EngineId, auth); } // Initialize a temporary OctetString class to hold encrypted ScopedPdu OctetString encryptedScopedPdu = new OctetString(); offset = encryptedScopedPdu.decode(buffer, offset); // decode encrypted packet byte[] decryptedScopedPdu = privacyProtocol.Decrypt(encryptedScopedPdu, 0, encryptedScopedPdu.Length, pkey, _userSecurityModel.EngineBoots, _userSecurityModel.EngineTime, _userSecurityModel.PrivacyParameters); int tempOffset = 0; offset = _scopedPdu.decode(decryptedScopedPdu, tempOffset); } else { offset = _scopedPdu.decode(buffer, offset); } return(offset); }
/// <summary> /// Encode SNMP version 3 packet /// </summary> /// <remarks> /// Before encoding the packet into a byte array you need to ensure all required information is /// set. Examples of required information is request type, Vbs (Oid + values pairs), USM settings including /// SecretName, authentication method and secret (if needed), privacy method and secret (if needed), etc. /// </remarks> /// <returns>Byte array BER encoded SNMP packet.</returns> public override byte[] encode() { MutableByte buffer = new MutableByte(); // encode the global message data sequence header information MutableByte globalMessageData = new MutableByte(); // if message id is 0 then generate a new, random message id if (_messageId.Value == 0) { Random rand = new Random(); _messageId.Value = rand.Next(1, Int32.MaxValue); } // encode message id _messageId.encode(globalMessageData); // encode max message size _maxMessageSize.encode(globalMessageData); // message flags _msgFlags.encode(globalMessageData); // security model code _securityModel.Value = _userSecurityModel.Type; _securityModel.encode(globalMessageData); // add global message data to the main buffer // encode sequence header and add data AsnType.BuildHeader(buffer, SnmpConstants.SMI_SEQUENCE, globalMessageData.Length); buffer.Append(globalMessageData); MutableByte packetHeader = new MutableByte(buffer); // before going down this road, check if this is a discovery packet OctetString savedUserName = new OctetString(); bool privacy = _msgFlags.Privacy; bool authentication = _msgFlags.Authentication; bool reportable = _msgFlags.Reportable; if (_userSecurityModel.EngineId.Length <= 0) { // save USM settings prior to encoding a Discovery packet savedUserName.Set(_userSecurityModel.SecurityName); _userSecurityModel.SecurityName.Reset(); // delete security name for discovery packets _msgFlags.Authentication = false; _msgFlags.Privacy = false; _msgFlags.Reportable = true; } _userSecurityModel.encode(buffer); if (_userSecurityModel.EngineId.Length <= 0) { // restore saved USM values _userSecurityModel.SecurityName.Set(savedUserName); _msgFlags.Authentication = authentication; _msgFlags.Privacy = privacy; _msgFlags.Reportable = reportable; } // Check if privacy encryption is required MutableByte encodedPdu = new MutableByte(); if (_msgFlags.Privacy && _userSecurityModel.EngineId.Length > 0) { IPrivacyProtocol privacyProtocol = PrivacyProtocol.GetInstance(_userSecurityModel.Privacy); if (privacyProtocol == null) { throw new SnmpException(SnmpException.UnsupportedPrivacyProtocol, "Specified privacy protocol is not supported."); } // Get BER encoded ScopedPdu MutableByte unencryptedPdu = new MutableByte(); _scopedPdu.encode(unencryptedPdu); byte[] privacyParameters = null; // we have to expand the key IAuthenticationDigest auth = Authentication.GetInstance(_userSecurityModel.Authentication); if (auth == null) { throw new SnmpException(SnmpException.UnsupportedNoAuthPriv, "Invalid authentication protocol. noAuthPriv mode not supported."); } byte[] pkey = privacyProtocol.PasswordToKey(_userSecurityModel.PrivacySecret, _userSecurityModel.EngineId, auth); byte[] encryptedBuffer = privacyProtocol.Encrypt(unencryptedPdu, 0, unencryptedPdu.Length, pkey, _userSecurityModel.EngineBoots, _userSecurityModel.EngineTime, out privacyParameters, auth); _userSecurityModel.PrivacyParameters.Set(privacyParameters); OctetString encryptedOctetString = new OctetString(encryptedBuffer); encryptedOctetString.encode(encodedPdu); // now redo packet encoding buffer.Reset(); buffer.Set(packetHeader); _userSecurityModel.encode(buffer); int preEncodedLength = encodedPdu.Length; buffer.Append(encodedPdu); if (_maxMessageSize.Value != 0) { // verify compliance with maximum message size if ((encodedPdu.Length - preEncodedLength) > _maxMessageSize) { throw new SnmpException(SnmpException.MaximumMessageSizeExceeded, "ScopedPdu exceeds maximum message size."); } } } else { _scopedPdu.encode(encodedPdu); buffer.Append(encodedPdu); } int preVersionLength = buffer.Length; base.encode(buffer); int versionHeaderLength = buffer.Length - preVersionLength; if (_msgFlags.Authentication && _userSecurityModel.EngineId.Length > 0) { _userSecurityModel.Authenticate(ref buffer); // Now re-encode the packet with the authentication information _userSecurityModel.encode(packetHeader); packetHeader.Append(encodedPdu); base.encode(packetHeader); buffer = packetHeader; } return(buffer); }
/// <summary> /// Some protocols support a method to extend the encryption or decryption key when supplied key /// is too short. /// </summary> /// <param name="shortKey">Key that needs to be extended</param> /// <param name="password">Privacy password as configured on the SNMP agent.</param> /// <param name="engineID">Authoritative engine id. Value is retrieved as part of SNMP v3 discovery procedure</param> /// <param name="authProtocol">Authentication protocol class instance cast as <see cref="IAuthenticationDigest"/></param> /// <returns>Extended key value</returns> public byte[] ExtendShortKey(byte[] shortKey, byte[] password, byte[] engineID, IAuthenticationDigest authProtocol) { byte[] extKey = new byte[MinimumKeyLength]; int length = shortKey.Length; for (int i = 0; i < length; i++) { extKey[i] = shortKey[i]; } while (length < extKey.Length) { // fix the key hashing. Only hash upto the length of the *shortKey* byte[] hash = authProtocol.ComputeHash(extKey, 0, length); if (hash == null) { return(null); } int bytesToCopy = extKey.Length - length; if (bytesToCopy > authProtocol.DigestLength) { bytesToCopy = authProtocol.DigestLength; } Buffer.BlockCopy(hash, 0, extKey, length, bytesToCopy); length += bytesToCopy; } return(extKey); }
/// <summary> /// Encrypt <see cref="ScopedPdu"/> data BER encoded in a byte array. /// </summary> /// <param name="unencryptedData">BER encoded <see cref="ScopedPdu"/> byte array that needs to be encrypted</param> /// <param name="offset">Offset within the BER encoded byte array to start encryption operation from.</param> /// <param name="length">Length of data to encrypt</param> /// <param name="key">Encryption key</param> /// <param name="engineBoots">Authoritative engine boots value. Retrieved as part of SNMP v3 discovery process.</param> /// <param name="engineTime">Authoritative engine time value. Retrieved as part of SNMP v3 discovery process.</param> /// <param name="privacyParameters">Byte array that will receive privacy parameters information that is the result of the /// encryption procedure.</param> /// <param name="authDigest">Authentication digest reference. Not used by AES protocol and can be null</param> /// <returns>Byte array containing encrypted <see cref="ScopedPdu"/> BER encoded data</returns> public byte[] Encrypt(byte[] unencryptedData, int offset, int length, byte[] key, int engineBoots, int engineTime, out byte[] privacyParameters, IAuthenticationDigest authDigest) { // check the key before doing anything else if (key == null || key.Length < _keyBytes) throw new ArgumentOutOfRangeException("encryptionKey", "Invalid key length"); byte[] iv = new byte[16]; Int64 salt = NextSalt(); privacyParameters = new byte[PrivacyParametersLength]; byte[] bootsBytes = BitConverter.GetBytes(engineBoots); iv[0] = bootsBytes[3]; iv[1] = bootsBytes[2]; iv[2] = bootsBytes[1]; iv[3] = bootsBytes[0]; byte[] timeBytes = BitConverter.GetBytes(engineTime); iv[4] = timeBytes[3]; iv[5] = timeBytes[2]; iv[6] = timeBytes[1]; iv[7] = timeBytes[0]; // Set privacy parameters to the local 64 bit salt value byte[] saltBytes = BitConverter.GetBytes(salt); privacyParameters[0] = saltBytes[7]; privacyParameters[1] = saltBytes[6]; privacyParameters[2] = saltBytes[5]; privacyParameters[3] = saltBytes[4]; privacyParameters[4] = saltBytes[3]; privacyParameters[5] = saltBytes[2]; privacyParameters[6] = saltBytes[1]; privacyParameters[7] = saltBytes[0]; // Copy salt value to the iv array Buffer.BlockCopy(privacyParameters, 0, iv, 8, 8); Rijndael rm = new RijndaelManaged(); rm.KeySize = _keyBytes * 8; rm.FeedbackSize = 128; rm.BlockSize = 128; // we have to use Zeros padding otherwise we get encrypt buffer size exception rm.Padding = PaddingMode.Zeros; rm.Mode = CipherMode.CFB; // make sure we have the right key length byte[] pkey = new byte[MinimumKeyLength]; Buffer.BlockCopy(key, 0, pkey, 0, MinimumKeyLength); rm.Key = pkey; rm.IV = iv; ICryptoTransform cryptor = rm.CreateEncryptor(); byte[] encryptedData = cryptor.TransformFinalBlock(unencryptedData, offset, length); // check if encrypted data is the same length as source data if (encryptedData.Length != unencryptedData.Length) { // cut out the padding byte[] tmp = new byte[unencryptedData.Length]; Buffer.BlockCopy(encryptedData, 0, tmp, 0, unencryptedData.Length); return tmp; } return encryptedData; }
/// <summary> /// Operation not used by DES. Key length has to be 16 bytes of encryption/decryption operation will fail. /// /// When called, shortKey is returned. /// </summary> /// <param name="shortKey">Encryption key</param> /// <param name="password">Privacy password</param> /// <param name="engineID">Authoritative engine id</param> /// <param name="authProtocol">Authentication protocol class instance</param> /// <returns>unaltered shortKey value</returns> public byte[] ExtendShortKey(byte[] shortKey, byte[] password, byte[] engineID, IAuthenticationDigest authProtocol) { return(shortKey); }
/// <summary> /// Operation not used by DES. Key length has to be 16 bytes of encryption/decryption operation will fail. /// /// When called, shortKey is returned. /// </summary> /// <param name="shortKey">Encryption key</param> /// <param name="password">Privacy password</param> /// <param name="engineID">Authoritative engine id</param> /// <param name="authProtocol">Authentication protocol class instance</param> /// <returns>unaltered shortKey value</returns> public byte[] ExtendShortKey(byte[] shortKey, byte[] password, byte[] engineID, IAuthenticationDigest authProtocol) { return shortKey; }
/// <summary>Encode SNMP version 3 packet</summary> /// <param name="authKey">Authentication key (not password)</param> /// <param name="privKey">Privacy key (not password)</param> /// <remarks> /// Before encoding the packet into a byte array you need to ensure all required information is /// set. Examples of required information is request type, Vbs (Oid + values pairs), USM settings including /// SecretName, authentication method and secret (if needed), privacy method and secret (if needed), etc. /// </remarks> /// <returns>Byte array BER encoded SNMP packet.</returns> public byte[] Encode(byte[] authKey, byte[] privKey) { MutableByte buffer = new MutableByte(); // encode the global message data sequence header information MutableByte globalMessageData = new MutableByte(); // if message id is 0 then generate a new, random message id if (messageId.Value == 0) { Random rand = new Random(); messageId.Value = rand.Next(1, int.MaxValue); } // encode message id messageId.Encode(globalMessageData); // encode max message size maxMessageSize.Encode(globalMessageData); // message flags messageFlags.Encode(globalMessageData); // security model code securityModel.Value = userSecurityModel.Type; securityModel.Encode(globalMessageData); // add global message data to the main buffer // encode sequence header and add data AsnType.BuildHeader(buffer, SnmpConstants.SmiSequence, globalMessageData.Length); buffer.Append(globalMessageData); MutableByte packetHeader = new MutableByte(buffer); // before going down this road, check if this is a discovery packet OctetString savedUserName = new OctetString(); bool privacy = messageFlags.Privacy; bool authentication = messageFlags.Authentication; bool reportable = messageFlags.Reportable; if (userSecurityModel.EngineId.Length <= 0) { // save USM settings prior to encoding a Discovery packet savedUserName.Set(userSecurityModel.SecurityName); userSecurityModel.SecurityName.Reset(); // delete security name for discovery packets messageFlags.Authentication = false; messageFlags.Privacy = false; messageFlags.Reportable = true; } userSecurityModel.Encode(buffer); if (userSecurityModel.EngineId.Length <= 0) { // restore saved USM values userSecurityModel.SecurityName.Set(savedUserName); messageFlags.Authentication = authentication; messageFlags.Privacy = privacy; messageFlags.Reportable = reportable; } // Check if privacy encryption is required MutableByte encodedPdu = new MutableByte(); if (messageFlags.Privacy && userSecurityModel.EngineId.Length > 0) { IPrivacyProtocol privacyProtocol = PrivacyProtocol.GetInstance(userSecurityModel.Privacy); if (privacyProtocol == null) { throw new SnmpException(SnmpException.EErrorCode.UnsupportedPrivacyProtocol, "Specified privacy protocol is not supported."); } // Get BER encoded ScopedPdu MutableByte unencryptedPdu = new MutableByte(); scopedPdu.Encode(unencryptedPdu); // we have to expand the key IAuthenticationDigest auth = Authentication.GetInstance(userSecurityModel.Authentication); if (auth == null) { throw new SnmpException(SnmpException.EErrorCode.UnsupportedNoAuthPriv, "Invalid authentication protocol. noAuthPriv mode not supported."); } byte[] encryptedBuffer = privacyProtocol.Encrypt(unencryptedPdu, 0, unencryptedPdu.Length, privKey, userSecurityModel.EngineBoots, userSecurityModel.EngineTime, out byte[] privacyParameters, auth); userSecurityModel.PrivacyParameters.Set(privacyParameters); OctetString encryptedOctetString = new OctetString(encryptedBuffer); encryptedOctetString.Encode(encodedPdu); // now redo packet encoding buffer.Reset(); buffer.Set(packetHeader); userSecurityModel.Encode(buffer); int preEncodedLength = encodedPdu.Length; buffer.Append(encodedPdu); if (maxMessageSize.Value != 0) { // verify compliance with maximum message size if ((encodedPdu.Length - preEncodedLength) > maxMessageSize) { throw new SnmpException(SnmpException.EErrorCode.MaximumMessageSizeExceeded, "ScopedPdu exceeds maximum message size."); } } } else { scopedPdu.Encode(encodedPdu); buffer.Append(encodedPdu); } Encode(buffer); if (messageFlags.Authentication && userSecurityModel.EngineId.Length > 0) { userSecurityModel.Authenticate(authKey, ref buffer); // Now re-encode the packet with the authentication information userSecurityModel.Encode(packetHeader); packetHeader.Append(encodedPdu); Encode(packetHeader); buffer = packetHeader; } return(buffer); }
/// <summary>Encrypt <see cref="ScopedPdu"/> data BER encoded in a byte array.</summary> /// <param name="unencryptedData">BER encoded <see cref="ScopedPdu"/> byte array that needs to be encrypted</param> /// <param name="offset">Offset within the BER encoded byte array to start encryption operation from.</param> /// <param name="length">Length of data to encrypt</param> /// <param name="key">Encryption key</param> /// <param name="engineBoots">Authoritative engine boots value. Retrieved as part of SNMP v3 discovery process.</param> /// <param name="engineTime">Authoritative engine time value. Retrieved as part of SNMP v3 discovery process.</param> /// <param name="privacyParameters">Byte array that will receive privacy parameters information that is the result of the /// encryption procedure.</param> /// <param name="authDigest">Authentication digest reference. Not used by AES protocol and can be null</param> /// <returns>Byte array containing encrypted <see cref="ScopedPdu"/> BER encoded data</returns> public byte[] Encrypt(byte[] unencryptedData, int offset, int length, byte[] key, int engineBoots, int engineTime, out byte[] privacyParameters, IAuthenticationDigest authDigest) { // check the key before doing anything else if (key == null || key.Length < keyBytes) { throw new ArgumentOutOfRangeException(nameof(key), "Invalid key length"); } byte[] iv = new byte[16]; var salt = NextSalt(); privacyParameters = new byte[PrivacyParametersLength]; byte[] bootsBytes = BitConverter.GetBytes(engineBoots); iv[0] = bootsBytes[3]; iv[1] = bootsBytes[2]; iv[2] = bootsBytes[1]; iv[3] = bootsBytes[0]; byte[] timeBytes = BitConverter.GetBytes(engineTime); iv[4] = timeBytes[3]; iv[5] = timeBytes[2]; iv[6] = timeBytes[1]; iv[7] = timeBytes[0]; // Set privacy parameters to the local 64 bit salt value byte[] saltBytes = BitConverter.GetBytes(salt); privacyParameters[0] = saltBytes[7]; privacyParameters[1] = saltBytes[6]; privacyParameters[2] = saltBytes[5]; privacyParameters[3] = saltBytes[4]; privacyParameters[4] = saltBytes[3]; privacyParameters[5] = saltBytes[2]; privacyParameters[6] = saltBytes[1]; privacyParameters[7] = saltBytes[0]; // Copy salt value to the iv array Buffer.BlockCopy(privacyParameters, 0, iv, 8, 8); Rijndael rm = new RijndaelManaged { KeySize = keyBytes * 8, FeedbackSize = 128, BlockSize = 128, // we have to use Zeros padding otherwise we get encrypt buffer size exception Padding = PaddingMode.Zeros, Mode = CipherMode.CFB, }; // make sure we have the right key length byte[] pkey = new byte[MinimumKeyLength]; Buffer.BlockCopy(key, 0, pkey, 0, MinimumKeyLength); rm.Key = pkey; rm.IV = iv; ICryptoTransform cryptor = rm.CreateEncryptor(); byte[] encryptedData = cryptor.TransformFinalBlock(unencryptedData, offset, length); // check if encrypted data is the same length as source data if (encryptedData.Length != unencryptedData.Length) { // cut out the padding byte[] tmp = new byte[unencryptedData.Length]; Buffer.BlockCopy(encryptedData, 0, tmp, 0, unencryptedData.Length); return(tmp); } return(encryptedData); }
/// <summary> /// Encrypt ScopedPdu using DES encryption protocol /// </summary> /// <param name="unencryptedData">Unencrypted ScopedPdu byte array</param> /// <param name="offset">Offset to start encryption</param> /// <param name="length">Length of data to encrypt</param> /// <param name="key">Encryption key. Key has to be at least 32 bytes is length</param> /// <param name="engineBoots">Authoritative engine boots value</param> /// <param name="engineTime">Authoritative engine time value. Not used for DES</param> /// <param name="privacyParameters">Privacy parameters out buffer. This field will be filled in with information /// required to decrypt the information. Output length of this field is 8 bytes and space has to be reserved /// in the USM header to store this information</param> /// <param name="authDigest">Authentication digest class reference. Not used by DES and can be null.</param> /// <returns>Encrypted byte array</returns> /// <exception cref="ArgumentOutOfRangeException">Thrown when encryption key is null or length of the encryption key is too short.</exception> public byte[] Encrypt(byte[] unencryptedData, int offset, int length, byte[] key, int engineBoots, int engineTime, out byte[] privacyParameters, IAuthenticationDigest authDigest) { if (key == null || key.Length < MinimumKeyLength) throw new ArgumentOutOfRangeException("encryptionKey", "Encryption key length has to 32 bytes or more."); privacyParameters = GetSalt(engineBoots); byte[] iv = GetIV(key, privacyParameters); // DES uses 8 byte keys but we need 16 to encrypt ScopedPdu. Get first 8 bytes and use them as encryption key byte[] outKey = GetKey(key); int div = (int)Math.Floor(length / 8.0); if ((length % 8) != 0) div += 1; int newLength = div * 8; byte[] result = new byte[newLength]; byte[] buffer = new byte[newLength]; byte[] inbuffer = new byte[8]; byte[] cipherText = iv; int posIn = 0; int posResult = 0; Buffer.BlockCopy(unencryptedData, offset, buffer, 0, length); DES des = new DESCryptoServiceProvider(); des.Mode = CipherMode.ECB; des.Padding = PaddingMode.None; ICryptoTransform transform = des.CreateEncryptor(outKey, null); for (int b = 0; b < div; b++) { for (int i = 0; i < 8; i++) { inbuffer[i] = (byte)(buffer[posIn] ^ cipherText[i]); posIn++; } transform.TransformBlock(inbuffer, 0, inbuffer.Length, cipherText, 0); Buffer.BlockCopy(cipherText, 0, result, posResult, cipherText.Length); posResult += cipherText.Length; } des.Clear(); return result; }
/// <summary> /// Some protocols support a method to extend the encryption or decryption key when supplied key /// is too short. /// </summary> /// <param name="shortKey">Key that needs to be extended</param> /// <param name="password">Privacy password as configured on the SNMP agent.</param> /// <param name="engineID">Authoritative engine id. Value is retrieved as part of SNMP v3 discovery procedure</param> /// <param name="authProtocol">Authentication protocol class instance cast as <see cref="IAuthenticationDigest"/></param> /// <returns>Extended key value</returns> public byte[] ExtendShortKey(byte[] shortKey, byte[] password, byte[] engineID, IAuthenticationDigest authProtocol) { byte[] extKey = new byte[MinimumKeyLength]; byte[] lastKeyBuf = new byte[shortKey.Length]; Array.Copy(shortKey, lastKeyBuf, shortKey.Length); int keyLen = shortKey.Length > MinimumKeyLength ? MinimumKeyLength : shortKey.Length; Array.Copy(shortKey, extKey, keyLen); while (keyLen < MinimumKeyLength) { byte[] tmpBuf = authProtocol.PasswordToKey(lastKeyBuf, engineID); if (tmpBuf == null) { return null; } if (tmpBuf.Length <= (MinimumKeyLength - keyLen)) { Array.Copy(tmpBuf, 0, extKey, keyLen, tmpBuf.Length); keyLen += tmpBuf.Length; } else { Array.Copy(tmpBuf, 0, extKey, keyLen, MinimumKeyLength - keyLen); keyLen += (MinimumKeyLength - keyLen); } lastKeyBuf = new byte[tmpBuf.Length]; Array.Copy(tmpBuf, lastKeyBuf, tmpBuf.Length); } return extKey; }