How I can use SSL (CA) certificate (.pem file) in a Groovy script while using http-builder for making API calls.
I am looking for something like the line below but sslContext is not available in http-builder:0.7.2
http.client.sslContext = sslContext
I have tried the below code
def health() {
HTTPBuilder http = new HTTPBuilder(baseURL)
def sslContext = SSLContext.getInstance('TLS')
sslContext.init(null, [certificate] as TrustManager[], null)
**http.client.sslContext = sslContext**
http.get(path: "/health") { req ->
response.success = { resp, json ->
println "Success! ${resp.status}"
}
response.failure = { resp, json ->
println "Request failed with status ${resp.status}"
}
}
}
Expectation:
HttpBuilder should take SSL certificate and make an API call (GET) to fetch response from the server.
Related
Good evening everyone,
I'm trying to do do SSL Pinning for my app and the Lets Encrypt certificate (ISRG Root X1) is returning CertPathValidatorException: Trust anchor for certification path not found.
I'm on Android Studios using Kotlin, using Fuel for the web requests, here is the code I have
fun pinning(ctx: Context): Pair<SSLSocketFactory, KeyStore> {
val cf: CertificateFactory = CertificateFactory.getInstance("X.509")
val caInput: InputStream = BufferedInputStream(ctx.resources.openRawResource(R.raw.isrgrootx1))
val ca: X509Certificate = caInput.use {
cf.generateCertificate(it) as X509Certificate
}
Log.println(Log.DEBUG, "SYSTEM-CA", ca.subjectDN.toString())
// Create a KeyStore containing our trusted CAs
val keyStoreType = KeyStore.getDefaultType()
val keyStore = KeyStore.getInstance(keyStoreType).apply {
load(null, null)
setCertificateEntry("ca", ca)
}
// Create a TrustManager that trusts the CAs inputStream our KeyStore
val tmfAlgorithm: String = TrustManagerFactory.getDefaultAlgorithm()
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm).apply {
init(keyStore)
}
// Create an SSLContext that uses our TrustManager
val context: SSLContext = SSLContext.getInstance("TLS").apply {
init(null, tmf.trustManagers, null)
}
return Pair(context.socketFactory, keyStore)
}
val (pin, key) = pinning(this)
FuelManager.instance.socketFactory = pin
FuelManager.instance.keystore = key
Log.println(Log.INFO, "FUEL-MANAGER", "Imported instances successfully")
The FuelManager is run as soon as the app starts (override fun onStart())
How to Bypass SSL Certificate Verification in flutter?
Error: Handshake Exception: Handshake error in client(OS Error:CERTIFICATE_VERIFY_FAILED:self signed certificate(handshake.cc:345)
You need to configure your HttpService to work with Self-Signed SSL local servers. Like this:
import 'dart:io';
import 'dart:convert';
class HttpService {
Future<dynamic> sendRequestToServer(dynamic model, String reqType, bool isTokenHeader, String token) async {
HttpClient client = new HttpClient();
client.badCertificateCallback =((X509Certificate cert, String host, int port) => true);
HttpClientRequest request = await client.postUrl(Uri.parse("https://${serverConstants.serverUrl}$reqType"));
request.headers.set('Content-Type', 'application/json');
if(isTokenHeader){
request.headers.set('Authorization', 'Bearer $token');
}
request.add(utf8.encode(jsonEncode(model)));
HttpClientResponse result = await request.close();
if(result.statusCode == 200) {
return jsonDecode(await result.transform(utf8.decoder)
.join());
} else {
return null;
}
}
}
Read more from here.
It seems that you are using a self signed certificate, which is not trusted by the OS. You can set it as trusted following these steps:
Create a class that overrides HttpOverrides in the following way:
class MyHttpOverrides extends HttpOverrides {
#override
HttpClient createHttpClient(SecurityContext context) {
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port) {
//add your certificate verification logic here
return true;
};
}
}
Then, in your main method, instance your class and set it as the global HttpOverride:
HttpOverrides.global = new DevHttpOverrides();
If badCertificateCallback returns true it will accept all bad certificates; if returns false it will reject a bad certificate.
link. https://stackoverflow.com/a/66268556/11738366
I am having a very weird issue with StackExchange.Redis to connect with Redis.
I have enabled SSL on Redis database and I am not able to connect from client to Redis server with SSL certificate with below code.
static RedisConnectionFactory()
{
try
{
string connectionString = "rediscluster:13184";
var options = ConfigurationOptions.Parse(connectionString);
options.Password = "PASSWORD";
options.AllowAdmin = true;
options.AbortOnConnectFail = false;
options.Ssl = true;
options.SslHost = "HOSTNAME";
var certificate = GetCertificateFromThubprint();
options.CertificateSelection += delegate
{
return certificate;
};
Connection = new Lazy<ConnectionMultiplexer>(
() => ConnectionMultiplexer.Connect(options)
);
}
catch (Exception ex)
{
throw new Exception("Unable to connect to Cache Server " + ex);
}
}
public static ConnectionMultiplexer GetConnection() => Connection.Value;
public static IEnumerable<RedisKey> GetCacheKeys()
{
return GetConnection().GetServer("rediscluster", 13184).Keys();
}
// Find certificate based on Thumbprint
private static X509Certificate2 GetCertificateFromThubprint()
{
// Find certificate from "certificate store" based on thumbprint and return
StoreName CertStoreName = StoreName.Root;
string PFXThumbPrint = "NUMBER";
X509Store certLocalMachineStore = new X509Store(CertStoreName, StoreLocation.LocalMachine);
certLocalMachineStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certLocalMachineCollection = certLocalMachineStore.Certificates.Find(
X509FindType.FindByThumbprint, PFXThumbPrint, true);
certLocalMachineStore.Close();
return certLocalMachineCollection[0];
}
However, If I create a console application and connect to Redis with above code then I am able to connect, but If I used same code from my web application to connect to redis then I am not able to connect.
Not sure if I am missing something.
Also, I went through "mgravell" post
In that post he has configured "CertificateValidation" method, In my scenario I want Redis to validate SSL certificate. so I have not implementation validation. And implemented "CertificateSelection" method to provide client certificate.
You can try to validate the cert using CertificateValidation. I tried the following code and it worked for me:
options.CertificateValidation += ValidateServerCertificate;
...
public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
return false;
}
In cases like this where you are using a client certificate and it works in a console app but does not work for some other application (you don't say but I guess from an IIS hosted web app), it almost always has to do with whether the account has permission to access the private key.
The console app runs with your account which probably has access to the private key.
To give an account access
open the Local Computer certificate store
find your client certificate
right click and choose "All tasks -> Manage Provate Keys..."
click "Add..." and add the account.
Note: if your adding an IIS App Pool account the format is:
IIS APPPOOL<my app pool name>
Location should be the local machine and not a domain.
I was able to ssl the Redis server I had started on a VM with the following codes.
add stackexchange.redis visual studio
try
{
ConfigurationOptions configurationOptions = new ConfigurationOptions
{
KeepAlive = 0,
AllowAdmin = true,
EndPoints = { { "SERVER IP ADDRESS", 6379 }, { "127.0.0.1", 6379 } },
ConnectTimeout = 5000,
ConnectRetry = 5,
SyncTimeout = 5000,
AbortOnConnectFail = false,
};
configurationOptions.CertificateSelection += delegate
{
var cert = new X509Certificate2("PFX FILE PATH", "");
return cert;
};
ConnectionMultiplexer connection =
ConnectionMultiplexer.Connect(configurationOptions);
IDatabase databaseCache = connection.GetDatabase();
//set value
databaseCache.StringSet("KEYNAME", "KEYVALUE");
//get Value
label_show_value.Text = databaseCache.StringGet("KEYNAME").ToString();
}
catch (Exception e1)
{
}
I'm trying to make a call to a third-party API which requires a client certificate. I generated the client certificate using the SSL tool and
uploaded this to the third party site. I have generated a successful POST request through Postman, providing the client certificate through
their dialogs.
The Headers are:
X-application = (MyApplicationName)
Content-Type = application/x-www-form-urlencoded
Accept = application/json
Body (x-www-form-urlencoded)
UserName = (username)
Password = (password)
When I perform a similar request through .NET I am receiving an error code indicating the certificate is not present. I have added the certificate to my personal certificate store and verified
the certificate has been added to the webhandler through debugging.
Can anyone suggest what the error might be or how I could diagnose the issue?
static async void LaunchRawHttpClient()
{
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12 | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls;
ServicePointManager.ServerCertificateValidationCallback +=
ValidateServerCertificate;
string page = "https://<URL>";
var handler = new WebRequestHandler();
X509Certificate2 cert = GetMyCert();
if (cert!= null)
{
handler.ClientCertificates.Add(cert);
}
else
{
Console.WriteLine("Cert not found");
Console.ReadLine();
return;
}
// ... Use HttpClient.
using (HttpClient client = new HttpClient(handler))
{
client.DefaultRequestHeaders.Add("X-Application", "<applicationname>");
client.DefaultRequestHeaders.Add("Accept", "application/json");
var nvc = new List<KeyValuePair<string, string>>();
nvc.Add(new KeyValuePair<string, string>("username", "<username>"));
nvc.Add(new KeyValuePair<string, string>("password", "<password>"));
FormUrlEncodedContent reqContent = new FormUrlEncodedContent(nvc);
reqContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded");
using (HttpResponseMessage response = await client.PostAsync(page, reqContent))
using (HttpContent content = response.Content)
{
// ... Read the string.
string result = await content.ReadAsStringAsync();
// ... Display the result.
if (result != null)
{
Console.WriteLine(result);
}
}
}
}
static X509Certificate2 GetMyCert()
{
string certThumbprint = "<thumbprint>";
X509Certificate2 cert = null;
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = store.Certificates.Find
(X509FindType.FindByThumbprint, certThumbprint, false);
if (certCollection.Count > 0)
cert = certCollection[0];
store.Close();
return cert;
}
public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
{
Console.WriteLine("No SSL Errors");
return true;
}
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
Console.ReadLine();
return false;
}
I receive "No SSL Errors" message x2, followed by the missing certificate status code.
Thanks in advance
Jim
Finally found the answer on this - the problem was the private key file was not being loaded. Postman sent requests successfully, as did Curl. Curl asks for the key file explicity and this was a clue.
In .NET Core - there's a function on the X509Certificate2 object which allows you to copy it to another object combined with the key file. My project is in .NET framework and Core wasn't available.
The option I went for was using openssl to combine the cer and the key file into a pfx, which I loaded into the X509Certificate2 object. The Http Post then succeeded.
I am trying to connect a target host, which requires a username and password for primitive authentication from a proxy server that also have its own host, username and password.
Updated
The use case is something like this
My System connects with internet using proxy assume proxy.mydomain.com
And my proxy uses authentication to connect and authenticatuion is done using username and password
Username : MyDomain\myusername
Password : Password
Ok now the API or service which I want to connect is using Preemptive authentication I have some code snippet form API Docs :
final HttpState state = client.getState();
final HttpClientParams params = client.getParams();
myCreds = new UsernamePasswordCredentials(userName, userPassword);
System.out.println("Created credentials for " + myCreds.getUserName());
// Apply the credentials to the httpclient for preemptive authentication.
params.setAuthenticationPreemptive(true);
state.setCredentials(AuthScope.ANY, myCreds);
I tried with all apche clients from 4.1 to 4.3 but no solution at all.
I can set one credential but not getting how can both set together to a HttpClient ?
Below is my Code :
CloseableHttpResponse response;
HttpHost myHost=new HttpHost("172.17.8.192", 443, "https");
CredentialsProvider myServerCredsProvider;
if (userName.length() > 0 && userPassword.length() > 0)
{
myServerCredsProvider = new BasicCredentialsProvider();
myServerCredentials = new UsernamePasswordCredentials(userName, userPassword);
logger.info("Username : "+userName+" Password : "+userPassword);
myServerCredsProvider.setCredentials(
new AuthScope(myHost.getHostName(),myHost.getPort()),
myServerCredentials);
logger.info("After Creds Provider");
client = HttpClients.custom()
.setDefaultCredentialsProvider(myServerCredsProvider).build();
}
HttpHost proxy = new HttpHost("proxy.mycompany.com", 8080);
UsernamePasswordCredentials poxyAuthDetails=new UsernamePasswordCredentials("MyDomain//username", "password");
CredentialsProvider proxyProvider=new BasicCredentialsProvider();
proxyProvider.setCredentials(new AuthScope(proxy.getHostName(),proxy.getPort()), poxyAuthDetails);
RequestConfig config = RequestConfig.custom()
.setProxy(proxy)
.build();
HttpGet getServerDetailsUrl=new HttpGet(BaseURI+DominoServerAddress+API_BASE_URI);
getServerDetailsUrl.setConfig(config);
try {
response=client.execute(getServerDetailsUrl);
try {
HttpEntity entity = response.getEntity();
logger.info("----------------------------------------");
logger.info(response.getStatusLine());
if (entity != null) {
logger.info("Response content length: " + entity.getContentLength());
}
EntityUtils.consume(entity);
} finally {
response.close();
}
} catch (IOException ex) {
isAuthenticate=false;
logger.info("Exception Occured "+ex.getMessage());
}
I am getting exception while trying to execute :
HTTP/1.1 407 Proxy Authorization Required