There is a ton of information available about cURL and SSL, but not so much is out there about writing a server. I have a small local server written in PHP that I would like to have TLS/SSL enabled. I am having issues with my server crashing upon secure connections. I am only receiving the error, "PHP Warning: stream_socket_accept(): Failed to enable crypto". I have an identical server running without TLS, and it is working fine. I have an idea it is the certificates, or the connection to/reading the certificates. However, I am not sure if it is an error on how I generated the certificates, how I have them joined to PEM, or something else. Also, for our domains, I've used *.domain.tld in both the code below, as well as the local name in the cert creation.
Furthermore, the certificates shown in the web browser show the 127.0.0.1 cert and not the localhost (or other domains) certificates regardless of the domain requested. Is that because the 127.0.0.1 is set as the local cert? About the certificates- this is my current code for creating the .pem file to use on the server:
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout apache.key -out apache.crt
apache.crt apache.key > joined.pem
A basic rendition of the server code is:
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
$ctx = stream_context_create(['ssl' => [
'local_cert' => "{path}/Websites/127.0.0.1/certs/joined.pem",
'SNI_server_certs' => [
"127.0.0.1" => "{path}/Websites/127.0.0.1/certs/joined.pem",
"localhost" => "{path}//Websites/localhost/certs/joined.pem",
]
]]);
stream_context_set_option($ctx, 'ssl', 'ssl_method', 'STREAM_CRYPTO_METHOD_TLSv23_SERVER');
stream_context_set_option($ctx, 'ssl', 'allow_self_signed', true);
stream_context_set_option($ctx, 'ssl', 'verify_peer', false);
stream_context_set_option($ctx, 'ssl', 'ciphers', "HIGH");
$socket = stream_socket_server("tls://127.0.0.1:8443", $errno, $errstr, $flags, $ctx);
while ( $client = stream_socket_accept($socket, "-1", $clientIP)):
$msg = fread($client, 8192);
$resp = "HTTP/1.1 200 OK\r\nContent-type: text/html\r\n\r\n<h1>Hi, you are secured.<br>{$msg}";
fwrite($client,$resp );
fclose($client);
endwhile;
One more thing, what is the proper cipher to set for appeasing all of the major browsers out there, Chrome seems to play by its own rules.
Any ideas what I am missing here?
My issue was not setting SANs during the certificate creation. The link https://serverfault.com/questions/880804/can-not-get-rid-of-neterr-cert-common-name-invalid-error-in-chrome-with-self corrected my issues.
Related
I'm using Spring WebClient to invoke a webservice over SSL, but I'm getting java.security.cert.CertificateException: No subject alternative DNS name matching lizzad.int.octa.com found.
As far as I can see, this means that the certificate does not have the name lizzad.int.octa.com. This makes some sense because I've the service invokation working if I invoke another url.
But I was told to change the URL and it was assured that the certificate should be the same. Nevertheless it is not working and then the client asked me if I could bypass something in order to make this work in tests environment.
After researching a bit I found some code I thought it could be hepful to configure WebClient.
TcpClient tcpClient = TcpClient.create().secure(sslContextSpec -> {
// configure ssl
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
sslContextBuilder
.trustManager(InsecureTrustManagerFactory.INSTANCE);
sslContextSpec.sslContext(sslContextBuilder)
.defaultConfiguration(SslProvider.DefaultConfigurationType.NONE)
.handshakeTimeoutMillis(30)
.closeNotifyFlushTimeoutMillis(12000)
.closeNotifyReadTimeoutMillis(12000)
.handlerConfigurator(
(handler)->{
SSLEngine engine = handler.engine();
engine.setNeedClientAuth(true);
SSLParameters params = new SSLParameters();
List<SNIMatcher> matchers = new LinkedList<>();
SNIMatcher matcher = new SNIMatcher(0) {
#Override
public boolean matches(SNIServerName serverName) {
return true;
}
};
matchers.add(matcher);
params.setSNIMatchers(matchers);
engine.setSSLParameters(params);
}
)
;
});
HttpClient httpClient = HttpClient.from(tcpClient);
return WebClient.builder()
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_OCTET_STREAM_VALUE)
.clientConnector(new ReactorClientHttpConnector(httpClient)).build();
But it seams that Matcher is not what will make the hostname to be ignore.
On the other hand I also found here [https://stackoverflow.com/questions/43371418/java-security-cert-certificateexception-no-subject-alternative-dns-name-matchin][1]
new javax.net.ssl.HostnameVerifier() {
public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) {
return true;
}
});
that if I was not using WebClient I could set a HostnameVerifier and return true in order to bypass the hostname verification.
Do you know if it is possible and how can I define a HostnameVerifier like this in my WebClient Configuration to prevent
java.security.cert.CertificateException: No subject alternative DNS name matching lizzad.int.octa.com
Thanks
[1]: java.security.cert.CertificateException: No subject alternative DNS name matching
I think this cannot be configured/ignored from the client side. You are trying to call the server on lizzad.int.octa.com and it is actually reaching it however the server is saying that it won't continue to talk with the client because lizzad.int.octa.com is not present within the SAN field of the server certificate.
This stackoverflow question/answer is similar to yours: Certificate for <localhost> doesn't match any of the subject alternative names
I would advise to recreate the server certificate if it is possible. You just need to add lizzad.int.octa.com as a DNS for the san field. See below for an example with keytool:
keytool -genkeypair -keyalg RSA -keysize 2048 -alias server -dname "CN=server,OU=some-organisation-unit,O=some-organisation,C=US" -ext "SAN:c=DNS:lizzad.int.octa.com,IP:127.0.0.1" -validity 3650 -keystore identity.jks -storepass secret -keypass secret -deststoretype pkcs12
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 use define-easy-handler all the time. I now have a freshly minted ssl certificate and associated pem files, but can't figure out what the ssl equivalent of d-e-h is.
For example, I have:
(hunchentoot:define-easy-handler
(login :uri "/login")
()
(login-html))
which is just a simple form whose formaction goes here:
(hunchentoot:define-easy-handler
(dologin :uri "/dologin")
(email password)
(dologin-html email password))
I got the required .pem files from freecert, so I think that I have the files that go to :SSL-CERTIFICATE-FILE and :SSL-PRIVATEKEY-FILE. I've tried various args to the above to make this work, but can't seem to get it to work. Can someone give me an example of how to do this?
Thanks in advance for you help!
You can keep your easy-handlers and change the type of acceptor you need.
(defpackage :web (:use :cl :hunchentoot))
(in-package :web)
;; This url can be accessed by all acceptors
(define-easy-handler (no-ssl :uri "/normal") ()
(setf (content-type*) "text/plain")
"NORMAL PAGE")
;; This url can be accessed only by an acceptor named SSL
(define-easy-handler (ssl :uri "/secure" :acceptor-names '(ssl)) ()
(setf (content-type*) "text/plain")
"SECURED PAGE")
For tests, if you don't already have a self-signed certificate , you can do:
$ cd /tmp
$ openssl req -new -x509 -nodes -out server.crt -keyout server.key
Then, we define two kinds of acceptors:
(defvar *no-ssl-acceptor*
(make-instance 'easy-acceptor :port 8080))
(defvar *ssl-acceptor*
(make-instance 'easy-ssl-acceptor
:name 'ssl
:port 7777
:ssl-privatekey-file #P"/tmp/server.key"
:ssl-certificate-file #P"/tmp/server.crt"))
Start them:
(start *ssl-acceptor*)
(start *no-ssl-acceptor*)
Your browser should complain the first time you try to access HTTPS pages (ignore the security exception).
http://localhost:8080/normal
http://localhost:8080/secure (should fail with 404)
https://localhost:7777/normal
https://localhost:7777/secure
Note also that the :acceptor-names argument is optional (thanks #Simeon Ikudabo), here above it was added explictly for the examples. You can just define an SSL acceptor and let all your pages be served over a secure link.
This is not a function of the handlers but of the acceptor. All you need to do is use an easy-ssl-acceptor instead of an easy-acceptor for starting your server:
(hunchentoot:start (make-instance 'hunchentoot:easy-ssl-acceptor :port 4242))
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 need some help. I'm integrating getstream.io into my laravel application (v5.1), I'm stuck with this error:
cURL error 60: SSL certificate problem: unable to get local issuer
certificate
This is my code:
use GetStream\Stream\Client;
public function index()
{
$client = new Client('rrzp7mz8htgn', '8cgs94jg2z5da2h4q2an8q6q5vktrp8y8w7rsft3zndf63c8y9n59g2h2qvtdhqq');
$ericFeed = $client->feed('user', 'eric');
$data = [
"actor"=>"eric",
"verb"=>"like",
"object"=>"3",
"tweet"=>"Hello world"
];
$ericFeed->addActivity($data);
}
I followed the instructions below from packalyst
Add the get-stream into your composer:
"require": {
"get-stream/stream-laravel": "~2.1"
},
then run composer update
I also added the provider and the aliases
'providers' => array(
'GetStream\StreamLaravel\StreamLaravelServiceProvider',
...
),
'aliases' => array(
'FeedManager' => 'GetStream\StreamLaravel\Facades\FeedManager',
...
),
I run:
php artisan vendor:publish --provider="GetStream\StreamLaravel\StreamLaravelServiceProvider"
I emailed already getstream.io, but no response yet. I'll be updated this post when I received some answers from them.
I also checked this post from laracast, but there's no answer.
https://laracasts.com/discuss/channels/general-discussion/activity-feeds-with-getstreamio?page=0
Getstream.io replied to my email and helped me, Here's the solution,
the SSL error message it’s usually related to using old certificate
key chains with CURL. This is unfortunately quite of a common issue
with CURL and SSL, I suggest you to try the solution suggested in this
thread:
Paypal Access - SSL certificate: unable to get local issuer certificate
and this is what i did:
Downloaded cacert.pem from the above link at curl.haxx.se/ca/cacert.pem and save it to c:/wamp/bin/php/php5.5.12/cert/
Click my wamp icon, navigate to PHP > php.ini
Added the following line and click save.
curl.cainfo=c:/wamp/bin/php/php5.5.12/cert/cacert.pem
Restart wamp and that's it. it worked
Hope this helps other developers using getstream.io. Credits to Tommaso of getstream.io.