I am using the latest tag 4.0.4 of Starscream (https://github.com/daltoniam/starscream). I have created my own SSL Certificate using
openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.pem -days 365
On my backend, I use node.js to create the https server and create a websocket with the WebSocket-Node library (https://github.com/theturtle32/WebSocket-Node)
const httpsServer = https.createServer(
{
key: fs.readFileSync('./certs/key.pem'),
cert: fs.readFileSync('./certs/cert.pem')
});
httpsServer.listen(1234, function()
{
logger.log.info(filename + "Server is listening on port " + 1234);
});
var wsServer = new webSocketServer({
httpServer: httpsServer,
autoAcceptConnections: false
});
In Swift 5, I can connect to and communicate with the websocket using the following code
var request = URLRequest(url: URL(string: "wss://mydomain.com:1234")!)
request.timeoutInterval = 10
let pinner = FoundationSecurity(allowSelfSigned: true)
socket = WebSocket(request: request, certPinner: pinner)
socket?.delegate = self
socket?.connect()
However, the above does not use my certificate at all. I was expecting to have to import my .cer into xcode (add to the bundle), then set up the WebSocket using the certificate. Followed by some kind of handshaking or ssl challenge before the connection is accepted.
One issue I am running into is the new Starscream library does not have "socket.security" (like older versions appear to have) or anyway to add a certificate to the connection. So I cannot figure out how to add a cert to the socket.
// I've seen other post using "socket.security", but this does not
// appear to work anymore as "socket.security" doesn't exist
socket.security = SSLSecurity(certs: [ssl], usePublicKeys: true)
I don't know how my app is connecting and communicating without the certificate. This means anyone can communicate to my websocket if they know the domain and port #.
Shouldn't the httpsServer reject it?
Shouldn't I need to add the certificate to my app bundle and somehow configure the Websocket with it?
Primary Question: How can I secure my WebSocket so only my app (with the certificate) can communicate with my backend https websocket server?
Related
I have a server (Centos 7) setup to be used as mail server. Using postfix/dovecot/opendkim/opendmarc..
It works as it should, users are able to connect their emails using gmail for example. Able to send and receive mail.
Also when I use MailKit and test my .NET Core application from my home pc MailKit connects fine and the emails are send.
However, when I deploy the application to my server MailKit fails to connect.
If I look in the logs I see the following
postfix/submission/smtpd[4486]: match_hostname: unknown ~? 127.0.0.1/32
postfix/submission/smtpd[4486]: match_hostaddr: MY_SERVER_IP ~? 127.0.0.1/32
postfix/submission/smtpd[4486]: match_hostname: unknown ~? MY_SERVER_IP/32
postfix/submission/smtpd[4486]: match_hostaddr: MY_SERVER_IP ~? MY_SERVER_IP/32
postfix/submission/smtpd[4486]: lost connection after STARTTLS from unknown[MY_SERVER_IP]
But if I look a bit higher in the logs I see
Anonymous TLS connection established from unknown[MY_SERVER_IP]: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
My MailKit (which works fine from outside of the server):
using (SmtpClient emailClient = new SmtpClient())
{
await emailClient.ConnectAsync(emailConfiguration.SmtpServer, emailConfiguration.SmtpPort, SecureSocketOptions.StartTls);
emailClient.AuthenticationMechanisms.Remove("XOAUTH2");
await emailClient.AuthenticateAsync(emailConfiguration.SmtpUsername, emailConfiguration.SmtpPassword);
await emailClient.SendAsync(message);
await emailClient.DisconnectAsync(true);
}
edit:
The exception from MailKit (certificate is proper and not self-signed):
MailKit.Security.SslHandshakeException: An error occurred while attempting to establish an SSL or TLS connection.
May 19 16:07:37 domain.com NETCoreApp[4452]: The server's SSL certificate could not be validated for the following reasons:
May 19 16:07:37 domain.com NETCoreApp[4452]: • The server certificate has the following errors:
May 19 16:07:37 domain.com NETCoreApp[4452]: • unable to get certificate CRL
May 19 16:07:37 domain.com NETCoreApp[4452]: • The root certificate has the following errors:
May 19 16:07:37 domain.com NETCoreApp[4452]: • unable to get certificate CRL
May 19 16:07:37 domain.com NETCoreApp[4452]: • unable to get local issuer certificate
May 19 16:07:37 domain.com NETCoreApp[4452]: ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
The unable to get certificate CRL error sounds like SslStream was unable to get the CRL, perhaps because the CRL server is unreachable for some reason.
You could try adding emailClient.CheckCertificateRevocation = false; before the ConnectAsync to check if that's the issue.
The other error, unable to get local issuer certificate, might be because the server that MailKit is running on doesn't have the Root CA certificate in its X509Store but your home PC does.
Update:
The problem is that LetsEncrypt SSL certificates do not include a CRL location which means that certificate revocation checks will fail.
To bypass this, you need to set client.CheckCertificateRevocation = false; before connecting.
I found an answer which works but isn't my preferred method since I wanted to be able to use MailKit for more that just my own server (make it configurable from within the app itself)
I came to the solution because I thought it had to do with some internal traffic going wrong..
By using the old SmtpClient from System.Net.Mail I was able to use the DefaultCredentials.
using (SmtpClient client = new SmtpClient("127.0.0.1"))
{
client.UseDefaultCredentials = true;
MailAddress from = new MailAddress(emailMessage.FromAddress.Address, emailMessage.FromAddress.Name);
foreach (IEmailAddress emailAddress in emailMessage.ToAddresses)
{
MailAddress to = new MailAddress(emailAddress.Address, emailAddress.Name);
MailMessage email = new MailMessage(from, to)
{
Subject = emailMessage.Subject,
Body = emailMessage.Content
};
await client.SendMailAsync(email);
}
}
I have the same problem on ubuntu 20.04 with .NET core 3.1
and after 3 hours of trial and error, I finally found the solution.
I've just ignored the Certificate Validation CallBack.
using var client = new SmtpClient(new ProtocolLogger("smtp.log"));
client.CheckCertificateRevocation = false;
client.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
client.Connect("your.smtp.host", 587, SecureSocketOptions.StartTls);
I hope this would be helpful :)
I'm trying to secure a REST API using TLS/SSL, to do so I needed to update my client to use the public key and certificate.
The client is written in dart and here's how I implemented the SecurityContext :
SecurityContext clientContext = SecurityContext.defaultContext;
var certificate = (await rootBundle.load("assets/ssl/coastr.crt")).buffer.asInt8List();
print(certificate.toString());
clientContext.setTrustedCertificatesBytes(certificate);
/*var authorities = (await rootBundle.load('assets/ssl/coastr.ca-bundle')).buffer.asUint8List();
print(authorities.toString());
clientContext.setClientAuthoritiesBytes(authorities);*/
var key = (await rootBundle.load("assets/ssl/coastr_public.key")).buffer.asInt8List();
print(key.toString());
clientContext.usePrivateKeyBytes(key);
HttpClient client = HttpClient(context: clientContext);
HttpClientRequest request = await client.getUrl(Uri.parse(url));
HttpClientResponse response = await request.close();
The certificate (.crt file) is added without issue to the clientContext but adding the key to it returns me this error :
[ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception:
TlsException: Failure in usePrivateKeyBytes (OS Error:
BAD_PKCS12_DATA(pkcs8_x509.c:606)
passed a null parameter(ssl_privkey.cc:375), errno = 0)
The files I'm using are :
coastr.crt with this as a header : -----BEGIN CERTIFICATE-----
coastr_public.key with header : -----BEGIN PUBLIC KEY-----
I have no idea if I'm providing the wrong files to the client or if the error comes from elsewhere.
The files where generated using openssl.
Thank you for your help.
In general, you shouldn't have to add anything to the client to allow it to connect to a correctly configured HTTPS server. Hopefully, the server has a signed server side certificate. If that certificate is signed by a reputable CA, the client should automatically trust it. If it is signed by an in house CA, or self signed, you need to do some work. You need to provide the client with the signing certificate. In the former case that would be the root CA's certificate. In the latter case, supplying the server's certificate may work, though it's probably easier to disable the client check altogether.
The signing certificate is likely to be in CRT form as you've found. And you need to supply that exactly as you are doing. There's no need to supply any public keys as the are distributed in the certificates sent with the server hello.
Unless you want to use a client side certificate, there's no need to supply a private key, so you can skip the step that is failing. And supplying a public key to it is definitely not going to work, anyway.
I am using DelphiMongoDB from Grijjy (DelphiMongoDB) which is working pretty cool so far. But I can't find any functions to authenticate against a MongoDB. Did anybody getting this work or figured out how to do it?
Thanks and best regards
Update: The most up to date version of the Grijjy driver now supports TLS, X.509 client certificate authentication, SCRAM SHA-1 and SHA-256 authentication. We have also tested it against MongoDB Atlas instance at Azure.
Here is a simple example of how to use the authentication.
var
Settings: TgoMongoClientSettings;
Client: IgoMongoClient;
Database: IgoMongoDatabase;
Collection: IgoMongoCollection;
Doc: TgoBsonDocument;
begin
Settings := TgoMongoClientSettings.Create;
Settings.Secure := True;
Settings.AuthMechanism := TgoMongoAuthMechanism.SCRAM_SHA_1;
Settings.AuthDatabase := 'admin';
Settings.Username := 'username';
Settings.Password := 'password';
//Settings.QueryFlags := [TgoMongoQueryFlag.SlaveOk];
Client := TgoMongoClient.Create('my.mongodb.server.com', 27017, Settings);
Database := Client.GetDatabase('mydatabase');
Collection := Database.GetCollection('mycollection');
for Doc in Collection.Find() do
Writeln(Doc.ToJson(TgoJsonWriterSettings.Pretty));
end;
Legacy: Yes, the published Grijjy driver does not support authentication, but we have tested it internally and may add this ability in the near future to Github. You are also welcome to make a pull request if you want to adapt the following changes:
MongoDB currently supports 2 types of authentication, SCRAM and x.509 Certificate Authentication. Internally we have tested the x.509 Certificate Authentication, but the current driver on Github does not reflect this ability. We have not experimented with SCRAM yet.
To make it work with the MongoDB driver we published on Github, you may have to make a couple of changes.
You need to create a self-signed CA and certificate for your MongoDB server.
You need to configure your MongoDB server to use certs.
You need to create a self-signed certificate for your MongoDB client or clients. You can use the same cert for all clients.
You need to enable SSL/TLS connections and use your client certificate with the MongoDB driver.
1 To create all the certificates, you need an existing CA or create a self-signed CA. You can use the openssl.exe binary to do most of this:
Create root certificate authority (ca.pem and privkey.pem):
openssl req -out ca.pem -new -x509 -days 3650 -subj "/C=US/ST=California/O=Company/CN=root/emailAddress=root#domain.com"
To create a self-signed certificate for your MongoDB server (server.pem):
openssl genrsa -out server.key 2048
openssl req -key server.key -new -out server.req -subj "/C=US/ST=California/O=Company/CN=db.myserver.com/emailAddress=user#domain.com"
openssl x509 -req -in server.req -CA ca.pem -CAkey privkey.pem -CAcreateserial -out server.crt -days 3650
type server.key server.crt > server.pem
openssl verify -CAfile ca.pem server.pem
2 To configure MongoDB to use certs on the Windows version (similar on other versions), edit the c:\data\mongod.cfg:
systemLog:
destination: file
path: c:\data\log\mongod.log
storage:
dbPath: c:\data\db
net:
port: 27017
bindIp: 127.0.0.1
ssl:
mode: requireSSL
PEMKeyFile: c:\data\server.pem
CAFile: c:\data\ca.pem
{allowConnectionsWithoutCertificates: true }
{allowInvalidHostnames: true }
You may need allowInvalidHostnames to True if you are using a self signed certificate.
3 To create a self-signed certificate for your MongoDB client (client1.pem):
openssl genrsa -out client1.key 2048
openssl req -key client1.key -new -out client1.req -subj "/C=US/ST=California/O=Company/CN=client1/emailAddress=user#domain.com"
openssl x509 -req -in client1.req -CA ca.pem -CAkey privkey.pem -CAserial ca.srl -out client1.crt -days 3650
type client1.key client1.crt > client1.pem
openssl verify -CAfile ca.pem client1.pem
Note: You will also need to use the client certificate in whatever tool you are using to admin the MongoDB server.
4 To enable SSL/TLS connections for MongoDB driver you may have to change the source files. Our unit Grijjy.Http shows how to enable the driver for ‘https’. Essentially you need to do 2 things to the Connection inside the Grijjy.MongoDB.Protocol unit probably within the method TgoMongoProtocol.Connect :
Set Connection.SSL := True;
Set the Connection.Certificate to the client1.pem you created.
You will have to do some testing, but I hope it points you in the correct direction to make it work. Would love your contribution to the open source project if you can.
As far as I can understand the sources, https://github.com/grijjy/DelphiMongoDB/ doesn't support authentication.
Also from the source, https://github.com/stijnsanders/TMongoWire doesn't either.
FireDAC Mongo uses the C Mongo client library, which supports authentication.
Our Open Source SynMongoDB.pas supports authentication, FPC and almost all Delphi versions (even pre-Unicode). Using variant late-binding to access the BSON/JSON content, it is pretty easy to work with it. Just check the corresponding documentation pages. You can write for instance:
var doc: variant;
...
doc := Coll.FindOne(5);
writeln('Name: ',doc.Name);
writeln('Number: ',doc.Number);
or
var docs: TVariantDynArray;
...
Coll.FindDocs(docs);
for i := 0 to high(docs) do
writeln('Name: ',docs[i].Name,' Number: ',docs[i].Number);
The TMongoClient.OpenAuth method supports both old/deprecated MONGODB-CR method, and the new SCRAM-SHA-1 method:
Client := TMongoClient.Create('localhost',27017);
try
DB := Client.OpenAuth('mydb','mongouser','mongopwd');
...
Note that even if it is part of the mORMot framework, this unit is stand-alone: you don't need to use the ORM, SOA, or MVC parts of the framework - even if it works very well with the ORM, and is able to convert SQL-like statements into MongoDB pipelines, which is a unique very powerful feature. Another unique feature is proper Decimal128 support.
Over a network, also ensure that you use a TLS connection to the server. SynMongoDB.pas can do that under Windows, with no external OpenSSL library needed (it uses the raw Windows SO API).
How can I enable certificate pinning using OkHttp for my Android / Java application?
The OkHttp documentation gives us a clear way to do this complete with sample code. In case it goes away, here it is pasted in below:
1. Add a broken CertificatePinner and make a request.
Any request will do, even if it doesn't exist. You can do this in your Android application, or just create a dummy Java application and run this as well.
For example, to pin https://publicobject.com, start with a broken
configuration:
String hostname = "publicobject.com";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build();
client.newCall(request).execute();
As expected, this fails with a certificate pinning exception:
javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!
Peer certificate chain:
sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=: CN=publicobject.com, OU=PositiveSSL
sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=: CN=COMODO RSA Secure Server CA
sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=: CN=COMODO RSA Certification Authority
sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=: CN=AddTrust External CA Root
Pinned certificates for publicobject.com:
sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
at okhttp3.CertificatePinner.check(CertificatePinner.java)
at okhttp3.Connection.upgradeToTls(Connection.java)
at okhttp3.Connection.connect(Connection.java)
at okhttp3.Connection.connectAndSetOwner(Connection.java)
2. Configure your OkHttp Client Correctly:
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
.add("publicobject.com", "sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=")
.add("publicobject.com", "sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=")
.add("publicobject.com", "sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=")
.build();
That's all there is to it!
This method will give you all your certificates in the entire chain. This is advantageous since it's safer as only one certificate in the chain has to match for the request to succeed. It's likely at some point in the future, your certificates will be updated, but as long as the entire chain isn't updated, your application shouldn't break.
I'm using several RESTful webservice in JAVA based web-application. I'm using the RESTeasy client to access my webservice. Here all communication between the client and service is through XML(JAX-B xml annotated detail classes). Here are the following codes
String serviceURL = "https://service.company.com/Service/getService"
ServiceRequestDetail serviceRequestDetail = getServiceRequestAsDetailClass();
ServiceResponseDetail serviceResponseDetail = new ServiceResponseDetail();
ClientRequest clientRequest = new ClientRequest(serviceURL);
clientRequest.accept(MediaType.APPLICATION_XML);
clientRequest.body(MediaType.APPLICATION_XML, serviceRequestDetail);
ClientResponse<ServiceRequestDetail> response =
clientRequest.post(ServiceRequestDetail.class);
if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code : " +
response.getStatus());
}
ServiceResponseDetail serviceResponseDetail =
response.getEntity(ServiceResponseDetail.class);
and when I try to access my service I get the "Peer not Authenticated" error
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
...
Is there any way to add the SSL configuration details in the RESTeasy client? any other suggestions for solving this issue is also welcome
Thanks in advance
I found out the answer but I'm really sorry for the late response.
To answer my question, RESTeasy client does support TLS/SSL. Infact the problem was I missed to install the certificate into the JVM.
keytool -import -alias <Replace certificate Alias name> -keystore $JAVA_HOME\jre\lib\security\cacerts -file <Replace your Certificate file location>
This solved the issue of "Peer Not Authenticated". Hope it helps. Kudos
If you don't want to add certificate to JVM and keep this cert separate. You can load the cert as part of your code like below.
// load the certificate
InputStream fis = this.getClass().getResourceAsStream("file/path/to/your/certificate.crt");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(fis);
// load the keystore that includes self-signed cert as a "trusted" entry
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
keyStore.setCertificateEntry("cert-alias", cert);
tmf.init(keyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);`
then attach to rest easy builder like
resteasyClientBuilder.sslContext(sslContext)