I know SecurityToken serialization should be a simple thing. Have done it in the past with tokens from Active STS. However, now when I am trying to do it using a token from Passive STS flow, getting an error from the WriteToken method that
"the private key is not present in the X.509Certificate".
The pretty straight forward code I am using is:
StringBuilder sb = new StringBuilder();
var writer = XmlWriter.Create(new StringWriter(sb), new XmlWriterSettings { OmitXmlDeclaration = true });
SecurityTokenHandlerCollection handlers = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
SecurityTokenHandler handler = handlers[securityToken];
handler.WriteToken(writer, securityToken);
This code needs to be in a client of STS where the certificate private key would obviously be not there.
How do I get around this?
Yeah! Got the answer!
I was mixing .Net 3.5 Microsoft.IdentityModel.dll based code in .Net 4.5. Once I moved the above code to serialize the token into .Net 4.5, it worked as expected.
(Was banging my head on this for last 2 days :()
Related
I have an old app that runs a read only membership provider. I am tasked with creating an admin page to help with adding/changing/deleting users from this app. The membership provider uses FormsAuthentication, which I cannot use because my admin app is in .net Core. I'm trying to reverse-engineer the way they encrypt using FormsAuthentication and I have this so far:
They use:
FormsAuthentication.HashPasswordForStoringInConfigFile(password, "sha1").ToLower();
I've reverse-engineered it to:
(string pwd is passed in)
HashAlgorithm hashAlgorithm = new HMASHA1();
var step1 = Encoding.UTF8.GetBytes(pwd);
var step2 = hashAlgorithm.ComputeHash(step1);
var step3 = BinaryToHex(step2);
And step3 comes out to something like this "AD626B9D42073B299ECFC664CCB7A8B01F3AF726", which looks like what the passwords look like in the XML user file for the old app.
I'm just curious if I use this method of hashing (which works in .net core), will the hashed passwords be able to be "validated" by FormsAuthentication?
My tests so far don't seem to be working. Any ideas? Am I doing it wrong?
EDIT: it is not HMASHA1, it's SHA1Cng - which I can't use because it is in System.Core in the .net framework 4.something... what can I use to do this in .net core?
I figured it out, this works:
using System.Security.Cryptography;
var sha1 = SHA1.Create();
var step1 = Encoding.UTF8.GetBytes(pwd);
var step2 = sha1.ComputeHash(step1);
var step3 = BinaryToHex(step2);
BinaryToHex and it's associated functions are copied from System.Web.Security.Cryptography.CryptoUtil
Would still like to be able to do this in reverse and decrypt passwords.
Slightly different take, legacy hashed passwords in database
string incoming = inCrypt(inputPassword);
incoming=Regex.Replace(incoming, #"[^0-9a-zA-Z]", "");
public string inCrypt(string pwd)
{
var sha1 = SHA1.Create();
var step1 = System.Text.Encoding.UTF8.GetBytes(pwd);
var step2 = sha1.ComputeHash(step1);
var step3 = BitConverter.ToString(step2);
return step3.ToString();
}
it worked, and the hashed passwords can be compared as strings.
I am working on a project which I did not write, have inherited, and have an issue that I'm not sure quite how to solve. My background is not in .NET, so please excuse anything that doesn't sound right, as I may not know what the correct terminology should be.
We are using Visual Studio 2008 to compile a project that is running on Windows CE 6.0. We are using the Compact Framework v2.0. The software is running on an Embedded processor in a network (WIFI) connected industrial environment. The main UI is written in VB, and all of the supporting DLLs are written using C#.
Up until now we've only been required to connect to http (non-secure) web addresses for GET requests. We now have a requirement to switch these addresses over to https (secure) for security's sake.
The HttpWebRequest is built/submitted from VB. When I provide the code with the https address, I get the "Could not establish secure channel for SSL/TLS" error that is in the subject.
Here is the code for that request:
Dim myuri As System.Uri = New System.Uri(sUrl)
Dim myHttpwebresponse As HttpWebResponse = Nothing
Dim myhttpwebrequest As HttpWebRequest = CType(WebRequest.Create(myuri), HttpWebRequest)
myhttpwebrequest.KeepAlive = False
myhttpwebrequest.Proxy.Credentials = CredentialCache.DefaultCredentials
myhttpwebrequest.ContentType = "text/xml"
myhttpwebrequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
myhttpwebrequest.AllowAutoRedirect = False
myhttpwebrequest.Timeout = 150000
Dim mycred As NetworkCredential = New NetworkCredential(username, password)
Dim myCredentialCache As CredentialCache = New CredentialCache()
myCredentialCache.Add(myuri, "Basic", mycred)
myhttpwebrequest.Credentials = myCredentialCache
myhttpwebrequest.Method = "GET"
myhttpwebrequest.ProtocolVersion = HttpVersion.Version10
ServicePointManager.CertificatePolicy = New AcceptServerNameMismatch
myHttpwebresponse = CType(myhttpwebrequest.GetResponse(), HttpWebResponse)
I have done quite a bit of reading over the last day or so that indicate that the CertificatePolicy is where I can override the ICertificatePolicy classes to essentially validate all SSL requests. Definitely not safe, and not ideal, but I'm not sure of another way to handle these requests.
My class to do this is:
Public Class MyCertificatePolicy
Implements ICertificatePolicy
Public Shared DefaultValidate As Boolean = True
Public Sub trustedCertificatePolicy()
End Sub
Public Function CheckValidationResult(ByVal srvPoint As ServicePoint, _
ByVal cert As X509Certificate, ByVal request As WebRequest, ByVal problem As Integer) _
As Boolean Implements ICertificatePolicy.CheckValidationResult
Return True
End Function
End Class
Unfortunately when the response comes back, it never calls CheckValidationResult(). Thus, no validation and the error.
So my questions...
The "Right" way to do this according to everything that I've read is to use the ServerCertificateValidationCallback. Unfortunately with the version of Compact Framework that we are using (maybe all?) it is not included. Is there something that I'm missing that would cause that function not to get called?
Again, from what I've read, I believe that the Framework that we're running on doesn't support TLS v1.1 or v1.2. Which most current servers are running. Is there a way in VB to get around this?
Is there another Request method that can be used?
Any help or guidance as to where to go from here is greatly appreciated!
You need to install the trusted root certificate on the device(s), that matches the SSL certificate on your server.
Or change the certificate on the server to match one of the Trusted Roots on the device(s). By default, the devices ship with a very small number of trusted CAs, unlike desktop browsers that contain nearly every CA in the world.
I am using ReportExecutionServiceSoapClient in .Net Core i got the latest version of .net Core and tried to get a report from reporting services to work. after I've used the WCF connection service I was able to add the code with looks like bellow
// Instantiate the Soap client
ReportExecutionServiceSoap rsExec = new ReportExecutionServiceSoapClient(ReportExecutionServiceSoapClient.EndpointConfiguration.ReportExecutionServiceSoap);
// Create a network credential object with the appropriate username and password used
// to access the SSRS web service
string historyID = null;
TrustedUserHeader trustedUserHeader = new TrustedUserHeader();
ExecutionHeader execHeader = new ExecutionHeader();
// Here we call the async LoadReport() method using the "await" keyword, which means any code below this method
// will not execute until the result from the LoadReportAsync task is returned
var taskLoadReport = rsExec.LoadReportAsync(reportPath, historyID);
// By the time the LoadReportAsync task is returned successfully, its "executionInfo" property
// would have already been populated. Now the remaining code in this main thread will resume executing
string deviceInfo = null;
string format = "EXCEL";
// Now, similar to the above task, we will call the RenderAsync() method and await its result
var taskRender = await rsExec.RenderAsync(renderReq);
When it hist renderAsync all falls apart because the credentials for the service are not set anywhere. I've tried to Login async with no success. Also I've tried to set the credentials with SetExecutionCredentialsAsync but I've got and error saying "The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'NTLM'." I don't know how to change that for ReportExecutionServiceSoapClient.
I have read some posts in which Microsoft guys says that the authentication with a soap is not resolved but for me it seems so close to be true. I feel like I am missing something.
Technology stack: VS 2017, .net Core web api, ssrs 2016, sql server 2016 standard
How can I authenticate the user for this call?
I know this is an old question but I had the same issue and stumbled onto the answer.
After creating the ReportExecutionServiceSoap object you can specify the username and password in the ClientCredentials. I've had success with this using the Basic client credential type. Be sure you are using HTTPS, otherwise your password is sent in plaintext to the reporting server. I also recommend storing the user/password in a secure place and not code.
BasicHttpBinding rsBinding = new BasicHttpBinding();
rsBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
rsBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
EndpointAddress rsEndpointAddress = new EndpointAddress("https://servername/ReportServer/ReportExecution2005.asmx");
var rsExec = new ReportExecutionServiceSoapClient(rsBinding, rsEndpointAddress);
rsExec.ClientCredentials.UserName.UserName = "username";
rsExec.ClientCredentials.UserName.Password = "pass";
I'm writing a client against a vendor's webservice, using WCF in Visual Studio 2010. I have no ability to change their implementation or configuration.
Running against an install on their test server, I had no problems. I added a service reference from their wsdl, set the url in code, and made the call:
var client = new TheirWebservicePortTypeClient();
client.Endpoint.Address = new System.ServiceModel.EndpointAddress(webServiceUrl);
if (webServiceUsername != "")
{
client.ClientCredentials.UserName.UserName = webServiceUsername;
client.ClientCredentials.UserName.Password = webServicePassword;
}
TheirWebserviceResponse response = client.TheirOperation(myRequest);
Simple and straightforward. Until they moved it to their production server and configured it to use https. Then I got this error:
The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Basic realm='.
So I went looking for help. I found this: Can not call web service with basic authentication using wcf.
The approved answer suggested this:
BasicHttpBinding binding = new BasicHttpBinding();
binding.SendTimeout = TimeSpan.FromSeconds(25);
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType =
HttpClientCredentialType.Basic;
EndpointAddress address = new EndpointAddress(your-url-here);
ChannelFactory<MyService> factory =
new ChannelFactory<MyService>(binding, address);
MyService proxy = factory.CreateChannel();
proxy.ClientCredentials.UserName.UserName = "username";
proxy.ClientCredentials.UserName.Password = "password";
Which also seemed simple enough. Except for my trying to figure out which of the multitude of classes and interfaces that were generated from the wsdl to make the service reference I should use in place of the "MyService", above.
My first try was to use "TheirWebservicePortTypeClient" - the class I had instantiated in the previous version. That gave me a runtime error:
The type argument passed to the generic ChannelFactory class must be an interface type.
So I dug into the generated code, a bit more. I saw this:
public partial class TheirWebservicePortTypeClient
:
System.ServiceModel.ClientBase<TheirWebservicePortType>,
TheirWebservicePortType
{
...
}
So I tried instantiating ChannelFactory<> with TheirWebservicePortType.
This gave me compile-time errors. The resulting proxy didn't have a ClientCredentials member, or a TheirOperation() method.
So I tried "System.ServiceModel.ClientBase".
Instantiation ChannelFactory<> with it still gave me compile-time errors. The resulting proxy did have a ClientCredentials member, but it still didn't have a TheirOperation() method.
So, what gives? How do I pass a username/password to an HTTPS webservice, from a WCF client?
==================== Edited to explain the solution ====================
First, as suggested, instantiation the factory with TheirWebservicePortType, adding the username and password to the factory.Credentials, instead of to proxy.ClientCredentials worked fine. Except for one bit of confusion.
Maybe it's something to do with the odd way the wsdl is written, but the client class, TheirWebservicePortTypeClient, defined TheirOperation as taking a Request argument and returning a Response result. The TheirWebservicePortType interface defined TheirOperation as taking a TheirOperation_Input argument and returning a TheirOperation_Output result, where TheirOperation_Input contained a Request member and TheirOperation_Output contained a Response member.
In any case, if I constructed a TheirOperation_Input object from the passed Request, the call to the proxy succeeded, and I could then extract the contained Response object from the returned TheirOperation_Output object:
TheirOperation_Output output = client.TheirOperation(new TheirOperation_Input(request));
TheirWebserviceResponse response = output.TheirWebserviceResponse;
You add the credentials to the ChannelFactory Credentials property
When making an HttpWebRequest within a CLR stored procedure (as per the code below), the first invocation after the Sql Server is (re-)started or after a given (but indeterminate) period of time waits for quite a length of time on the GetResponse() method call.
Is there any way to resolve this that doesn't involve a "hack" such as having a Sql Server Agent job running every few minutes to try and ensure that the first "slow" call is made by the Agent and not "real" production code?
function SqlString MakeWebRequest(string address, string parameters, int connectTO)
{
SqlString returnData;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(String.Concat(address.ToString(), "?", parameters.ToString()));
request.Timeout = (int)connectTO;
request.Method = "GET";
using (WebResponse response = request.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(responseStream))
{
SqlString responseFromServer = reader.ReadToEnd();
returnData = responseFromServer;
}
}
}
response.Close();
return returnData;
}
(Error handling and other non-critical code has ben removed for brevity)
See also this Sql Server forums thread.
This was a problem for me using HttpWebRequest at first. It's due to the the class looking for a proxy to use. If you set the object's Proxy value to null/Nothing, it'll zip right along.
Looks to me like code signing verification. The MS shipped system dlls are all signed and SQL verifies the signatures at load time. Apparently the certificate revocation list is expired and the certificate verification engine times out retrieving a new list. I have blogged about this problem before Fix slow application startup due to code sign validation and the problem is also described in this Technet article: Certificate Revocation and Status Checking.
The solution is pretty arcane and involves registry editing of the key: HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CertDllCreateCertificateChainEngine\Config:
ChainUrlRetrievalTimeoutMilliseconds This is each individual CRL check call timeout. If is 0 or not present the default value of 15 seconds is used. Change this timeout to a reasonable value like 200 milliseconds.
ChainRevAccumulativeUrlRetrievalTimeoutMilliseconds This is the aggregate CRL retrieval timeout. If set to 0 or not present the default value of 20 seconds is used. Change this timeout to a value like 500 milliseconds.
There is also a more specific solution for Microsoft signed assemblies (this is from the Biztalk documentation, but applies to any assembly load):
Manually load Microsoft Certificate
Revocation lists
When starting a .NET application, the
.NET Framework will attempt to
download the Certificate Revocation
list (CRL) for any signed assembly. If
your system does not have direct
access to the Internet, or is
restricted from accessing the
Microsoft.com domain, this may delay
startup of BizTalk Server. To avoid
this delay at application startup, you
can use the following steps to
manually download and install the code
signing Certificate Revocation Lists
on your system.
Download the latest CRL updates from
http://crl.microsoft.com/pki/crl/products/CodeSignPCA.crl
and
http://crl.microsoft.com/pki/crl/products/CodeSignPCA2.crl.
Move the CodeSignPCA.crl and CodeSignPCA2.crl files to the isolated
system.
From a command prompt, enter the following command to use the certutil
utility to update the local
certificate store with the CRL
downloaded in step 1:
certutil –addstore CA c:\CodeSignPCA.crl
The CRL files are updated regularly,
so you should consider setting a
reoccurring task of downloading and
installing the CRL updates. To view
the next update time, double-click the
.crl file and view the value of the
Next Update field.
Not sure but if the delay long enough that initial DNS lookups could be the culprit?
( how long is the delay verse a normal call? )
and/or
Is this URI internal to the Network / or a different internal network?
I have seen some weird networking delays from using load balance profiles inside a network that isn't setup right, the firewalls, load-balancers, and other network profiles might be "fighting" the initial connections...
I am not a great networking guy, but you might want to see what an SA has to say about this on serverfault.com as well...
good luck
There is always a delay the first time SQLCLR loads the necessary assemblies.
That should be the case not only for your function MakeWebRequest, but also for any .NET function in the SQLCLR.
HttpWebRequest is part of the System.Net assembly, which is not part of the supported libraries.
I'd recommend using the library System.Web.Services instead to make web service calls from inside the SQLCLR.
I have tested and my first cold run (after SQL service restart) was in 3 seconds (not 30 as yours), all others are in 0 sec.
The code sample I've used to build a DLL:
using System;
using System.Data;
using System.Net;
using System.IO;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
namespace MySQLCLR
{
public static class WebRequests
{
public static void MakeWebRequest(string address, string parameters, int connectTO)
{
string returnData;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(String.Concat(address.ToString(), "?", parameters.ToString()));
request.Timeout = (int)connectTO;
request.Method = "GET";
using (WebResponse response = request.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(responseStream))
{
returnData = reader.ReadToEnd();
reader.Close();
}
responseStream.Close();
}
response.Close();
}
SqlDataRecord rec = new SqlDataRecord(new SqlMetaData[] { new SqlMetaData("response", SqlDbType.NVarChar, 10000000) });
rec.SetValue(0, returnData);
SqlContext.Pipe.Send(rec);
}
}
}