0

I am trying to make a GET request to VISA api from .NET Core and I am getting the following error message in WebException

The credentials supplied to the package were not recognized visa

VISA APIs support two-way SSL connection and for that a set of private key and certificate are issued by visa along with a pair of userid and password.

I created a p12 certificate file using following command as per the VISA docoumentation.

openssl pkcs12 -export -in cert.pem -inkey "privateKey.pem" 
    -certfile cert.pem -out myProject_keyAndCertBundle.p12

I then wrote following code to make a request to VISA API. I am running this code as part of a .NET Core console application.

public string MakeHelloWorldCall()
{
    string requestURL = "https://sandbox.api.visa.com/vdp/helloworld";

    string userId = ConfigurationManager.AppSettings["userId"];
    string password = ConfigurationManager.AppSettings["password"];
    string certificatePath = ConfigurationManager.AppSettings["cert"];
    string certificatePassword = ConfigurationManager.AppSettings["certPassword"];
    string statusCode = "";

    HttpWebRequest request = WebRequest.Create(requestURL) as HttpWebRequest;
    request.Method = "GET";

    request.Headers["Authorization"] = GetBasicAuthHeader(userId, password);

    var certificate = new X509Certificate2(certificatePath, certificatePassword);
    request.ClientCertificates.Add(certificate);

    try
    {
        // Make the call
        // This is the line which throws the exception.
        using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
        {
            statusCode = response.StatusCode.ToString();
        }
    }
    catch (WebException e)
    {
        Console.WriteLine(e.Message);
        Exception ex = e.InnerException;

        while(ex != null)
        {
            Console.WriteLine(ex.Message);
            ex = ex.InnerException;
        }

        if (e.Response is HttpWebResponse)
        {
            HttpWebResponse response = (HttpWebResponse)e.Response;
            statusCode = response.StatusCode.ToString();
        }
    }

    return statusCode;
}

private string GetBasicAuthHeader(string userId, string password)
{
    string authString = userId + ":" + password;
    var authStringBytes = Encoding.UTF8.GetBytes(authString);
    string authHeaderString = Convert.ToBase64String(authStringBytes);
    return "Basic " + authHeaderString;
}

I see following output in Console.

The SSL connection could not be established, see inner exception. The credentials supplied to the package were not recognized
The SSL connection could not be established, see inner exception.
The credentials supplied to the package were not recognized

So the root error is The credentials supplied to the package were not recognized.

I am pretty much stuck here as I am not sure what is causing this error.

I tried searching about this error on Google and SO. But most of the posts suspects insufficient privilege to access the certificate. But I am running Visual Studio with Administrator User account and also I am using certificate file not the certificate installed on the machine.

I guess anyone with experience with Two-Way SSL Connection and/or VISA APIs would be able to understand what exact reason behind this error.

EDIT: I am able to call the API successfully from SOAP UI where I configured the same .p12 certificate and Authorization header.

Braiam
  • 1
  • 11
  • 47
  • 78
Chetan
  • 6,711
  • 3
  • 22
  • 32
  • 1
    Protip: You should be using `HttpClient` instead of `WebRequest`. – Fred Jul 01 '19 at 13:22
  • Thanks @Fred... Let me give it a try... – Chetan Jul 01 '19 at 13:26
  • @Fred I tried HttpClient too... but that didn't solve the problem. I am still getting the same error `The credentials supplied to the package were not recognized` – Chetan Jul 01 '19 at 13:39
  • @Fred HttpClient was a good idea.. I was able to solve the issue with HttpClient and one other change... – Chetan Jul 01 '19 at 14:06

3 Answers3

1

Finally, I was able to solve this issue.

Following two thing I did to come to the resolution.

  1. Use HttpClient instead of HttpWebRequest to make API request. Thanks to @Fred.
  2. Instead of using certificate file, I installed the certificate on the machine under Current User Certificate store.

Following is the working code.

public string MakeHelloWorldCallUsingHttpClient()
{
    string requestURL = "https://sandbox.api.visa.com/vdp/helloworld";

    string userId = ConfigurationManager.AppSettings["userId"];
    string password = ConfigurationManager.AppSettings["password"];
    string apiResponse = "";

    HttpClientHandler clientHandler = new HttpClientHandler();

    HttpClient httpClient = new HttpClient(clientHandler);
    httpClient.DefaultRequestHeaders.Authorization 
            = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", GetBasicAuthHeaderValue(userId, password));
    try
    {
        // Make the call
        var response = httpClient.GetAsync(requestURL).Result;

        if (response.StatusCode == HttpStatusCode.OK)
        {
            apiResponse = response.Content.ReadAsStringAsync().Result;
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
        Exception ex = e.InnerException;
        while (ex != null)
        {
            Console.WriteLine(ex.Message);
            ex = ex.InnerException;
        }
    }
    return apiResponse;
}

private string GetBasicAuthHeaderValue(string userId, string password)
{
    string authString = userId + ":" + password;
    var authStringBytes = Encoding.UTF8.GetBytes(authString);
    return Convert.ToBase64String(authStringBytes);
}

Following is the output I am getting now from the API.

{"timestamp":"2019-07-01T14:02:22","message":"helloworld"}

Following line of code makes sure to locate the client certificates from the Current User's certificate store.

clientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;

Setting above ClientCertificateOptions to ClientCertificateOption.Manual would require certificate to be added manually to clientHandler.ClientCertificates as following.

var certificate = new X509Certificate2(certificatePath, certificatePassword);
clientHandler.ClientCertificates.Add(certificate);

This would cause the same error as I am currently facing.

This makes me think that it is in best interest to let the Framework locate the client certificate.

Chetan
  • 6,711
  • 3
  • 22
  • 32
  • Now that you use `HttpClient` you should no longer use catch `WebException`. – Fred Jul 01 '19 at 18:41
  • You should not catch the base exception it a bad practice. You should catch a more specific exception class. Perhaps `HttpRequestException`. – Fred Jul 03 '19 at 13:39
  • I had to set the Protocol to use TLS 1.2 also. `ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;` – cderrick Apr 28 '20 at 23:16
0

Do you use full IIS? Make sure that your Application Pool has access to this file

How to give the permission

But it's difficult to debug stuff you don't have access.

Please check this answer

M. Artem
  • 681
  • 7
  • 8
  • No I am not using IIS. I am using Console Application.... I will not be calling VISA API from IIS in my application. It will be from a background service... – Chetan Jul 01 '19 at 13:32
0

I faced this same issue and finally was able to find someone who had posted the solution on the Visa community developer site

https://community.developer.visa.com/t5/Two-way-SSL-X-Pay-Token/NET-Core-3-1-The-credentials-supplied-to-the-package-were-not/td-p/14240

The gist of the solution being that for C# instead of using this command to generate the key

openssl pkcs12 -export -in cert.pem -inkey "privateKey.pem" -certfile cert.pem -out myProject_keyAndCertBundle.p12

You should exculde the "-certfile" parameter and use the following

openssl pkcs12 -export -in cert.pem -inkey "privateKey.pem" -out myProject_keyAndCertBundle.p12 

In my C# project this solved my issue and I was able to still load the key from a file and not use the Certificate store.

Lee Baker
  • 562
  • 1
  • 3
  • 12