Alamofire ServerTrustPolicy always invalidates ServerTrust - node.js

I'm using Alamofire on Swift 2.0 and implemented the ServerTrustPolicy for my local server as you can see here:
let defaultManager: Alamofire.Manager = {
let serverTrustPolicies: [String: ServerTrustPolicy] = [
// "localhost": .PinCertificates(
// certificates: ServerTrustPolicy.certificatesInBundle(),
// validateCertificateChain: false,
// validateHost: false
// )
"localhost": .DisableEvaluation
]
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPAdditionalHeaders = Alamofire.Manager.defaultHTTPHeaders
return Alamofire.Manager(
configuration: configuration,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)
}()
The problem is when I do a request I always get the same error no matter if I use .PinCertificates or .DisableEvaluation:
Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made."
UserInfo={
NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x60c00004d980>,
NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?,
_kCFStreamErrorDomainKey=3,
_kCFStreamErrorCodeKey=-9802,
NSErrorPeerCertificateChainKey=<CFArray 0x6060001498a0 [0x10bb3c7b0]>{
type = immutable, count = 1, values = (
0 : <cert(0x61600006ed80) s: localhost i: localhost>
)},
NSUnderlyingError=0x6040000ad750 {
Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)"
UserInfo={
_kCFStreamPropertySSLClientCertificateState=0,
kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x60c00004d980>,
_kCFNetworkCFStreamSSLErrorOriginalValue=-9802,
_kCFStreamErrorDomainKey=3,
_kCFStreamErrorCodeKey=-9802,
kCFStreamPropertySSLPeerCertificates=<CFArray 0x6060001498a0 [0x10bb3c7b0]>{
type = immutable, count = 1, values = (
0 : <cert(0x61600006ed80) s: localhost i: localhost>
)}
}
},
NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made.,
NSErrorFailingURLKey=https://localhost:3000/auth/requestToken?auth_appId=d018ccd505db2cb1d5aacabb03fc2f3a,
NSErrorFailingURLStringKey=https://localhost:3000/auth/requestToken?auth_appId=d018ccd505db2cb1d5aacabb03fc2f3a,
NSErrorClientCertificateStateKey=0
}
I tried using
curl --cacert ./ca.pem https://localhost:3000
which throws works just fine
I use a self signed certificate which I generated like this:
Create Root CA private key
openssl genrsa -out ca.key.pem 2048
Self sign my Root CA
openssl req -x509 -new -nodes -key ca.key.pem -days 1024 -out ca.crt.pem
Create the server certificate by generating the key, then csr, then signing it with the CA
openssl genrsa -out server.key.pem 2048
openssl req -new -key server.key.pem -out server.csr.pem
openssl x509 -req -in server.csr.pem -CA ca.crt.pem -CAkey ca.key.pem -CAcreateserial -out server.crt.pem -days 500
I use nodejs's https module as my web server which I configure like this:
require('ssl-root-cas')
.inject()
.addFile('./ca.crt.pem');
var privateKey = fs.readFileSync('./server.key.pem');
var certificate = fs.readFileSync('./server.crt.pem');
var credentials = { key: privateKey, cert: certificate };
var server = https.createServer(credentials, app);
server.listen(port);
I had a look into the Alamofire source code and found out that in the delegate method:
public func URLSession(
session: NSURLSession,
didReceiveChallenge challenge: NSURLAuthenticationChallenge,
completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void))
{
var disposition: NSURLSessionAuthChallengeDisposition = .PerformDefaultHandling
var credential: NSURLCredential?
if let sessionDidReceiveChallenge = sessionDidReceiveChallenge {
(disposition, credential) = sessionDidReceiveChallenge(session, challenge)
} else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
let host = challenge.protectionSpace.host
if let
serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicyForHost(host),
serverTrust = challenge.protectionSpace.serverTrust
{
if serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host) {
disposition = .UseCredential
credential = NSURLCredential(forTrust: serverTrust)
} else {
disposition = .CancelAuthenticationChallenge
}
}
}
completionHandler(disposition, credential)
}
The call to serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host) actually returns true. This means that the error is probably is happening somwhere in the completionHandler code, right?
Am I doing something terribly wrong at handling the certificate stuff?
PS: defaultManager is not beeing deallocated :)

First off, make sure your defaultManager isn't being deallocated. You'll always get the -999 if it is. With that said, if you use .DisableEvaluation then I would assume you have some bigger issues with your SSL configuration server-side.
Are you able to cURL your web service successfully?
I'll update my answer accordingly as you provide more info.

Related

Oracle 19.3: Certificate validation failure when consuming a REST API from PL/SQL

I'm hitting issues consuming a REST API I have written in Node.JS (running on localhost as https) from PL/SQL (using utl_http). This is a proof of concept I'm working on for work, but I've ran into many issues.
Technical environment:
Oracle EE 19.3 setup as multi-tenant with a single pluggable database (PDB1) running Windows 10 Pro on laptop
This is what I've done.
I have created a self signed certificate using openssl as follows:
REM created private key for server - key.pem
openssl genrsa -out key.pem
REM created CSR using private key generated above - csr.prm
openssl req -new -key key.pem -out csr.pem
REM create certificate - cert.pem
openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pem
This has been used successfully on my Node.JS server code (running on https://localhost:4010). I connect using my browser and get past the warnings telling me the certificate is not trusted, etc. I expected this as I've self signed the certificate so no issues here.
The Node.JS server code is below:
const https = require("https");
const fs = require("fs");
const express = require("express");
const app_https = express();
securitydetails = {
key: fs.readFileSync("key.pem"),
cert: fs.readFileSync("cert.pem")
};
// https server port 4010 redirected to express
https
.createServer(securitydetails, app_https)
.listen(4010, function() {
console.log('https (secure) server started on port 4010');
});
// express https root endpoint
app_https.get('/', function(req,res) {
res.write('<h1>HTTPS SECURED</h1>');
res.write('<h1>The Heroes API</h1>');
res.write('<p>Available endpoints:</p>');
res.write('<p>https://localhost:4010/heroes</p>');
res.send();
})
//https: heroes endpoint
app_https.get('/heroes', function(req, res) {
let heroesObj = {
"securityStatus": "HTTPS (SECURE)",
"heroes": [
{"name": "Batman", "antagonist": "Joker"},
{"name": "Black Panther", "antoganist": "Erik Killmonger"},
{"name": "Storm", "antoganist": "Shadow King"},
{"name": "Wonder Woman", "antoganist": "Ares"}
]
};
heroesDetail = JSON.stringify(heroesObj);
res.write(heroesDetail);
res.send();
});
On the Oracle side, I've created a directory for the wallet, and added the cert.pem file to it like so:
REM create wallet
orapki wallet create -wallet E:\oracle\product\19.3\admin\wallet -pwd Stackoverflowisuseful1! -auto_login
REM add certificate cert.pem to wallet
orapki wallet add -wallet D:\oracle\product\19.3\admin\wallet -trusted_cert -cert "E:\certificates\cert.pem" -pwd Stackoverflowisuseful1!
Then checked to see if all is well:
REM display wallet
orapki wallet display -wallet E:\oracle\product\19.3\admin\wallet -complete
All looked well.
I added an ACE for the server:
BEGIN
DBMS_NETWORK_ACL_ADMIN.append_host_ace (
host => 'localhost',
lower_port => 4010,
upper_port => 4010,
ace => xs$ace_type(privilege_list => xs$name_list('http'),
principal_name => 'test1api',
principal_type => xs_acl.ptype_db));
END;
/
The client code written in PL/SQL as follows and run as test1api.
set serveroutput on
DECLARE
url VARCHAR2(50) := 'https://localhost:4010/heroes';
http_request UTL_HTTP.req;
http_response UTL_HTTP.resp;
http_value varchar2(1024) := NULL;
BEGIN
-- Make a HTTP request and get the response.
http_request := UTL_HTTP.begin_request(url);
http_response := UTL_HTTP.get_response(http_request);
UTL_HTTP.READ_LINE(http_response, http_value, TRUE);
DBMS_OUTPUT.PUT_LINE(http_value);
UTL_HTTP.end_response(http_response);
END;
/
And it errors with this:
Error report -
ORA-29273: HTTP request failed
ORA-29024: Certificate validation failure
ORA-06512: at "SYS.UTL_HTTP", line 380
ORA-06512: at "SYS.UTL_HTTP", line 1148
ORA-06512: at line 11
29273. 00000 - "HTTP request failed"
*Cause: The UTL_HTTP package failed to execute the HTTP request.
*Action: Use get_detailed_sqlerrm to check the detailed error message.
Fix the error and retry the HTTP request.
Using get_detailed_sqlerrm we get ...
select utl_http.get_detailed_sqlerrm from dual;
ORA-29024: Certificate validation failure
I've got this whole setup to work against http, so it's certainly my lack of understanding around certificate generation and/or adding certificates to the Oracle wallet and utilising them.
Has anyone got any ideas?
Thanks.

convert x509 ECDSA .pem cert and key to pkcs12

Is it possible to do the same as openssl does with chilkat as below:
I have tried few methods with chilkat but when I try to use it for signing a PDF file it fails.
However if I do convert it with openssl as below it passes
*** ECDSA
# Generate self-signed certificate with ECDSA using two common curves
openssl req -x509 -nodes -days 3650 -newkey ec:<(openssl ecparam -name prime256v1) -keyout ecdsakey.pem -out ecdsacert.pem
# print private and public key + curve name
openssl ec -in ecdsakey.pem -text -noout
# print certificate
openssl x509 -in ecdsacert.pem -text -noout
# generate container
openssl pkcs12 -export -inkey ecdsakey.pem -in ecdsacert.pem -out ecdsacred.p12
My certificate info looks as below:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
5a:ed:46:91:6c:d6:d4:e2:89:14:47:4c:39:62:e8:80:e4:17:e9:3b
Signature Algorithm: ecdsa-with-SHA256
Issuer: C = IE, ST = Dublin, O = AID:Tech, OU = Blockchain, CN = ca
Validity
Not Before: Mar 31 21:10:00 2020 GMT
Not After : Mar 31 21:15:00 2021 GMT
Subject: OU = client, CN = john doe
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:91:f4:62:5b:79:31:59:41:d3:ff:59:8a:41:22:
06:13:34:5e:ce:0a:3f:16:ea:e7:91:fe:53:4f:a3:
ea:63:f7:90:aa:a3:66:72:98:97:01:2a:a6:33:b7:
c2:97:55:bf:83:b4:ca:b4:8e:6f:95:70:1f:da:f7:
f5:a4:00:77:ad
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Subject Key Identifier:
5E:79:09:10:5D:64:BF:68:D7:29:AC:2A:BC:BB:39:2D:FF:12:D7:37
X509v3 Authority Key Identifier:
keyid:87:4B:D2:9C:83:32:05:97:CD:93:7A:25:B7:46:39:DF:AE:19:DE:79
1.2.3.4.5.6.7.8.1:
{"attrs":{"hf.Affiliation":"","hf.EnrollmentID":"john doe","hf.Type":"client"}}
Signature Algorithm: ecdsa-with-SHA256
30:44:02:20:3c:ff:16:5f:58:c9:4b:6f:d3:7e:75:b4:68:60:
07:a3:f7:8e:d8:0f:29:52:ee:86:8f:35:46:d0:a1:d0:f1:ea:
02:20:47:ff:19:02:7a:58:d4:6d:e4:67:4a:ca:c4:67:54:90:
48:8c:b0:70:29:77:97:bb:52:2f:80:7f:5a:e8:d2:0d
Here is method 1 that I tried:
const chilkat = require('#chilkat/ck-node12-macosx');
const os = require('os');
const fs = require('fs')
function chilkatExample() {
var cert = new chilkat.Cert();
var privKey = new chilkat.PrivateKey();
// Load any type of certificate (.cer, .p7b, .pem, etc.) by calling LoadFromFile.
var success = cert.LoadFromFile("static/johnDoeCert.pem");
if (success !== true) {
console.log(cert.LastErrorText);
return;
}
// Load the private key.
// (The various privKey methods that load from a file will automatically detect
// the format. It doesn't actually matter if you try to load a non-PKCS8 format private key
// by calling LoadPkcs8EncryptedFile -- internally Chilkat will detect the format and will load
// based on what it finds.)
success = privKey.LoadPkcs8EncryptedFile("static/privKey.pem","");
if (success !== true) {
console.log(privKey.LastErrorText);
return;
}
// Write the cert as PEM.
success = cert.ExportCertPemFile("qa_output/cert.pem");
// Or get the PEM string directly...
console.log(cert.ExportCertPem());
// Associate the private key with the cert object.
success = cert.SetPrivateKey(privKey);
if (success !== true) {
console.log(cert.LastErrorText);
return;
}
// Write the cert + private key to a .pfx file.
success = cert.ExportToPfxFile("static/myPfx.p12","", true);
if (success !== true) {
console.log(cert.LastErrorText);
return;
}
console.log("Success.");
}
chilkatExample();
And here is method 2 that I tried:
const os = require('os');
const fs = require('fs')
try {
const chilkat = require('#chilkat/ck-node12-macosx');
const CERT = new chilkat.Cert();
const encodedCert = fs.readFileSync('./static/johnDoeCert.pem', { encoding: 'base64' });
const loadedCert = CERT.LoadFromBase64(encodedCert);
if (!loadedCert) {
console.log('failed to load cert')
}
const certChain = CERT.GetCertChain();
if (!CERT.LastMethodSuccess){
console.log('failed to load cert chain')
}
const PRIVKEY = new chilkat.PrivateKey()
const encodedPrivKey = fs.readFileSync('./static/privKey.pem', 'utf8');
const loadedKEy = PRIVKEY.LoadPkcs8EncryptedFile('./static/privKey.pem', "");
if (!loadedKEy) {
console.log('failed to load privagte key')
}
const PFX = new chilkat.Pfx()
const loadedPFX = PFX.AddPrivateKey(PRIVKEY, certChain)
if (!loadedPFX) {
console.log('could not load PFX')
}
const writeP12 = PFX.ToFile("", "./static/johnDoe.p12")
if (!writeP12){
console.log('could not write PFX')
}
} catch (error) {
console.log(error)
}
Thank you!
It seems that `method 1 from above works fine, my problem was on another part of my code that is not related to this.
I did slight modifications to the code above and here is my final result:
const os = require('os');
const fs = require('fs')
const chilkat = require('#chilkat/ck-node12-macosx');
try {
const CERT = new chilkat.Cert();
const PRIVKEY = new chilkat.PrivateKey()
const loadedCert = CERT.LoadFromFile('./static/cert.pem');
if (!loadedCert) {
console.log('failed to load cert')
}
const loadedKEy = PRIVKEY.LoadPkcs8EncryptedFile('./static/privKey.pem', "");
if (!loadedKEy) {
console.log('failed to load privagte key')
}
// Associate the private key with the cert object.
const associatePrivKey = CERT.SetPrivateKey(PRIVKEY);
if (!associatePrivKey) {
console.log(CERT.LastErrorText);
return;
}
// Write the cert + private key to a .pfx file.
const exported = CERT.ExportToPfxFile("static/cert.p12", "password", false);
if (!exported) {
console.log(CERT.LastErrorText);
return;
}
console.log("Success.");
} catch (error) {
console.log(error)
}

How to accept self-signed certificates with LWP::UserAgent

I am attempting to set up a node.js server that uses HTTPS. I will then write a scripts in Perl to make a HTTPS request to the server and measure latency of the round trip.
Here is my node.js:
var express = require('express');
var https = require('https');
var fs = require('fs');
var key = fs.readFileSync('encrypt/rootCA.key');
var cert = fs.readFileSync('encrypt/rootCA.pem');
// This line is from the Node.js HTTPS documentation.
var options = {
key: key,
cert: cert
};
https.createServer(options, function (req, res) {
res.writeHead(200);
res.end("hello world - https\n");
}).listen(8088);
Key/cert generation was done as follows:
openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem
This is my Perl script:
#!/usr/bin/perl
use LWP::UserAgent;
my $ua = LWP::UserAgent->new;
my $req = HTTP::Request->new(GET => 'https://127.0.0.1:8080');
my $res = $ua->request($req);
if ($res->is_success) {
print $res->as_string;
} else {
print "Failed: ", $res->status_line, "\n";
}
Returning the error:
Failed: 500 Can't verify SSL peers without knowing which Certificate Authorities to trust
The node.js documentation describes how to set up an HTTPS server but it is vague about generating primary cert and intermediate cert.
https://medium.com/netscape/everything-about-creating-an-https-server-using-node-js-2fc5c48a8d4e
To make LWP::UserAgent ignore server certificate use the following configuration:
my $ua = LWP::UserAgent->new;
$ua->ssl_opts(
SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE,
verify_hostname => 0
);
The typical answer to such question is to disable certificate validation at all. But, this is totally insecure and essentially disables most of the protection offered by HTTPS. If validation is disabled completely a man in the middle attacker just can use an arbitrary certificate to intercept the connection and sniff and modify the data. Thus, don't do it.
The correct way to deal with such certificates is to add these certificates as trusted. This can be done with the SSL_ca_file argument:
my $ua = LWP::UserAgent->new;
$ua->ssl_opts(SSL_ca_file => 'rootCA.pem');
$ua->get('https://127.0.0.1:8080');
By explicitly trusting the self-signed server certificate as CA it will no longer throw "certificate verify failed".
But, unless your server certificate is actually issued to "127.0.0.1" you will now get "hostname verification failed" since the subject of the certificate does not match the domain of the URL. This can be fixed by setting the expected hostname:
my $ua = LWP::UserAgent->new;
$ua->ssl_opts(
SSL_ca_file => 'rootCA.pem',
SSL_verifycn_name => 'www.example.com',
);
$ua->get('https://127.0.0.1:8080');
Note that SSL_ca_file needs the self-signed certificate to have the CA flag set to true, i.e. that the certificate is a CA certificate which can be used to issue other certificates. If this is not the case with your certificate or if you just want to accept a specific certificate no matter if it is expired, revoked, does not match the hostname etc you can do the validation by using the fingerprint of the certificate.
my $ua = LWP::UserAgent->new;
$ua->ssl_opts(SSL_fingerprint => 'sha1$9AA5CFED857445259D90FE1B56B9F003C0187BFF')
$ua->get('https://127.0.0.1:8080');
The fingerprint here is the same you get with openssl x509 -noout -in rootCA.pem -fingerprint -sha1, only the algorithm added in front (sha1$...) and the colons removed.

Programmatically create certificate and certificate key in Node

Using node.js, I'd like to write code to programmatically do the equivalent of the following:
openssl genrsa -des3 -passout pass:x -out server.pass.key 2048
openssl rsa -passin pass:x -in server.pass.key -out server.key
rm server.pass.key
openssl req -new -key server.key -out server.csr
openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
When complete, I need the RSA key server.key and the self-signed SSL certificate server.crt.
forge looks the most promising, but so far I haven't figured out how to get it to work. I have the following code:
var pki = forge.pki;
var keys = pki.rsa.generateKeyPair(2048);
var privKey = forge.pki.privateKeyToPem(keys.privateKey);
var pubKey = forge.pki.publicKeyToPem(keys.publicKey);
But when I write the pubKey to a file, I've noticed it starts with ...
-----BEGIN PUBLIC KEY-----
MIIB...
-----END PUBLIC KEY-----
... and isn't recognized, whereas using openssl above it starts with:
-----BEGIN CERTIFICATE-----
MIID...
-----END CERTIFICATE-----
Since the original link went dead, I've made my own code that generates a self-signed certificate using node-forge (which it looks like they already have based on the original question), so I thought I'd put it here for someone who wants it
Simply creating a public and private key pair isn't enough to work as a certificate, you have to put in attributes, node-forge is incredibly useful this way, as its pki submodule is designed for this.
First, you need to create a certificate via pki.createCertificate(), this is where you'll assign all of your certificate attributes.
You need to set the certificate public key, serial number, and the valid from date and valid to date. In this example, the public key is set to the generated public key from before, the serial number is randomly generated, and the valid from and to dates are set to one day ago and one year in the future.
You then need to assign a subject, and extensions to your certificate, this is a very basic example, so the subject is just a name you can define (or let it default to 'Testing CA - DO NOT TRUST'), and the extensions are just a single 'Basic Constraints' extension, with certificate authority set to true.
We then set the issuer to itself, as all certificates need an issuer, and we don't have one.
Then we tell the certificate to sign itself, with the private key (corresponding to its public key we've assigned) that we generated earlier, this part is important when signing certificates (or child certificates), they need to be signed with the private key of its parent (this prevents you from making fake certificates with a trusted certificate parent, as you don't have that trusted parent's private key)
Then we return the new certificate in a PEM-encoded format, you could save this to a file or convert it to a buffer and use it for a https server.
const forge = require('node-forge')
const crypto = require('crypto')
const pki = forge.pki
//using a blank options is perfectly fine here
async function genCACert(options = {}) {
options = {...{
commonName: 'Testing CA - DO NOT TRUST',
bits: 2048
}, ...options}
let keyPair = await new Promise((res, rej) => {
pki.rsa.generateKeyPair({ bits: options.bits }, (error, pair) => {
if (error) rej(error);
else res(pair)
})
})
let cert = pki.createCertificate()
cert.publicKey = keyPair.publicKey
cert.serialNumber = crypto.randomUUID().replace(/-/g, '')
cert.validity.notBefore = new Date()
cert.validity.notBefore.setDate(cert.validity.notBefore.getDate() - 1)
cert.validity.notAfter = new Date()
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1)
cert.setSubject([{name: 'commonName', value: options.commonName}])
cert.setExtensions([{ name: 'basicConstraints', cA: true }])
cert.setIssuer(cert.subject.attributes)
cert.sign(keyPair.privateKey, forge.md.sha256.create())
return {
ca: {
key: pki.privateKeyToPem(keyPair.privateKey),
cert: pki.certificateToPem(cert)
},
fingerprint: forge.util.encode64(
pki.getPublicKeyFingerprint(keyPair.publicKey, {
type: 'SubjectPublicKeyInfo',
md: forge.md.sha256.create(),
encoding: 'binary'
})
)
}
}
//you need to put the output from genCACert() through this if you want to use it for a https server
/* e.g
let cert = await genCACert();
let buffers = caToBuffer(cert.ca);
let options = {};
options.key = buffers.key;
options.cert = buffers.cert;
let server = https.createServer(options, <listener here>);
*/
function caToBuffer(ca) {
return {
key: Buffer.from(ca.key),
cert: Buffer.from(ca.cert)
}
}
Do with this what you will.
Okay, as you probably realized, I wasn't generating a certificate. It required quite a bit more work, which you can find here.
Essentially, after a bunch of setup, I had to create, sign, and convert the certificate to Pem:
cert.sign(keys.privateKey);
var pubKey = pki.certificateToPem(cert);
Hope this helps someone else!

Can nodejs generate SSL certificates?

I'm writing a proxy support HTTPS-HTTPS proxy. Before i use Python as the main programming language, but i'm interested in node.js now, so i prepare to migrate.
The largest problem i'm facing is that i don't know how to generate CA and other server certificates in node.js. In Python, there is pyOpenSSL which is awesome. I don't find something similar in node.js until now.
Maybe i should use openssl-fork method? But how to handle the interactive operation in openssl.
Thank you.
Use shell for certificate:
openssl genrsa -out server-key.pem 1024
openssl req -new -key server-key.pem -out server-csr.pem
openssl x509 -req -in server-csr.pem -signkey server-key.pem -out server-cert.pem
Then use them in node.js
var https = require('https');
https.createServer({
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
},
function (req,res) {
...
})
EDIT:
You can also give a try to this project in NPM :
https://npmjs.org/package/openssl-wrapper
I found it searching the NPM repo : https://npmjs.org/search?q=openssl
I did not tried or checked it myself, but it looks like a way to generate the certificate using node, which is the original question.
var openssl = require('openssl-wrapper');
var password = 'github';
return openssl.exec('genrsa', {des3: true, passout: 'pass:' + password, '2048': false}, function(err, buffer) {
console.log(buffer.toString());
});
I'd be interested by a feedback. ;)
In case somebody does want to programatically create CSRs from node.js, I have created a nodejs module which uses openssl to create a private key and a CSR.
Edit: Use pem instead. It is much more complete and probably more reliable.
Edit2: Actually, pem is also just a simple wrapper over openssh. For a pure js implementation, look into forge
node-forge allow that. Nothing to say more. DOES NOT use openssl shell command internally.
https://github.com/digitalbazaar/forge#x509
there is a pure javascript library called "Jsrsasign" to generate CSR certificates.
const r = require("jsrsasign");
const kp = r.KEYUTIL.generateKeypair("RSA", 2048);
const csr = r.KJUR.asn1.csr.CSRUtil.newCSRPEM({
subject: {},
sbjpubkey: kp.pubKeyObj,
sigalg: "SHA256withRSA",
sbjprvkey: kp.prvKeyObj,
});
const privateKey = r.KEYUTIL.getPEM(kp.prvKeyObj, "PKCS1PRV");
for more docs visit http://kjur.github.io/jsrsasign/
None of the node libraries seem to support the options I need, so I use the openssl executable.
import { execSync } from 'child_process'
import fs from 'fs'
import tempy from 'tempy'
const extHeader = `authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = #alt_names
[alt_names]
`
const shell = cmd => execSync(cmd, { stdio: 'pipe' })
const writeCert = (extFile, outfile) => {
const cmd = [
`openssl`,
`x509`,
`-req`,
`-in ssl/my.csr`,
`-CA ssl/root-ca.pem`,
`-CAkey ssl/root-ca.key`,
`-CAserial ssl/root-ca.srl`,
`-out ssl/${outfile}`,
`-days 1825`,
`-sha256`,
`-extfile ${extFile}`,
`-passin pass:mypassphrase`
]
shell(cmd.join(' '))
}
const createCert = domains => {
const sans = domains.map((d, i) => `DNS.${i + 1} = ${d}`)
const ext = extHeader + sans.join('\n')
const extFile = tempy.file()
fs.writeFileSync(extFile, ext, 'utf-8')
writeCert(extFile, 'out.crt')
}
Dependencies:
openssl executable
yarn add tempy

Resources