I'm using this course which is very interesting but I have some problem.
http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api
I get certificate error but I understand why is this problem. This certificate should be connected to name of my computer. I don`t understand how he biuld this certificate and how edit this issue.
Here is important code. What I have to do to solve this problem with certificate name?
using System;
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace LocalAccountsApp.Filters
{
public class RequireHttpsAttribute : AuthorizationFilterAttribute
{
public int Port { get; set; }
public RequireHttpsAttribute()
{
Port = 443;
}
public override void OnAuthorization(HttpActionContext actionContext)
{
var request = actionContext.Request;
if (request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
var response = new HttpResponseMessage();
if (request.Method == HttpMethod.Get || request.Method == HttpMethod.Head)
{
var uri = new UriBuilder(request.RequestUri);
uri.Scheme = Uri.UriSchemeHttps;
uri.Port = this.Port;
response.StatusCode = HttpStatusCode.Found;
response.Headers.Location = uri.Uri;
}
else
{
response.StatusCode = HttpStatusCode.Forbidden;
}
actionContext.Response = response;
}
else
{
base.OnAuthorization(actionContext);
}
}
}
}
That error is correct. The certificate you're using was signed by itself, for the domain localhost. Since it's not signed by a trusted certificate root (such as Verisign, for example), your browser warns you that the certificate is not valid and therefore the site may be illegitimate.
The error should not be causing problems with functionality, and when you deploy to production, you should get a trusted certificate signed for the domain you're deploying to.
You're not going to get anyone to sign a certificate for the domain localhost, but if the error bothers you, you can add the certificate you're using to the list of trusted root certificates by using MMC, as described in this TechNet article.
Related
I have a app that uses boiler plate code from aws ses docs https://docs.aws.amazon.com/ses/latest/dg/send-an-email-using-sdk-programmatically.html. This is my EmailSender.cs file:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Amazon;
using Amazon.Runtime;
using Amazon.SimpleEmail;
using Amazon.SimpleEmail.Model;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
namespace amaranth.Helpers
{
public class EmailSender : IEmailSender
{
private ApiEndpoints _endpoints;
public EmailSender(IOptions<ApiEndpoints> _options)
{
_endpoints = _options.Value;
}
public async Task SendEmailAsync(string email, string subject, string htmlMessage)
{
using (var client = new AmazonSimpleEmailServiceClient(RegionEndpoint.USEast1))
{
var sendRequest = new SendEmailRequest
{
Source = "<A DOMAIN THAT I DO OWN>",
Destination = new Destination
{
ToAddresses =
new List<string> { email }
},
Message = new Message
{
Subject = new Content(subject),
Body = new Body
{
Html = new Content
{
Charset = "UTF-8",
Data = htmlMessage
}
}
},
// If you are not using a configuration set, comment
// or remove the following line
//ConfigurationSetName = configSet
};
try
{
Console.WriteLine("Sending email using Amazon SES...");
var response = await client.SendEmailAsync(sendRequest);
Console.WriteLine("The email was sent successfully.");
}
catch (Exception ex)
{
Console.WriteLine("The email was not sent.");
Console.WriteLine("Error message: " + ex.Message);
}
}
}
}
}
My variables email, subject and htmlMessage are all properly formatted. Also I verified the domain (represented with <A DOMAIN THAT I DO OWN> in the code). See here:
I am also out of the sandbox and I even created an IAM user to handle email:
But when I run my app, the email isn't sent and I get this error: Error message: Unable to get IAM security credentials from EC2 Instance Metadata Service.
I am not sure what the problem is but I noticed that I'm not really declaring my credentials anywhere. But... I don't even know where I would put the credentials. The amazon ses docs don't really appear to specify where the credentials go. Can somebody show me where I'm going wrong and where my credentials go?
UPDATE:
I added a BasicAWSCredentials where _endpoints.EmailUsername is my "Access key ID" and _endpoints.EmailPassword" is my Secret Access Key for the user I created specifically for this app.
var awsCreds = new BasicAWSCredentials(_endpoints.EmailUsername, _endpoints.EmailPassword);
using (var client = new AmazonSimpleEmailServiceClient(awsCreds, RegionEndpoint.USEast1))
But I got this error:
Error message: The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
I was able to get this working. It was a strange fix. First I added my credentials to the code:
AWSCredentials awsCreds = new BasicAWSCredentials(_endpoints.EmailUsername, _endpoints.EmailPassword);
using (var client = new AmazonSimpleEmailServiceClient(awsCreds, RegionEndpoint.USEast2))
But then I got this error: The request signature we calculated does not match the signature you provided. This was the difficult error to get around. I tried a lot of stuff but then I came across this post https://adithya.dev/aws-signaturedoesnotmatch-error/. I generated another credential for my aws user that had a + closer to the end of the name and then it worked perfectly. I think this is a weird bug in the C# (and possibly other languages) AWS code.
I am seeing an issue with sending an http request to an SSL enabled API.
The error message i get back is -
AuthenticationException: The remote certificate is invalid according to the validation procedure.
based on this request
using (HttpResponseMessage res = client.GetAsync("https://example.com").Result)
{
using (HttpContent content = res.Content)
{
string data = content.ReadAsStringAsync().Result;
if (data != null)
{
Console.WriteLine(data);
}
else
{
Console.WriteLine("Nothing returned");
}
}
}
I've been given a .pem file to verify that the certificate that is being sent back is signed by our CA and having some trouble figuring out how to do that in C#
In python I'm able to resolve the certificate errors by passing the .pem file to the verify parameter e.g.
r = requests.post(url="https://example.com", headers=headers, verify='mypem.pem')
Is there something equivalent in Dotnet Core 3's HttpClient?
Thanks for any assistance!
If you can't set up the cert as trusted for whatever reason, then you can bypass the certificate validation and verify the server yourself. It's much less elegant in .NET unfortunately, and this may not work on all platforms. Refer to this answer on bypass invalid SSL certificate in .net core for more discussion on that.
using (var httpClientHandler = new HttpClientHandler())
{
// Override server certificate validation.
httpClientHandler.ServerCertificateCustomValidationCallback = VerifyServerCertificate;
// ^ if this throws PlatformNotSupportedException (on iOS?), then you have to use
//httpClientHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
// ^ docs: https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler.dangerousacceptanyservercertificatevalidator?view=netcore-3.0
using (var client = new HttpClient(httpClientHandler))
{
// Make your request...
}
}
I think this implementation of the callback does what you need, "pinning" the CA. From this answer to Force HttpClient to trust single Certificate, with more comments from me. EDIT: That answer's status checking wasn't working, but per this answer linked by Jeremy Farmer, the following approach should:
static bool VerifyServerCertificate(HttpRequestMessage sender, X509Certificate2 certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
try
{
// Possibly required for iOS? :
//if (chain.ChainElements.Count == 0) chain.Build(certificate);
// https://forums.xamarin.com/discussion/180066/httpclienthandler-servercertificatecustomvalidationcallback-receives-empty-certchain
// ^ Sorry that thread is such a mess! But please check it.
// Without having your PEM I am not sure if this approach to loading the cert works, but there are other ways. From the doc:
// "This constructor creates a new X509Certificate2 object using a certificate file name. It supports binary (DER) encoding or Base64 encoding."
X509Certificate2 ca = new X509Certificate2("mypem.pem");
X509Chain chain2 = new X509Chain();
chain2.ChainPolicy.ExtraStore.Add(ca);
// "tell the X509Chain class that I do trust this root certs and it should check just the certs in the chain and nothing else"
chain2.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
// This setup does not have revocation information
chain2.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
// Build the chain and verify
var isValid = chain2.Build(certificate);
var chainRoot = chain2.ChainElements[chain2.ChainElements.Count - 1].Certificate;
isValid = isValid && chainRoot.RawData.SequenceEqual(ca.RawData);
Debug.Assert(isValid == true);
return isValid;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return false;
}
Sorry I can't test this at the moment, but hope it helps a bit.
I'm trying to implement server authentication in C# (using .NET appln). I would like to achieve the following :
Connect to HTTPS URL using
String sslServerHost = "https://mail.google.com";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sslServerHost);
If HTTPS request fails, I would be adding the server certificate to the local windows certification store.
X509Certificate cert = request.ServicePoint.Certificate;
//convert the X509Certificate to an X509Certificate2 object by passing it into the constructor
X509Certificate2 cert2 = new X509Certificate2(cert);
X509Store userCaStore = new X509Store(storeName: StoreName.Root, storeLocation: StoreLocation.CurrentUser);
// Code to import server certifictes to windows store.
userCaStore.Open(OpenFlags.ReadOnly);
userCaStore.Add(cert2);
The below is the C# code that I have used for performing server authentication.
As evident from the below code snippet, I have NEITHER ignored the certificate validation NOR added the X509Certificate to the local trust store, but still I was able to establish connection to the HTTPS URL ( WebRequest.create(url) and request.GetResponse() doesn't throw any exceptions )
String sslServerHost = "https://mail.google.com";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sslServerHost);
request.AllowAutoRedirect = false;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
try
{
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
Debug.WriteLine(responseFromServer);
Console.WriteLine(responseFromServer);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Why is it that I'm not getting an exception when trying to access the HTTPS URL given that I have NOT added the server certificates (X509Certificate) to the local windows store.
In short, how to achieve or implement server authentication in C# where if server certificate were not added then the C# code should throw an exception.
For Java, there is a good link https://github.com/escline/InstallCert/blob/master/InstallCert.java which best describes the server authentication mechanism where if client is trying to access an HTTPS Server and if server's certificate is not present in the Java trust store, then JVM throws an exception.
This mechanism doesn't seem to hold good for .NET applications. Any help or insight would be appreciated !!
The reason is probably that mail.google.com is already trusted, since the certificate chain leads up to some root certificate which IS already in your certificate store (in "Trusted Root Certification Authorities").
If you want to test failure, follow a tutorial to create your own CA and certificates using something like OpenSSL. Then set up a web site in IIS with this untrusted certificate.
According with the web request official documentation of the web request The certificate could be installed in My certificate store of the current user. That might explain why it nevers throws an error. You can try to connect to a server without adding the certificate to the cert store and see if that is throwing an error.
The solutions I have found to solve this problem involve setting a callback on ServicePointManager.ServerCertificateValidationCallback
ServicePointManager.ServerCertificateValidationCallback =
MyRemoteCertificateValidationCallback;
public bool MyRemoteCertificateValidationCallback(System.Object sender,
X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
bool isOk = true;
// If there are errors in the certificate chain, look at each error to determine
the cause.
if (sslPolicyErrors != SslPolicyErrors.None) {
for(int i=0; i<chain.ChainStatus.Length; i++) {
if(chain.ChainStatus[i].Status !=
X509ChainStatusFlags.RevocationStatusUnknown) {
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 1, 0);
chain.ChainPolicy.VerificationFlags =
X509VerificationFlags.AllFlags;
bool chainIsValid = chain.Build((X509Certificate2)certificate);
if(!chainIsValid) {
isOk = false;
}
}
}
} else {
X509Certificate2 cert3 = new X509Certificate2(certificate);
bool verify = cert3.Verify();
var cert1 = new X509Certificate2(certificate);
if (cert1.NotAfter <= DateTime.Now)
{
return false;
}
}
return isOk;
}
I am facing an issue while trying to consume a WCF web service which requires mutual authentication and message signing using X509 certificate. I have already implemented mutual authentication using X509 certificate, but I am facing an issue while trying to implement message signing. I have successfully installed certificates on my machine. the error message I am getting is:
Signature verification failed
Please note I have successfully tested this application in SoapUI. But I am facing issue while trying to implement the same in C#.
My code:
public override void SecureMessage(SoapEnvelope envelope, Security security)
{
// Get an X.509 certificate for signing the SOAP message.
X509SecurityToken signatureToken = GetSecurityToken("subjectname");
if (signatureToken == null)
{
throw new SecurityFault("Message Requirements could not be satisfied.");
}
// Add the X.509 certificate to the header.
security.Tokens.Add(signatureToken);
// Specify that the SOAP message is signed using this X.509
// certificate.
MessageSignature sig = new MessageSignature(signatureToken);
security.Elements.Add(sig);
}
public X509SecurityToken GetSecurityToken(string subjectName)
{
X509SecurityToken objX509SecurityToken = null;
X509Store objX509Store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
objX509Store.Open(OpenFlags.ReadOnly);
try
{
X509Certificate2Collection objX509Certificate2Collection = objX509Store.Certificates.Find(X509FindType.FindBySubjectName, subjectName, true);
X509Certificate2 objX509Certificate2;
if (objX509Certificate2Collection.Count == 1)
{
objX509Certificate2 = objX509Certificate2Collection[0];
objX509SecurityToken = new X509SecurityToken(objX509Certificate2);
}
else
{
objX509SecurityToken = null;
}
}
catch (Exception ex)
{
objX509SecurityToken = null;
}
finally
{
if (objX509Store != null)
objX509Store.Close();
}
return objX509SecurityToken;
}
I am using FileZilla as the server and a DNS service, so that I wouldn't have to use my local machine IP (but I've tried the following methods on both).
After trying System.Net.FtpWebRequest to work, I've read around (including a few posts on SO) and found out that the SSL support is not very adequate with that library. It was working with regular FTP, but when I tried forcing SSL, I was getting a certificate validation error saying: The remote certificate is invalid according to the validation procedure.
So, I've done some searching around and found Alex FTPS Client library. Here's the code I wrote up:
class FTPSWorker
{
public static void UploadFile(string sourceFile, string targetFile, string ftpIP, string ftpUser, string ftpPass)
{
try
{
using (FTPSClient client = new FTPSClient())
{
client.Connect(ftpIP, new NetworkCredential(ftpUser, ftpPass),
ESSLSupportMode.CredentialsRequired | ESSLSupportMode.DataChannelRequested);
client.SetTransferMode(ETransferMode.Binary);
client.PutFile(sourceFile, targetFile);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Unfortunately, I was getting the same exact certificate error. I can, however, access the FTP server perfectly fine using FileZilla client. So, I figured there would have to be a certificate issue.
I should note that my server was showing the following log entries:
Welcome Message
AUTH TLS
234 Using authentication type TLS
SSL connection established
disconnected
While the client (C# WPF application) was getting this error:
The remote certificate is invalid according to the validation procedure.
This is absolutely exact same error if I use the .NET library and MSDN code.
I've done more research and found solutions similar to these:
The remote certificate is invalid according to the validation procedure
"The remote certificate is invalid according to the validation procedure." using Gmail SMTP server
But they just seem like risky hacks... And while they do work, is there a way to have certification information to appear and maybe have user validate it/install it besides the basic Yes/No that it's currently using?
My code right now (I ditched Alex's library and went back to default .NET):
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(FTPWorker.ValidateServerCertificate);
public class FTPWorker
{
public static void UploadFile(string sourceFile, string targetFile, string ftpIP, string ftpUser, string ftpPass)
{
try
{
string filename = "ftp://" + ftpIP + "/test/" + targetFile;
FtpWebRequest ftpReq = (FtpWebRequest)WebRequest.Create(filename);
ftpReq.Method = WebRequestMethods.Ftp.UploadFile;
ftpReq.Credentials = new NetworkCredential(ftpUser, ftpPass);
ftpReq.UsePassive = true;
ftpReq.EnableSsl = true;
ftpReq.UseBinary = true;
ftpReq.KeepAlive = false;
byte[] b = File.ReadAllBytes(sourceFile);
ftpReq.ContentLength = b.Length;
using (Stream s = ftpReq.GetRequestStream())
{
s.Write(b, 0, b.Length);
}
FtpWebResponse ftpResp = (FtpWebResponse)ftpReq.GetResponse();
if (ftpResp != null)
{
MessageBox.Show(ftpResp.StatusDescription);
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
else
{
if (System.Windows.Forms.MessageBox.Show("The server certificate is not valid.\nAccept?",
"Certificate Validation", System.Windows.Forms.MessageBoxButtons.YesNo,
System.Windows.Forms.MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
return true;
else
return false;
}
}
}
So, for anyone that had the same issue, I ended up just giving the user a warning regarding the certificate and an option to accept or deny based on the link I provided in my original post. In order for a certificate to be validated, it has to be real and not a locally created one. So, that's the only workaround there is for now.
The Alex ftps will do the same certificate validation if you specify it to.
In your client.connect add the remotecertificatevalidationcallback to accept the certificate
client.Connect(ftpIP, new NetworkCredential(ftpUser, ftpPass),
ESSLSupportMode.CredentialsRequired | ESSLSupportMode.DataChannelRequested,
new RemoteCertificateValidationCallback(ValidateTestServerCertificate));
Then below.
private static bool ValidateTestServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// Accept any certificate
return true;
}
I wanted to use the default .net. but I'm stuck connecting to a server that's using implicit. :(