HttpWebRequest with authorization

  • Thread starter Thread starter Jochen Hahnen
  • Start date Start date
J

Jochen Hahnen

Hello,

I have a problem connecting to a webserver with basic authentication. I am
using the compactframework so I can not use the class CredentialCache (this
was working fine). First of all I was searching for a solution using
NetworkCredential:

Code :

try
{
Uri url = new Uri(@"http://some.url/server/somebscwserver.cgi");
HttpWebRequest req =
(HttpWebRequest)WebRequest.Create(@"http://some.url/bscw/bscw.cgi");
NetworkCredential myCredentials = new NetworkCredential( "username",
"password");

// the following 3 lines would do the job but are not supported
// CredentialCache myCredentialCache = new CredentialCache();
// MyCredentialCache.Add(url, "Basic", myCredential);
// req.Credentials = myCredentialCache;

// so I tried this:
req.Credentials = myCredentials;

System.IO.Stream response = req.GetResponse().GetResponseStream();
// throws the exeption
}
catch( Exception ex )
{
MessageBox.Show( ex.Message );
}

But now the application throws the following execption: "The remote server
returned an error: (401) Unauthorized." (What is wrong here ??? )


Ok so next I tried to build up the authorization Header myself:

Code:

HttpWebRequest req =
(HttpWebRequest)WebRequest.Create(@"http://some.url/bscw/bscw.cgi/")
req.Method = "GET";
string base64EncodedAuthorizationString = "username" + ":" + "password";
byte[] binaryData = new Byte[base64EncodedAuthorizationString.Length];
binaryData = Encoding.UTF8.GetBytes(base64EncodedAuthorizationString);
base64EncodedAuthorizationString = Convert.ToBase64String(binaryData);
base64EncodedAuthorizationString = "Basic " +
base64EncodedAuthorizationString;
req.Headers.Add("Authorization", base64EncodedAuthorizationString);

[...] some code for connection

The Header is now: "Authorization: Basic Sm9jaGVuOnppdmk=\r\n\r\n".

But I still get the same Error Message( 401 Unauthorized)


To be sure the header line is correct I tried the following header using
TelNet:

Header:

<-GET /bscw/bscw.cgi/ HTTP/1.1
Authorization: Basic Sm9jaGVuOnmk=
Host: someserver.url.de

where "Sm9jaGVuOnmk=" is the Base64Encode of "username:password"
And this works fine !!! That means I was able to authorize on the server.


Is the ".Headers"-class used by HttpWebRequest at all ? Because in any
Request using TelNet where I set up the authorization line above
(Authorization: Basic Sm9jaGVuOnmk=)
the authorization succeeded !


Thanks for your time and help,
Jochen Hahnen
 
Jochen Hahnen said:
I have a problem connecting to a webserver with basic authentication. I am
using the compactframework so I can not use the class CredentialCache (this
was working fine). First of all I was searching for a solution using
NetworkCredential:

<snip>

The headers of the HttpWebRequest class are definitely used, including
on the compact framework. I do exactly the same thing myself in an app
I'm involved in at the moment.

I suggest you run a network trace to find out *exactly* what's going on
when you connect in both the failing code and with telnet. A couple of
comments on your code though:
byte[] binaryData = new Byte[base64EncodedAuthorizationString.Length];
binaryData = Encoding.UTF8.GetBytes(base64EncodedAuthorizationString);

There's no point in allocating an empty buffer and then immediately
overwriting it. You should also be using Encoding.ASCII, I believe - I
don't think the authorization header is meant to cope with non-ASCII
username/password combinations, although the HTTP spec is pretty woolly
here (and elsewhere in terms of character encodings implied in
headers).

Just:

byte[] binaryData = Encoding.ASCII.GetBytes
(base64EncodedAuthorizationString);

will do.

(I'd also suggest having two different string variables - because at
the time the above executes, base64EncodedAuthorizationString isn't
base64-encoded at all, and it's thus a confusing name.)
 
Thanks for these hints. But the problem remains the same.
But I think to run a network trace would be a good idea. But sorry for my
lack of knowledge I don't know how to set up such a trace.

Greetings,
Jochen


Jon Skeet said:
Jochen Hahnen said:
I have a problem connecting to a webserver with basic authentication. I am
using the compactframework so I can not use the class CredentialCache (this
was working fine). First of all I was searching for a solution using
NetworkCredential:

<snip>

The headers of the HttpWebRequest class are definitely used, including
on the compact framework. I do exactly the same thing myself in an app
I'm involved in at the moment.

I suggest you run a network trace to find out *exactly* what's going on
when you connect in both the failing code and with telnet. A couple of
comments on your code though:
byte[] binaryData = new Byte[base64EncodedAuthorizationString.Length];
binaryData =
Encoding.UTF8.GetBytes(base64EncodedAuthorizationString);

There's no point in allocating an empty buffer and then immediately
overwriting it. You should also be using Encoding.ASCII, I believe - I
don't think the authorization header is meant to cope with non-ASCII
username/password combinations, although the HTTP spec is pretty woolly
here (and elsewhere in terms of character encodings implied in
headers).

Just:

byte[] binaryData = Encoding.ASCII.GetBytes
(base64EncodedAuthorizationString);

will do.

(I'd also suggest having two different string variables - because at
the time the above executes, base64EncodedAuthorizationString isn't
base64-encoded at all, and it's thus a confusing name.)
 
Jochen Hahnen said:
Thanks for these hints. But the problem remains the same.
But I think to run a network trace would be a good idea. But sorry for my
lack of knowledge I don't know how to set up such a trace.

Actually, you don't even need a packet sniffer in this case - just set
up something which listens on port 80 and prints out to a console
whatever it receives. Fire the web request at it, and see what comes
out. (Sure, you won't get a response, but that's no problem.)

Look at the TcpListener example code in the MSDN for a sample which
does pretty much that. Just change the port number.
 
That sounds really easy, but there is the next problem :-)
It seems to be not allowed to set up a TcpListener to Port 80 since this
port is already used by the system.

Error:
SocketException: System.Net.Sockets.SocketException: Only one usage of each
sock
et address (protocol/network address/port) is normally permitted
at System.Net.Sockets.Socket.Bind(EndPoint localEP)
at System.Net.Sockets.TcpListener.Start()
 
Jochen Hahnen said:
That sounds really easy, but there is the next problem :-)
It seems to be not allowed to set up a TcpListener to Port 80 since this
port is already used by the system.

Error:
SocketException: System.Net.Sockets.SocketException: Only one usage of each
sock
et address (protocol/network address/port) is normally permitted
at System.Net.Sockets.Socket.Bind(EndPoint localEP)
at System.Net.Sockets.TcpListener.Start()

Either set it up on a different system or stop IIS then. This is only
for testing purposes.
 
You may need to make sure Enabled BASIC authentication
and ALSO Disable Windows Integrated Authentication on IIS
server, since .NET Compact Framework does NOT work
with Windows Integrated Auth.
 
I think this can not be the reason for the problem since the header line
explicitly shows that Basic Authentication is enabled on the server.


Heng Cao said:
You may need to make sure Enabled BASIC authentication
and ALSO Disable Windows Integrated Authentication on IIS
server, since .NET Compact Framework does NOT work
with Windows Integrated Auth.


Jochen Hahnen said:
Hello,

I have a problem connecting to a webserver with basic authentication. I am
using the compactframework so I can not use the class CredentialCache (this
was working fine). First of all I was searching for a solution using
NetworkCredential:

Code :

try
{
Uri url = new Uri(@"http://some.url/server/somebscwserver.cgi");
HttpWebRequest req =
(HttpWebRequest)WebRequest.Create(@"http://some.url/bscw/bscw.cgi");
NetworkCredential myCredentials = new NetworkCredential( "username",
"password");

// the following 3 lines would do the job but are not supported
// CredentialCache myCredentialCache = new CredentialCache();
// MyCredentialCache.Add(url, "Basic", myCredential);
// req.Credentials = myCredentialCache;

// so I tried this:
req.Credentials = myCredentials;

System.IO.Stream response = req.GetResponse().GetResponseStream();
// throws the exeption
}
catch( Exception ex )
{
MessageBox.Show( ex.Message );
}

But now the application throws the following execption: "The remote server
returned an error: (401) Unauthorized." (What is wrong here ??? )


Ok so next I tried to build up the authorization Header myself:

Code:

HttpWebRequest req =
(HttpWebRequest)WebRequest.Create(@"http://some.url/bscw/bscw.cgi/")
req.Method = "GET";
string base64EncodedAuthorizationString = "username" + ":" + "password";
byte[] binaryData = new Byte[base64EncodedAuthorizationString.Length];
binaryData = Encoding.UTF8.GetBytes(base64EncodedAuthorizationString);
base64EncodedAuthorizationString = Convert.ToBase64String(binaryData);
base64EncodedAuthorizationString = "Basic " +
base64EncodedAuthorizationString;
req.Headers.Add("Authorization", base64EncodedAuthorizationString);

[...] some code for connection

The Header is now: "Authorization: Basic Sm9jaGVuOnppdmk=\r\n\r\n".

But I still get the same Error Message( 401 Unauthorized)


To be sure the header line is correct I tried the following header using
TelNet:

Header:

<-GET /bscw/bscw.cgi/ HTTP/1.1
Authorization: Basic Sm9jaGVuOnmk=
Host: someserver.url.de

where "Sm9jaGVuOnmk=" is the Base64Encode of "username:password"
And this works fine !!! That means I was able to authorize on the server.


Is the ".Headers"-class used by HttpWebRequest at all ? Because in any
Request using TelNet where I set up the authorization line above
(Authorization: Basic Sm9jaGVuOnmk=)
the authorization succeeded !


Thanks for your time and help,
Jochen Hahnen
 
Hi Jon,
Actually, you don't even need a packet sniffer in this case - just set
up something which listens on port 80 and prints out to a console
whatever it receives. Fire the web request at it, and see what comes
out. (Sure, you won't get a response, but that's no problem.)

An even easier alternative would be to run a tool such as Simon Fell's
TcpTrace [1] :)

[1] www.pocketsoap.com/tcptrace
 
Back
Top