I'm writing to ask for an example of use of the active resource with certificate authentication.
The documentation found on http://apidock.com/rails/ActiveResource/Base does not provide examples.
I have two rails application that need to communicate: APP1 (https://app1.mauroapplications.com), APP2 (https://app2.mauroapplications.com)
I generated public/private keys for each application (RSA). APP2 has the public key of APP1 (app1.pem) and viceversa.
In my development environment I have a self signed certificate for SSL.
How do I have to configure a Model extending ActiveResource in APP2?
Many thanks in advance,
Mauro
The following code works:
class Person < ActiveResource::Base
self.site = 'https://api.people.com:3000/'
self.ssl_options = {
cert: OpenSSL::X509::Certificate.new(File.open('cert.pem')),
key: OpenSSL::PKey::RSA.new(File.open('key.pem')),
ca_file: 'ca.cert.pem',
verify_mode: OpenSSL::SSL::VERIFY_PEER
}
end
Where cert.pem is your client certificate, key.pem your client's private key and ca.cert.pem the CA certificate (for validation of the server certificate).
Related
I exposed a simple REST service with Apache Camel like Spring boot microservice, which creates a request to a service in https, using the netty4-http component.
public class RoutingTest extends RouteBuilder {
#Override
public void configure() throws Exception {
restConfiguration()
.host("localhost")
.port("8080");
rest().post("test")
.route()
.setBody(constant("message=Hello"))
.setHeader(Exchange.HTTP_METHOD, constant(HttpMethod.POST))
.setHeader(Exchange.CONTENT_TYPE, constant("application/x-www-form-urlencoded"))
.to("netty4-http:https://localhost/service/test");
}
}
When i call http://localhost:8080/test, I get 400 Bad Request error when the routing call https://localhost/service/test service.From the logs I read that the request arrives in HTTP instead HTTPS format and I don't understand why:
You're speaking plain HTTP to an SSL-enabled server port. Instead use
the HTTPS scheme to access this URL, please.
If I invoke the service https://localhost/service/test with Postman, it works correctly.
SSL is configured with a Self-signed certificate.
How do I create a correct https request with the netty component in apache camel? The documentation only suggests the replacement of the protocol, at most a few options which however do not work.
UPDATE (SOLVED SEE BELOW)
I updated the call in this way
.to("netty4-http:https://localhost/dpm/idp/oauth/token?ssl=true&sslContextParameters=#sslContextParameters");
The ssl = true parameter is mandatory and I have also configured the bean for SSLContextParameters like this:
#Bean(name = "sslContextParameters")
public static SSLContextParameters sslParameters() throws KeyManagementException, GeneralSecurityException, IOException {
KeyStoreParameters ksp = new KeyStoreParameters();
ksp.setResource("C:/myfolder/test.jks");
KeyManagersParameters kmp = new KeyManagersParameters();
kmp.setKeyStore(ksp);
kmp.setKeyPassword("jskPassword");
SSLContextParameters scp = new SSLContextParameters();
scp.setKeyManagers(kmp);
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(new TrustSelfSignedStrategy());
SSLContext sslcontext = builder.build();
scp.createSSLContext().setDefault(sslcontext);
return scp;
}
I am fighting a bit with the classes that are deprecated. For testing I leave only one method deprecated because I should work with inheritance.
If I understood correctly, I had to generate a JKS file for the trust zone, starting from my self-signed certificates (.crt and .key files). Once done, I added the instructions for the KeyStoreParameters with the password.
It is almost solved, but now I am getting this error when i execute the
PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
You probably need to configure a sslContextParameters object that you can use to configure the Netty component for SSL.
I am not sure about the parameter name. The docs say sslContextParameters, but I thought it was sslContextParametersRef.
.to("netty4-http:https://localhost/service/test?sslContextParametersRef=#sslConfig");
The #sslConfig means that Camel can get the object from the registry with the identifier sslConfig. So for example with Spring this would be a Spring managed Bean with ID sslConfig.
The Netty component (not http) also has a parameter ssl=true. No idea if this is also needed for Netty-http. So you will have to test a bit with these different parameters.
By the way the docs of the Netty component have an SSL example with context parameter configuration etc. Have a look at it.
Resolved. Some instructions needed for the self-signed certificate were missing.
Below is the complete bean.
#Bean(name = "sslContextParameters")
public static SSLContextParameters sslParameters() throws KeyManagementException, GeneralSecurityException, IOException {
KeyStoreParameters ksp = new KeyStoreParameters();
ksp.setResource("C:/myfolder/test.jks");
ksp.setPassword("jskPassword");
KeyManagersParameters kmp = new KeyManagersParameters();
kmp.setKeyStore(ksp);
kmp.setKeyPassword("jskPassword");
SSLContextParameters scp = new SSLContextParameters();
scp.setKeyManagers(kmp);
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLContext sslcontext = builder.build();
scp.createSSLContext().setDefault(sslcontext);
// Necessary for the the self-signed server certificate
TrustManagersParameters tmp = new TrustManagersParameters();
tmp.setKeyStore(ksp);
scp.setTrustManagers(tmp);
return scp;
}
As for the test.jks file, I created it with keytool, the tool supplied with the JDK for managing certificates (creation, export and import).
In my case having already created the certificate with OpenSSL, I had to create only the JKS (Java Keystore) file to be imported. For it is necessary to convert the certificate in the P12 file (it should be an archive) and finally in the JKS.
During the operations you will be asked to enter passwords for both files
- openssl pkcs12 -export -in test.crt -inkey test.key -out test.p12
- keytool -importkeystore -srckeystore test.p12 -destkeystore test.jks -srcstoretype pkcs12
- keytool -importkeystore -srckeystore test.jks -destkeystore test.jks -deststoretype pkcs12
here test is the name of my certificate file. The last operation is not mandatory but it is recommended by keytool itself in order to migrate the JKS format, proprietary format if I understand correctly, to the more common PKCS12 format.
The value jskPassword in the code is the password I set when creating the keystore.
I hope it will help.
I have a WCF client that is going to authenticate against some web service using a certificate issued by said service. At first my client used a https binding as below:
var httpsBinding = new BasicHttpsBinding();
httpsBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
httpsBinding.Security.Mode = BasicHttpsSecurityMode.Transport;
but this gave the following error:
InvalidOperationException: The client certificate is not provided.
Specify a client certificate in ClientCredentials.
I then added the following code to my client configuration:
this.ChannelFactory.Credentials.ClientCertificate.SetCertificate("test", System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
System.Security.Cryptography.X509Certificates.StoreName.My);
And now I get the error
System.InvalidOperationException: 'Cannot find the X.509 certificate
using the following search criteria: StoreName 'My', StoreLocation
'LocalMachine', FindType 'FindBySubjectDistinguishedName', FindValue
'test'.'
I am absolutely certain that the certificate is placed in the Personal folder on my Local Machine, but it still cannot find it. I have tried placing the certificate in various folders, renaming it, using the thumbprint for identification, but my application still can't find it. What could be the issue here?
I suggest you set up the certificate by using X509FindType.FindByThumbprint.
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
//client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(StoreLocation.LocalMachine, StoreName.Root, X509FindType.FindByThumbprint, "cbc81f77ed01a9784a12483030ccd497f01be71c");
client.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "9ee8be61d875bd6e1108c98b590386d0a489a9ca");
It corresponds to the below value.
In order to allow WCF service could access this local certificate, we usually add Everyone account to the management group of the certificate private key.
Besides, WCF service with authenticating the client with a certificate, this usually requires that we set up both the service certificate and the client certificate on the client-side.
Feel free to let me know if there is anything I can help with.
I have a .pem file which will successfully connect to my website via the --cert parameter of curl. I then converted that to a der file:
openssl x509 -inform PEM -outform DER -in client.pem -out cert.der
Then I loaded that cert.der into my project and I'm now trying to use that with Alamofire, following the example on their homepage:
let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
certificates: ServerTrustPolicy.certificatesInBundle(),
validateCertificateChain: true,
validateHost: true
)
let policyManager = ServerTrustPolicyManager(policies: ["my.domain.com" : serverTrustPolicy])
manager = Alamofire.Manager(configuration: configuration, serverTrustPolicyManager: policyManager)
manager.request(.GET, url, parameters: params, encoding: .URLEncodedInURL, headers: nil)
.authenticate(usingCredential: credential)
.validate()
.responseJSON {
When that runs though it just fails and I get a 'cancelled' as the error's localizedDescription, which is what Alamofire does when authentication fails.
What am I doing wrong?
The Alamofire cert pinning logic does not currently support this use case. It is only designed to handle cert and public key pinning, not client certificates used to authenticate with the server. This is something we could support in the future if this is a common use case.
With that said, I'm assuming in this case you are receiving a NSURLAuthenticationChallenge with a protection space that has an authentication method of type .NSURLAuthenticationMethodClientCertificate. In these cases, you need to evaluate the host of the challenge, then create an NSURLCredential using the credentialWithIdentity:certificates:persistence: API. By passing this credential off to the completion handler, the client certificate should be sent to the server to authenticate the connection. More info can be found here.
Client certificate authentication (NSURLAuthenticationMethodClientCertificate) requires the system identity and all certificates needed to authenticate with the server. Create an NSURLCredential object with credentialWithIdentity:certificates:persistence:.
I've never actually had a need to use this type of authentication before. You'll need to override the auth challenge SessionDelegate closure using the task override closure to get this working.
I have a WebApi controller action that I decorated with my [x509Authorize] attribute. I'm debugging this endpoint locally - and at the same time running a console application that tries to call this endpoint.
Client side
Here's the client code - slightly simplified:
X509Certificate Cert = X509Certificate.CreateFromCertFile("C:\\Temp\\ht-android-client.pfx");
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://localhost:44300/api/mobile/predict");
Request.ClientCertificates.Add(Cert);
HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
....
I've asserted that the Cert is the correct certificate. I've installed the .pfx in my CurrentUser\Personal store and in the LocalMachine\Personal store - and modified to take the Cert from that store, as suggested here but that doesn't seem to make a difference:
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var Cert = store.Certificates.Find(X509FindType.FindBySubjectName, "Android", true)[0];
Server side
And I'm listening on the WebAPI endpoint like with the following code:
public class x509AuthorizeAttribute : AuthorizeAttribute
{
public override Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
var cert = actionContext.Request.GetClientCertificate();
// value of 'cert' is null
I hit a breakpoint in the console app first - see that the correct certificate is selected. Then I hit the breakpoint on the server and see that the value of .GetClientCertificate() is null. What am I doing wrong? The other SO questions 1 and 2 didn't help me any further.
Additional information on the certificates
I've created a self-signed CA certificate which is installed on the LocalMachine\Trusted root CA store. I've created the android client cert - and signed it with my self-signed CA certificate. Then I converted that into a pkcs12 file. This is the certificate that the client is using - which is also installed in my personal stores ( both machine and currentUser ) and is valid ( you can see the chain go back to the ROOT CA cert ).
Also - the certificate's purpose is set to clientAuth:
So the problem is indeed that the server needs to have the following set in the web.config in order to force IIS to start the SSL cert negotiation:
<security>
<access sslFlags="SslNegotiateCert" />
</security>
If this is not present - the certificate will be ignored and you will get null on the GetClientCertificate() call.
This implies however that all clients for my WebAPI are now forced to present a valid certificate - so my original idea of having just one controller method requiring a certificate does not seem possible.
Then there's the challenge of setting this config paramter in web.config, because of the restrictions for Azure Cloud Services. However - this answer provides a solution for that.
EDIT
On a side note this is not supported yet in ASP.NET vNext ( v rc-01-final )
I constructed a apache mod_perl web service based on SSL.Of course, From my browser, I can access the web service using https (Of cource,I add my self-signed CA cert to brower's trust list) access the web service,but when using SOAP::Lite , I failed.
This is my source code:
$ENV{HTTPS_CERT_FILE} = '/etc/pki/tls/mycerts/client.crt';
$ENV{HTTPS_KEY_FILE} = '/etc/pki/tls/mycerts/client.key';
#$ENV{HTTPS_CA_FILE} = '/etc/pki/tls/mycerts/ca.crt';
#$ENV{HTTPS_CA_DIR} = '/etc/pki/tls/mycerts/ca.key';
#$ENV{HTTPS_VERSION} = 3;
$ENV{SSL_ca_file}='/etc/pki/tls/mycerts/ca.crt';
$ENV{SSL_ca_pah}='/etc/pki/tls/mycerts/';
#$ENV{SSL_cert_file}='/etc/pki/tls/mycerts/client.key';
#$ENV{SSL_key_file}='/etc/pki/tls/mycerts/client.crt';
$ENV{PERL_LWP_SSL_CA_FILE}='/etc/pki/tls/mycerts/ca.crt';
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=1;
#$ENV{PERL_LWP_SSL_CA_PATH}='/etc/pki/tls/mycerts/';
use SOAP::Lite;
my $name = "wuchang";
print "\n\nCalling the SOAP Server to say hello\n\n";
print SOAP::Lite
-> uri('http://localhost/mod_perl_rules1')
-> proxy('https://localhost/mod_perl_rules1')
-> result;
I get the response:
500 Can't connect to localhost:443 (certificate verify failed) at /root/Desktop/test.pl line 18
I really cannot debug this.I don't know if my certificate format is incorrect.I use openssl to generate my cert,including client cert ,server cert and my self-signed ca cert and I make CA sign the client and server cert.I really don't know what is going wrong/.
Simply tell it not to check the certificate. Set SSL Verify to zero like this:
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=0;