/// <summary> /// Create HTTP headers for a SOAP message. /// </summary> /// <param name="size">SOAP message size.</param> /// <param name="path">Address for the first line.</param> /// <param name="address">Hostname.</param> /// <param name="packet"></param> /// <param name="username"></param> /// <param name="password"></param> /// <param name="testingSettings"></param> /// <returns>UTF-8 encoded string.</returns> public static byte[] CreateHttpHeaders(long size, string path, string address, HttpPacket packet, string username, string password, DigestTestingSettings testingSettings, ref string nonceBack, ref int nonceCounter) { // Create HTTP headers and add content StringBuilder httpRequest = new StringBuilder(); httpRequest.AppendFormat(STATUSLINEPATTERN, path); httpRequest.AppendFormat(HOSTLINEPATTERN, address); httpRequest.AppendFormat("{0}: {1}; {2}={3}\r\n", CONTENTTYPE, APPLICATIONSOAPXML, CHARSET, "utf-8"); if (packet != null) { HttpDigest.DigestAuthenticationParameters parameters = new HttpDigest.DigestAuthenticationParameters(); parameters.Address = path; parameters.Challenge = packet; parameters.UserName = username; parameters.Password = password; httpRequest.AppendFormat("{0}\r\n", HttpDigest.CreateDigestAuthenticationHeader(parameters, testingSettings, ref nonceBack, ref nonceCounter)); } httpRequest.Append(CONTENTLENGTH + ": " + (size).ToString() + "\r\n"); httpRequest.Append("\r\n"); // Convert HTTP request to byte array to send return(Encoding.UTF8.GetBytes(httpRequest.ToString())); }
/// <summary> /// Creates header using challenge packet. /// </summary> /// <returns></returns> public static string CreateDigestAuthenticationHeader( DigestAuthenticationParameters authenticationParameters, DigestTestingSettings testingSettings, ref string nonceBack, ref int nonceCounter) { string header = string.Empty; string username = authenticationParameters.UserName; string password = authenticationParameters.Password; string address = authenticationParameters.Address; HttpPacket packet = authenticationParameters.Challenge; if (packet.Headers.ContainsKey(WWWAUTHENTICATEHEADER)) { string challenge = packet.Headers[WWWAUTHENTICATEHEADER]; int schemeEnd = challenge.IndexOf(' '); // scheme - should be "Digest" string scheme = challenge.Substring(0, schemeEnd).Trim(); string authParams = challenge.Substring(schemeEnd).Trim(); List <int> commas = new List <int>(); bool inQuote = false; for (int i = 0; i < authParams.Length; i++) { char nextChar = authParams[i]; if (nextChar == '"') { inQuote = !inQuote; } else { if (nextChar == ',' && !inQuote) { commas.Add(i); } } } commas.Add(authParams.Length); Dictionary <string, string> parameters = new Dictionary <string, string>(StringComparer.CurrentCultureIgnoreCase); int lastStart = 0; foreach (int comma in commas) { string field = authParams.Substring(lastStart, (comma - lastStart)); string[] pair = field.Split('='); var equalPos = field.IndexOf('='); var paramName = field.Substring(0, equalPos); var paramValue = field.Substring(equalPos + 1); if (!string.IsNullOrEmpty(paramName) && !string.IsNullOrEmpty(paramValue)) { parameters.Add(Unquote(paramName), paramValue.Trim()); } else { throw new ApplicationException(string.Format("Invalid authentication header: {0}", challenge)); } lastStart = comma + 1; } // parameters are parsed // quotes are not trimmed! if (parameters[NONCE] != nonceBack) { nonceBack = parameters[NONCE]; nonceCounter = 1; } else { nonceCounter++; } string nonceCounterString = string.Format("{0:x08}", nonceCounter); StringBuilder sb = new StringBuilder("Authorization: Digest "); if (testingSettings == null || !testingSettings.UserNameMissing) { sb.AppendFormat("username=\"{0}\", ", username); } if (testingSettings == null || !testingSettings.RealmMissing) { // realm should contain quotes; sb.AppendFormat("{0}={1}, ", REALM, parameters[REALM]); } string alg = MD5; if (parameters.ContainsKey(ALGORITHM)) { alg = parameters[ALGORITHM]; } sb.AppendFormat("qop=\"auth\", algorithm=\"{0}\", ", alg); if (testingSettings == null || !testingSettings.UriMissing) { sb.AppendFormat("uri=\"{0}\", ", address); } if (testingSettings == null || !testingSettings.NonceMissing) { sb.AppendFormat("{0}={1}, nc={2}, ", NONCE, parameters[NONCE], nonceCounterString); } // cnonce string cnonce = GetNonce(); sb.AppendFormat("cnonce=\"{0}\", ", cnonce); // opaque if (parameters.ContainsKey(OPAQUE)) { sb.AppendFormat("{0}={1}, ", OPAQUE, parameters[OPAQUE]); } // response if (testingSettings == null || !testingSettings.ResponseMissing) { string realm = Unquote(parameters[REALM]); string nonce = Unquote(parameters[NONCE]); /* * request-digest = <"> < KD ( H(A1), unq(nonce-value) * ":" nc-value * ":" unq(cnonce-value) * ":" unq(qop-value) * ":" H(A2) * ) <"> * * A1 = unq(username-value) ":" unq(realm-value) ":" passwd * * A2 = Method ":" digest-uri-value * */ string a1; a1 = string.Format("{0}:{1}:{2}", username, realm, password); System.Diagnostics.Debug.WriteLine(string.Format("A1: {0}", a1)); if (alg == MD5SESS) { if (string.IsNullOrEmpty(_a1)) { _a1 = GetMD5HashBinHex(a1); } a1 = string.Format("{0}:{1}:{2}", _a1, nonce, cnonce); } string a2 = string.Format("POST:{0}", address); string ha1 = GetMD5HashBinHex(a1); string ha2 = GetMD5HashBinHex(a2); System.Diagnostics.Debug.WriteLine(string.Format("HA1: {0}", ha1)); System.Diagnostics.Debug.WriteLine(string.Format("A2: {0}", a2)); System.Diagnostics.Debug.WriteLine(string.Format("HA2: {0}", ha2)); string a = string.Format("{0}:{1}:{4}:{2}:auth:{3}", ha1, nonce, cnonce, ha2, nonceCounterString); System.Diagnostics.Debug.WriteLine(string.Format("unhashedDigest: {0}", a)); string response = GetMD5HashBinHex(a); System.Diagnostics.Debug.WriteLine(string.Format("hashedDigest: {0}", response)); sb.AppendFormat("response=\"{0}\"", response); } header = sb.ToString(); } return(header); }
byte[] CreateRequest(Message message, out int bodyOffset) { byte[] messageBytes; if (_currentMessageBytes == null) { // Encode message using _encoder // After it message is closed (due to our code) ArraySegment <byte> messageBuffer = EncodeMessage(message); // Copy byte in order not to use Buffer messageBytes = new byte[messageBuffer.Count]; Array.Copy(messageBuffer.Array, messageBytes, messageBuffer.Count); _currentMessageBytes = messageBytes; } else { messageBytes = _currentMessageBytes; } // xml directive to add string xmlDirective = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"; string username = string.Empty; string password = string.Empty; HttpPacket header = null; DigestTestingSettings settings = null; if (_credentialsProvider != null) { username = _credentialsProvider.Username; password = _credentialsProvider.Password; if (_credentialsProvider.Security == Security.Digest || _credentialsProvider.Security == Security.DigestTesting) { header = _digestAuthChallenge; } if (_credentialsProvider.Security == Security.DigestTesting) { settings = _credentialsProvider.DigestTestingSettings; } } // create headers byte[] httpHeaders = HttpHelper.CreateHttpHeaders(messageBytes.Length + xmlDirective.Length, _to.Uri.PathAndQuery, _to.Uri.Host, header, username, password, settings, ref _nonceBack, ref _nonceCounter); //whole message with headers byte[] bytes = new byte[httpHeaders.Length + messageBytes.Length + xmlDirective.Length]; Array.Copy(httpHeaders, bytes, httpHeaders.Length); Array.Copy(Encoding.UTF8.GetBytes(xmlDirective), 0, bytes, httpHeaders.Length, xmlDirective.Length); Array.Copy(messageBytes, 0, bytes, httpHeaders.Length + xmlDirective.Length, messageBytes.Length); // body offset - for logger bodyOffset = httpHeaders.Length; return(bytes); }
// POSTPONED to projects following Erdinger /*[Test(Name = "DIGEST AUTHENTICATION – INVALID AUTHENTICATION", * Path = "Security Test Cases", * Order = "01.01.03", * Id = "1-1-3", * Category = Category.SECURITY, * Version = 2.1, * RequirementLevel = RequirementLevel.Must, * RequiredFeatures = new Feature[] { Feature.Digest }, * FunctionalityUnderTest = new Functionality[] { Functionality.DigestAuthentication })]*/ public void DigestAuthenticationParametersTest() { RunTest(() => { Assert(!(string.IsNullOrEmpty(_password) || string.IsNullOrEmpty(_username)), "This test cannot be performed without credentials supplied", "Check if credentials were defined"); string operationName = _secureOperation; string requestDescription = string.Empty; Action checkDigest = new Action( () => { bool exception = false; try { RunStep( () => { InvokeOperation(operationName); }, string.Format("Invoke {0} {1}", _secureOperation, requestDescription)); } catch (Exception exc) { StepPassed(); if (exc is HttpTransport.Interfaces.Exceptions.AccessDeniedException) { // digest exception = true; } } finally { DoRequestDelay(); } Assert(exception, string.Format("No HTTP 401 response received when operation is invoked {0}", requestDescription), "Check response"); }); requestDescription = "without credentials supplied"; _credentialsProvider.Security = Security.None; // without credentials at all checkDigest(); // with some fields missing DigestTestingSettings settings = new DigestTestingSettings(); _credentialsProvider.DigestTestingSettings = settings; _credentialsProvider.Security = Security.DigestTesting; settings.UserNameMissing = true; requestDescription = "without username included in request"; checkDigest(); // realm settings.RealmMissing = true; settings.UserNameMissing = false; requestDescription = "without realm included in request"; checkDigest(); // nonce settings.NonceMissing = true; settings.RealmMissing = false; requestDescription = "without nonce included in request"; checkDigest(); // uri settings.UriMissing = true; settings.NonceMissing = false; requestDescription = "without URI included in request"; checkDigest(); // response settings.ResponseMissing = true; settings.UriMissing = false; requestDescription = "without response included in request"; checkDigest(); }, () => { }); }