/// <summary> /// Closes the underlying package after saving it to the specified file. /// </summary> /// <param name="path">File system path where the package will be written.</param> public void CloseWrite(string path) { FileStream fs = null; if (package == null) { throw new InvalidOperationException("Package already closed."); } try { fs = new FileStream(path, FileMode.Create); package.Close(false); bs.Position = 0; bs.CopyTo(fs, (int)bs.Length); } finally { if (fs != null) { fs.Close(); } bs.Close(); } }
/// <summary> /// Constuctor. /// </summary> /// <param name="operation">Specifies how the message should be interpreted.</param> /// <param name="timestamp">The time (UTC) when the delivery was completed successfully or was aborted.</param> /// <param name="targetEP">The original target or cluster endpoint.</param> /// <param name="confirmEP">The confirmation endpoint (or <c>null</c>).</param> /// <param name="query">The query message.</param> /// <param name="topologyID">The globally unique cluster topology provider instance ID or <see cref="Guid.Empty" />.</param> /// <param name="topologyInfo">The serialized cluster itopology nformation (or <c>null</c>).</param> /// <param name="topologyParam">The serialized topology parameter (or <c>null</c>).</param> /// <param name="exception">The exception for failed deliveries or queries.</param> /// <param name="response">The response message (or <c>null</c>).</param> public DeliveryMsg(DeliveryOperation operation, DateTime timestamp, MsgEP targetEP, MsgEP confirmEP, Msg query, Guid topologyID, string topologyInfo, string topologyParam, Exception exception, Msg response) { this.Operation = operation; this.Timestamp = timestamp; this.TargetEP = targetEP; this.ConfirmEP = confirmEP; this.TopologyID = topologyID; this.TopologyInfo = topologyInfo; this.TopologyParam = topologyParam; this.Exception = exception; this.query = query; this.response = response; // Serialize the query and responses to the message blob EnhancedBlockStream es = new EnhancedBlockStream(); try { Msg.Save(es, query); if (response != null) { Msg.Save(es, response); } base._Data = es.ToArray(); } finally { es.Close(); } }
/// <summary> /// Handles the deserialization of the <see cref="Query" /> and <see cref="Response" /> /// messages from the blob data. /// </summary> /// <param name="es">The enhanced stream holding the payload data.</param> protected override void ReadFrom(EnhancedStream es) { // Let the base message class load the properties and the data blob. base.ReadFrom(es); // Now parse the query and (optional) response messages from the blob es = new EnhancedBlockStream(base._Data); try { query = Msg.Load(es); if (es.Eof) { response = null; } else { response = Msg.Load(es); } } finally { es.Close(); } }
/// <summary> /// Decrypts a user password by encrypted using <see cref="EncryptUserPassword" /> /// using the message authenticator and the shared NAS secret. /// </summary> /// <param name="encryptedPassword">The encrypted password.</param> /// <param name="secret">The shared NAS secret.</param> /// <returns>The decrypted password.</returns> /// <exception cref="RadiusException">Thrown if the encrypted password is invalid.</exception> public string DecryptUserPassword(byte[] encryptedPassword, string secret) { var bs = new EnhancedBlockStream(128, 128); byte[] secretBytes = Helper.ToAnsi(secret); byte[] clearBlock = new byte[16]; byte[] xorHash; byte[] decrypted; int pos; int zeroPos; try { // The encrypted password length must be a non-zero multiple of 16 bytes. if (encryptedPassword.Length == 0 || encryptedPassword.Length % 16 != 0) { throw new RadiusException("Encrypted user password length must be a positive multiple of 16 bytes."); } // The first XOR hash is MD5(secret + authenticator) xorHash = MD5Hasher.Compute(Helper.Concat(secretBytes, this.Authenticator)); // Perform the decryption. The trick here is to unmunge the 16 byte // blocks by XORing them with the current XOR hash. pos = 0; while (true) { // clearBlock = XOR hash ^ next 16 encrypted bytes for (int i = 0; i < 16; i++) { clearBlock[i] = (byte)(xorHash[i] ^ encryptedPassword[pos + i]); } bs.WriteBytesNoLen(clearBlock); pos += 16; if (pos >= encryptedPassword.Length) { break; } // Next XOR hash = MD5(secret + last cypherblock) xorHash = MD5Hasher.Compute(Helper.Concat(secretBytes, Helper.Extract(encryptedPassword, pos - 16, 16))); } // Scan forward to the first zero byte. If we find one, we're going // to assume that it was a padding byte. decrypted = bs.ToArray(); zeroPos = -1; for (int i = 0; i < decrypted.Length; i++) { if (decrypted[i] == 0) { zeroPos = i; break; } } if (zeroPos == -1) { return(Helper.FromAnsi(decrypted)); } else { return(Helper.FromAnsi(decrypted, 0, zeroPos)); } } finally { bs.Close(); } }
/// <summary> /// Encrypts a user password string by combining it with the shared NAS /// secret and the message authenticator as described in RFC 2865 page 27. /// </summary> /// <param name="userPassword">The user password to be encrypted.</param> /// <param name="secret">The shared NAS secret.</param> /// <returns>The encrypted password.</returns> /// <exception cref="RadiusException">Thrown if the password is too large or too small.</exception> public byte[] EncryptUserPassword(string userPassword, string secret) { var bs = new EnhancedBlockStream(128, 128); byte[] secretBytes = Helper.ToAnsi(secret); byte[] cypherBlock = new byte[16]; byte[] rawPwd; byte[] xorHash; byte[] pwd; int pos; try { rawPwd = Helper.ToAnsi(userPassword); if (rawPwd.Length == 0) { throw new RadiusException("Zero length password is not allowed."); } // Copy userPassword into pwd, padding the result out with zeros // to a multiple of 16 bytes if (rawPwd.Length % 16 == 0) { pwd = rawPwd; } else { pwd = new byte[(rawPwd.Length / 16 + 1) * 16]; Array.Copy(rawPwd, pwd, rawPwd.Length); } // The first XOR hash is MD5(secret + authenticator) xorHash = MD5Hasher.Compute(Helper.Concat(secretBytes, this.Authenticator)); // Perform the encryption pos = 0; while (true) { // Cyperblock = XOR hash ^ next 16 bytes of password for (int i = 0; i < 16; i++) { cypherBlock[i] = (byte)(xorHash[i] ^ pwd[pos + i]); } bs.WriteBytesNoLen(cypherBlock); pos += 16; if (pos >= pwd.Length) { break; } // Next XOR hash is MD5(secret + cypherblock) xorHash = MD5Hasher.Compute(Helper.Concat(secretBytes, cypherBlock)); } if (bs.Length > 128) { throw new RadiusException("Encrypted password exceeds 128 bytes."); } return(bs.ToArray()); } finally { bs.Close(); } }