How to host over HTTPS with nodejs using an existing certificate - ssl

I bought a certificate for HTTPS SSL connection on a hosting website. I received a [name].crt file
var options = {
cert: fs.readFileSync('./https/8546332154a5224.crt')
};
var app = express();
var server = https.createServer(options, app);
var expressWs = expressWs(app, server);
I believe I need a Certificate Signing Request File .csr and a private key. I don't know how to generate them.

You can use OpenSSL from the command line. OpenSSL generates the private key and CSR files:
openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr

Related

How do I connect to GRPC using TLS?

I'm trying unsuccessfully to get a basic GRPC server and client working with SSL/TLS, with a node client and Java server. Starting with no security:
// client.js
const creds = grpc.credentials.createInsecure()
const stub = new hello_proto.Greeter('localhost:50051', creds)
stub.sayHello(...)
// server.java
Server server = ServerBuilder.forPort(50051)
.addService(serviceImplementation)
.build();
server.start();
All works as expected here. I then tried to add SSL credentials, generating a certificate and private key like this (following a Python example):
$ openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.crt
This generates a certificate (server.crt) and private key (server.key). I then add those credentials to client and server (private key on server only), following the guidance from the grpc.io Auth Guide and grpc-java respectively:
// client.js
const rootCert = fs.readFileSync("path/to/server.crt");
const channelCreds = grpc.credentials.createSsl(rootCert);
const stub = new hello_proto.Greeter('localhost:50051', channelCreds);
stub.sayHello(...)
// server.java
File certChainFile = File("path/to/server.crt")
File privateKeyFile = File("path/to/server.key")
Server server = ServerBuilder.forPort(50051)
.useTransportSecurity(certChainFile, privateKeyFile)
.addService(serviceImplementation)
.build();
server.start();
Now I get an error UNAVAILABLE: No connection established on the client side:
Error: 14 UNAVAILABLE: No connection established
at Object.callErrorFromStatus (path/to/node_modules/#grpc/grpc-js/build/src/call.js:31:26)
at Object.onReceiveStatus (path/to/node_modules/#grpc/grpc-js/build/src/client.js:176:52)
at Object.onReceiveStatus (path/to/node_modules/#grpc/grpc-js/build/src/client-interceptors.js:336:141)
at Object.onReceiveStatus (path/to/node_modules/#grpc/grpc-js/build/src/client-interceptors.js:299:181)
at path/to/node_modules/#grpc/grpc-js/build/src/call-stream.js:130:78
at processTicksAndRejections (node:internal/process/task_queues:76:11) {
code: 14,
details: 'No connection established',
metadata: Metadata { internalRepr: Map(0) {}, options: {} }
}
No error on the server side. The client-side error is, unhelpfully, identical to the one I get when the server is down.
How do I implement basic TLS authentication between a Java server and node client?
Maybe you can reference my code in helloworlde/grpc-java-sample, feel free to translate Chinese;
For the both side, it need SslContext
Server
File keyCertChainFile = new File("server.pem");
File keyFile = new File("server.key");
SslContext sslContext = GrpcSslContexts.forServer(keyCertChainFile, keyFile)
.clientAuth(ClientAuth.OPTIONAL)
.build();
Server server = NettyServerBuilder.forAddress(new InetSocketAddress(9090))
.addService(new HelloServiceImpl())
.sslContext(sslContext)
.build();
Client
File trustCertCollectionFile = new File("server.pem");
SslContext sslContext = GrpcSslContexts.forClient()
.trustManager(trustCertCollectionFile)
.build();
ManagedChannel channel = NettyChannelBuilder.forAddress("127.0.0.1", 9090)
.overrideAuthority("localhost")
.sslContext(sslContext)
.build();

Java TooTallNate Secure Websocket Server on FireTV with Lets Encrypt Certificate: Handshake / Chain Error

I am using a Java Websocket Server (TooTallNate). A Javascript App is connecting securely via a LetsEncrypt certificate. It is renewed automatically via certbot and is serving an Apache on the same machine too. On all tested browsers everything is working fine, for both https and wss.
I wanted to submit my app as a packaged FireTV app. I tested it in the "Web App Tester" app. As soon as the JS tries to connect to the WSS, it raises an SSL error, which reads in adb-logcat
I/X509util: Failed to validate the certificate chain, error: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
but sometimes just
E/chromium(13208): [ERROR:ssl_client_socket_impl.cc(947)] handshake failed; returned -1, SSL error code 1, net_error -202
The relevant Java code filling the SSLContext from TooTallNate is:
private static SSLContext getContext() {
SSLContext context;
String password = "CHANGEIT";
String pathname = "pem";
try {
context = SSLContext.getInstance("TLS");
byte[] certBytes = parseDERFromPEM(getBytes(new File(pathname + File.separator + "cert.pem")),"-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
byte[] keyBytes = parseDERFromPEM(getBytes(new File(pathname + File.separator + "privkey.pem")),"-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----");
X509Certificate cert = generateCertificateFromDER(certBytes);
RSAPrivateKey key = generatePrivateKeyFromDER(keyBytes);
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
keystore.setCertificateEntry("cert-alias", cert);
keystore.setKeyEntry("key-alias", key, password.toCharArray(), new Certificate[]{cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, password.toCharArray());
KeyManager[] km = kmf.getKeyManagers();
context.init(km, null, null);
}
catch (Exception e) {
context = null;
}
return context;
}
It took some reading to get it to work. There are few information on this special problem, so I decided to answer myself:
It was truly a problem of the missing chain in the SSLContext. Couldn't believe that, because it worked on every other chromium based browser.
In my LetsEncrypt folder I have four files: privkey.pem (not at thing), cert.pem, chain.pem and fullchain.pem, where fullchain.pem is just the concatenation of chain.pem and cert.pem. cert.pem is our own certificate and chain.pem is the Digital Signature Trust DST Root CA X3 certificate which you can see yourself using:
cat chain.pem | openssl x509 -text
I compared the certificates served over https and wss using:
openssl s_client -connect myurl:443 | openssl x509 -text
and
openssl s_client -connect myurl:8080 | openssl x509 -text
The latter showed two error at the beginning of the output
verify error:num=20:unable to get local issuer certificate
while the https response from apache showed
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = myurl
verify return:1
Finally the working solution is a lot easier than my solutions I had meanwhile. Its not necessary to download any further chain certificates. Just load the chain.pem you already have in your LetsEncrypt folder like the cert.pem and add both as the chain to the keystore in the setKeyEntry function:
private static SSLContext getContext() {
SSLContext context;
String password = "CHANGEIT";
String pathname = "pem";
try {
context = SSLContext.getInstance("TLS");
byte[] certBytes = parseDERFromPEM(getBytes(new File(pathname + File.separator + "cert.pem")),"-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
byte[] chainBytes = parseDERFromPEM(getBytes(new File(pathname + File.separator + "chain.pem")),"-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
byte[] keyBytes = parseDERFromPEM(getBytes(new File(pathname + File.separator + "privkey.pem")),"-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----");
X509Certificate cert = generateCertificateFromDER(certBytes);
X509Certificate chain = generateCertificateFromDER(chainBytes);
RSAPrivateKey key = generatePrivateKeyFromDER(keyBytes);
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
keystore.setCertificateEntry("cert-alias", cert);
keystore.setKeyEntry("key-alias", key, password.toCharArray(), new Certificate[]{cert, chain});
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, password.toCharArray());
KeyManager[] km = kmf.getKeyManagers();
context.init(km, null, null);
}
catch (Exception e) {
context = null;
}
return context;
}

Configuring logstash with ssl and sending message to kafka(with ssl) topic

I have logstash-6.5.4 (with ssl), web and scheduler in my local (hostname: webbox) and kafka-2.0 (with ssl) on another (hostname: kafkabox).
I am not able to receive message in kafka topic when message is sent from logstash.
Neither error message is displayed not message is sent to kafka topic. I tried to import logstash.crt into kafka's truststore but it also didn't worked.
Created logstash.crt and logstash.key with below command.
sudo openssl req -x509 -batch -nodes -days 3650 -newkey rsa:2048 -keyout /etc/logstash/logstash.key -out /etc/logstash/logstash.crt
Imported the logstash.crt into kafka's truststore file also and tried.
keytool -import -alias logstash -file logstash.crt -keystore cacerts
Logstash conf file is given below...
input {
tcp {
host=>"0.0.0.0"
port=>5514
type=>"syslogType"
ssl_enable=>true
ssl_cert=>"/etc/logstash/logstash.crt"
ssl_key=>"/etc/logstash/logstash.key"
ssl_verify=>false
}
}
filter {
}
output {
kafka {
bootstrap_servers=>"kafkabox:9093"
codec=>"json_lines"
topic_id=>"a_test"
ssl_keystore_location=>"keystore file"
ssl_keystore_password=>"changeit"
ssl_key_password=>"changeit"
ssl_truststore_location=>"truststore file"
ssl_truststore_password=>"changeit"
security_protocol=>"SSL"
}
}
Expecting message is sent from logstash (with SSL) to kafka (with SSL).
Java Code to connect to logstash which internally failing to send message to kafka topics (in ssl mode).
public class LogstashClient {
private static String message = "<86>Jun 25 14:32:25 webbox sshd[7517]: Failed password for root from 196.165.132.192 port 45691 ssh2";
public static void main(String[] args) throws Exception {
nonSSL();
//SSL();
}
private static void SSL() throws Exception {
// logstash.crt is directly imported into kafka's truststore
// Below <<Client Truststore>> will also have logstash.crt imported for handshaking while connecting
System.setProperty("javax.net.ssl.trustStore", "<<Client Truststore>>");
System.setProperty("javax.net.ssl.trustStorePassword", "test1234");
SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) factory.createSocket("localhost", 5514);
System.out.println("Handshaking...");
socket.startHandshake();
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
boolean checkError = printWriter.checkError();
printWriter.println(message);
}
private static void nonSSL() throws Exception {
Socket socket = new Socket("localhost", 5514);
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
printWriter.println(message);
}
}
Thanks,
RK,

ServiceFabric Local Cluster SSL

I have some misunderstanding with running SF on local cluster with SSL, on localhost.
Microsoft created greate article about configuring HTTPS on your endpoints But it works well only if you use their certificate generator CertSetup.ps1 . If you try install your own pfx, it will not work.
First I created localhost self-signed cert by OpenSSL:
set OPENSSL_CONF=W:\OpenSSL-Win32\bin\openssl.cfg
openssl genrsa -out W:\CERTS\wepapissl.key -passout pass:1234567890 -aes256 2048
openssl req -x509 -new -key W:\CERTS\wepapissl.key -days 10000 -out W:\CERTS\wepapissl.crt -passin pass:1234567890 -subj /CN="localhost"
openssl pkcs12 -export -inkey W:\CERTS\wepapissl.key -in W:\CERTS\wepapissl.crt -out W:\CERTS\wepapissl.pfx -passout pass:0987654321 -passin pass:1234567890`
Second I have created default ASP.NET Core Web Application (Core 2.0, API template). And added code for configure Kestrel to use HTTPS:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseKestrel(opt =>
{
opt.Listen(IPAddress.Any, port, listenOptions =>
{
listenOptions.UseHttps(GetCertificateFromStore());
});
})
.UseStartup<Startup>()
.Build();
private static X509Certificate2 GetCertificateFromStore()
{
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
store.Open(OpenFlags.ReadOnly);
var certCollection = store.Certificates;
var currentCerts = certCollection.Find(X509FindType.FindBySubjectDistinguishedName, "CN=localhost", false);
return currentCerts.Count == 0 ? null : currentCerts[0];
}
finally
{
store.Close();
}
}
I have got expected result. Page with warning about website’s security certificate:
Result from ValueController with warning
Third I have created Service Fabric Application (Stateless ASP.NET Core template). Change my ServiceManifest.xml by editing Endpoint section:
<Endpoint Protocol="https" Name="ServiceEndpoint" Type="Input" Port="8256" />
And added code for configure Kestrel to use HTTPS (class Web1 : StatelessService):
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new ServiceInstanceListener[]
{
new ServiceInstanceListener(serviceContext =>
new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
{
ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");
return new WebHostBuilder()
.UseKestrel(opt =>
{
int port = serviceContext.CodePackageActivationContext.GetEndpoint("ServiceEndpoint").Port;
opt.Listen(IPAddress.IPv6Any, port, listenOptions =>
{
listenOptions.UseHttps(this.GetCertificateFromStore());
});
})
.ConfigureServices(
services => services
.AddSingleton<StatelessServiceContext>(serviceContext))
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
.UseUrls(url)
.Build();
}))
};
}
private X509Certificate2 GetCertificateFromStore()
{
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
store.Open(OpenFlags.ReadOnly);
var certCollection = store.Certificates;
var currentCerts = certCollection.Find(X509FindType.FindBySubjectDistinguishedName, "CN=localhost", false);
return currentCerts.Count == 0 ? null : currentCerts[0];
}
finally
{
store.Close();
}
}
Result: Successful build and deploy code on local SF cluster. But my resource can't be reached
P.S. I will repeat again, if you install new cert by using PowerShell provided by Mircosoft - CertSetup.ps1, it works well for SF application. I was trying to dig in PS script, but I can not understand what I missed.
P.P.S I am new in creating certificates, but it seems strange.
I have installed pfx by CertSetup.ps1. All works well (resource is reachable).
Then I have exported cert to pfx with private key and all extended properties
Delete from LocalMachine (MY and Root), CurrentUser (MY) stores
Install exported pfx to LocalMachine (My and Root), CurrentUser (My) stores
Rebuild & Redeploy code
Resoucre can not be reached
Is it magic? Or I miss something?
Couple details was not enough clear for me, any way. Answer:
If you tried to use your own generated certificate (openssl, makecert or etc), you shoud set privileges for NETWORK SERVICE.
To manually do this on your dev box, open up certlm.msc, expand Personal->Certificates, and right-click your cert. Select All Tasks->Manage private keys and then add NETWORK SERVICE.
More here: https://github.com/Azure/service-fabric-issues/issues/714#issuecomment-381281701

Certificate Signature Verification failed

When I try to upload the certificate to https://identity.apple.com/pushcert/, it tells me the signature is invalid.
I followed step-by-step the Mobile Device Manager documentation and http://www.softhinker.com/in-the-news/iosmdmvendorcsrsigning. I am using C#.NET
The format of the plist_encoded file is correct.
//Load signing certificate from MDM_pfx.pfx, this is generated using signingCertificatePrivate.pem and SigningCert.pem.pem using openssl
var cert = new X509Certificate2(MY_MDM_PFX, PASSWORD, X509KeyStorageFlags.Exportable);
//RSA provider to generate SHA1WithRSA
//Signed private key - PushCertSignature
var crypt = (RSACryptoServiceProvider)cert.PrivateKey;
var sha1 = new SHA1CryptoServiceProvider();
byte[] data = Convert.FromBase64String(csr);
byte[] hash = sha1.ComputeHash(data);
//Sign the hash
byte[] signedHash = crypt.SignHash(hash, CryptoConfig.MapNameToOID("sha1RSA"));
hashedSignature = Convert.ToBase64String(signedHash);
//Read Certificate Chain
String mdm = signCSR.readCertificate(mdmCertificate);
String intermediate = signCSR.readCertificate(intermediateCertificate);
String root = signCSR.readCertificate(rootCertificate);
StringBuilder sb = new StringBuilder(); ;
sb.Append(mdm);
sb.Append(intermediate);
sb.Append(root);
signCSR.PushCertWebRequest(csr, sb.ToString(), hashedSignature);
I am not sure what to place in MDM_pfx.pfx. What I did was that I generated the cst to upload to the enterprise iOS Provisioning portal and I download the certificate generate one.
Then I exported the private key of the CSR I generated and exported it as a .pfx file.
This is the file I used.
was this the correct way?
What you have to upload to https://identity.apple.com/pushcert/ isn't just the certificate, it's a plist (XML) with the certificate chain. A sample Java app is available (http://www.softhinker.com/in-the-news/iosmdmvendorcsrsigning) which you should be able to use for reference.
I solved this problem by using: C:\Program Files (x86)\GnuWin32\bin>openssl pkcs12 -export -out mdmapnspfx.pfx -
inkey mdmpk.pem -in mdm.pem
The key was incorrect, i was not using mdm.pem certificate by it was self-signed.