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.
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 am making both server and client for an application, using the ACE library with OpenSSL. I am trying to get mutual authentication to work, o the server will only accept connections from trusted clients.
I have generated a CA key and cert, and used it to sign a server cert and a client cert (each with their own keys also). I seem to be loading the trusted store correctly, but I keep getting the error "peer did not return a certificate" during handshake.
Server side code:
ACE_SSL_Context *context = ACE_SSL_Context::instance();
context->set_mode(ACE_SSL_Context::SSLv23_server);
context->certificate("../ACE-server/server_cert.pem", SSL_FILETYPE_PEM);
context->private_key("../ACE-server/server_key.pem", SSL_FILETYPE_PEM);
if (context->load_trusted_ca("../ACE-server/trusted.pem", 0, false) == -1) {
ACE_ERROR_RETURN((LM_ERROR, "%p\n", "load_trusted_ca"), -1);
}
if (context->have_trusted_ca() <= 0) {
ACE_ERROR_RETURN((LM_ERROR, "%p\n", "have_trusted_ca"), -1);
}
Client side code:
ACE_SSL_Context *context = ACE_SSL_Context::instance();
context->set_mode(ACE_SSL_Context::SSLv23_client);
context->certificate("../ACE-client/client_cert.pem", SSL_FILETYPE_PEM);
context->private_key("../ACE-client/client_key.pem", SSL_FILETYPE_PEM);
I generated the certificates following these instructions: https://blog.codeship.com/how-to-set-up-mutual-tls-authentication/
And checking online, I found that if the .crt and .key files are readable, they should already be in .pem format and there is no need to convert them. So I just changed the extension and used them here.
Any help is appreciated!
My problem apparently was the same as seen here: OpenSSL client not sending client certificate
I was changing the SSL context after creating the SSL Socket. Now the mutual authentication works, but my client crashes when closing the connection. Though I don't know why that is yet.
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 )
This comes from the python documentation for Python "Requests" http library
"You can also specify a local cert to use as client side certificate, as a single file (containing the private key and the certificate) or as a tuple of both file’s path":
>>> requests.get('https://kennethreitz.com', cert=('/path/server.crt', '/path/key'))
<Response [200]>
http://docs.python-requests.org/en/latest/user/advanced/
What's a good way to do the same thing in Clojure ? I looked at clj-http and http-kit but did not see an example
Have you seen async-http-client?
It has specific tests for cert handling you can view here. The API docs are here, particularly relevant would be the namespace http.async.client.cert.
From that test, a typical example of loading keystore and certificate is:
(def ks-file "test-resources/keystore.jks")
(def cert-file "test-resources/certificate.crt")
(def password "secret")
(defn load-test-certificate [] (load-x509-cert cert-file))
(defn load-test-keystore [] (load-keystore (resource-stream ks-file) password))
I'm trying to figure out how to use the self compiled OpenSSL API to load an existing X.509 certificate (.crt) which I have included in Xcode's project structure.
I need a X509 object (from OpenSSL x509.h) which should be created/loaded from an existing file. Including the header works fine but I really can't find a way to load an existing certificate... There are sooo many methods in the x509.h but no sufficient documentation.
Thanks,
Chris
If you've read the character data into a char* s, something like
BIO* bio = BIO_new_mem_buf((void*)s, -1);
X509* cert = 0;
PEM_read_bio_X509(bio, &cert, 0, NULL);
...
X509_free(cert);
BIO_free(bio);