Request with X509 Certificate - node.js

I have received a X509 certificate (one .cer file), I can decode it, so no problems on that. Now I want to sign a request with this certificate in node, but I can't get this to work:
var https = require("https");
var fs = require("fs");
var options = {
host: 'management.core.windows.net',
path: '/my-subscription-id/services/hostedservices',
port: 443,
method: 'GET',
cert: fs.readFileSync("./SSLDevCert.cer"),
agent: false
};
var req = https.request(options, function(res) {
console.log("statusCode: ", res.statusCode);
console.log("headers: ", res.headers);
res.on('data', function(d) {
process.stdout.write(d);
});
});
This fails with
Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
at Object.createCredentials (crypto.js:72:31)
at Object.connect (tls.js:857:27)
at Agent._getConnection (https.js:61:15)
at Agent._establishNewConnection (http.js:1183:21)
Doing the same in C# works fine:
var req = (HttpWebRequest)WebRequest.Create(string.Format("https://management.core.windows.net/{0}/services/hostedservices", "my-subscription-id"));
req.ClientCertificates.Add(new X509Certificate2(File.ReadAllBytes("./SSLDevCert.cer"));
var resp = req.GetResponse();

PEM_read_bio expects certificate in PEM format, while you have certificate in "raw" DER format. Obviously you need to convert your certificate to PEM format.
BTW .cer files in DER format don't contain private key and can't be used for signing anything.
You need to re-check what you actually have in your .cer file and in what format.

A follow up on this:
Only .cer file probably means that the private key is in the certificate (well that's the case with the Azure certs), you will have to transform in a PEM file (that starts with ----BEGIN RSA PRIVATE KEY----) and then do a request with:
var key = fs.readFileSync("./key.pem");
var options = {
cert: key,
key: key
}
Getting the private key from the file can be a bit tricky, but this worked on Azure certificates, so it might help any of you:
openssl pkcs12 -in ' + file + ' -nodes -passin pass:
(note the empty pass argument)

Related

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 can i pass the base64 string as a input certificate for Openssl?

I have a certificate which i am getting in string . I just want that string to be directly pass as -in in the openssl command.
But i don't see any method in openssl , can you help.
my code
const { exec } = require('child_process');
exec('openssl x509 -noout -issuer -in '+certificateString , (err, stdout, stderr) => {
if (err) {
console.log(err);
}else{
console.log(studout);
}
});
If i directly pass certificate file url it works, like this
openssl x509 -noout -issuer -in certificate.pem
this work
but how can i pass the certificate string directly in openssl ?
Any help?
First, do you have base64 or PEM? Those are not the same thing; although PEM includes base64, it is not only base64. If you have a file that works with openssl x509 -in file then it is PEM, NOT only base64. People seem to look at PEM, see more than 40 base64 characters, and their brains shut down and become unable to see the dashes-BEGIN line, the dashes-END line, and the line breaks, all of which are REQUIRED.
Second, certificate.pem is a filename or pathname, not a URL. A filename or pathname is not a URL, and a URL is not a filename or pathname, although some URL schemes (and particularly the only ones most people notice) include some elements in common with pathnames.
The certificate input to openssl x509 must be either a (named) file, or standard input (commonly abbreviated stdin). If you don't want to supply it as a named file, nodejs can provide data to a child's stdin, but not with the higher-level exec* methods, only the more basic spawn:
const{ spawn } = require('child_process');
const pem = `-----BEGIN CERTIFICATE-----
MIIDUzCCAjugAwIBAgIJANK2Ysp8bp+6MA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJDQTETMBEGA1UEBwwKVGluc2VsdG93bjEPMA0GA1UE
CgwGRGlzbmV5MB4XDTE5MDUwOTA5MTQ0NVoXDTIwMDUwODA5MTQ0NVowQDELMAkG
A1UEBhMCVVMxCzAJBgNVBAgMAkNBMRMwEQYDVQQHDApUaW5zZWx0b3duMQ8wDQYD
VQQKDAZEaXNuZXkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCuKNJI
oBJ6acFSqMlG6e6WS44jC0RP+2y0q+02P4xc9BIuPjKV6/Lkg3bnYenAzktemvc7
EVFOUS/Ema4UIc+aDtSpjAegWnZNrzX+K76Xxzw+RnZalXB1Z++CpTdtsgSmkrmR
wJ7ZZpclAK+Yt6Ggx9ea3/d8WJ85V30ezcG7hPf5BrCSxzjSPsxG3heDPh1/X0zk
H7PD0JB+IW08yOikLmQNZeTZXaIAaSXoIPj5L9Ax7kyDEiDcSBIcQbPGMfIG6CPO
hKOM4yZKWni0mO9jwgfYNU6Bxei35/KTVwBWXHck9N7DdEtoST9THYO7ZFqqvTdk
mLfBpsPXorFT+vAVAgMBAAGjUDBOMB0GA1UdDgQWBBQyXFJDoapFe4JaZBD1xVYE
ImDj7DAfBgNVHSMEGDAWgBQyXFJDoapFe4JaZBD1xVYEImDj7DAMBgNVHRMEBTAD
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAwFXI13uxhGz+fem7N03r0+dnNaXZQQ9CR
owTHVVOjsfrsbFPhKdZIKMKeqpc1AksqynhbV2zY5VjINMab8ADw165gTCgy8/0S
X3QQsy2P5RNx/YuRMvs6hP7ZhZQlabLVbBnCWqAevT2qEZ7Gmi+m9A9sdK2Hsrkj
0lxGCozscme7E3ZfR/3GQVzyfZVppRLsgIth9F2y6SyLXwi+v39C+a9vdZjMS3Uy
HuRD9Sk8xydWywI8wKBlfnX4KGMBjKpSDpMeb6723eXuPC+soUBafuUoP+fWqjg4
LFgYg1TtyzfdrkkWZ9/KxS47OxkF6BAQtFGVF2nNgcpdXxToK7pP
-----END CERTIFICATE-----
`;
const p = spawn("/path/to/openssl", ["x509","-noout","-issuer"]);
// options.stdio defaults to ['pipe','pipe','pipe']
p.stdout.on('data',(data)=> {console.log(data.toString())} );
p.stderr.on('data',(data)=> {console.log("ERROR:"+data.toString())} );
p.on('close',()=> {} );
p.stdin.write(pem); p.stdin.end();
However, you don't need to run an external program to parse a certificate; there are lots of JS libraries to do it. For example, with the second one npmjs finds for me:
const { Certificate } = require('#fidm/x509');
const { ASN1 } = require('#fidm/asn1');
var iss = Certificate.fromPEM(pem).issuer.attributes;
var s = ""; for(var a of iss){ s += "/" + a.shortName + "=" + a.value; }
console.log(s);
If you actually did have base64 and not PEM, replace the third line with
const bin = Buffer.from(b64,'base64');
var iss = new Certificate(ASN1.fromDER(bin)).issuer.attributes;

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!

Alamofire ServerTrustPolicy always invalidates ServerTrust

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.

How to setup an EV Certificate a node.js server

I've received four files from Comodo:
AddTrustExternalCARoot.crt
COMODORSAAddTrustCA.crt
COMODORSAExtendedValidationSecureServerCA.crt
mydomain.crt
This is my first time setting up a https server.
I know that I have to put on parameters that is passed to https.createServer but my problem is I don't know which one is the correct property.
The server certificate is set as cert, whereas your CA certificates are set under ca:
var fs = require('fs'),
https = require('https');
var cfg = {
key: fs.readFileSync('/path/to/privatekey.pem'),
cert: fs.readFileSync('/path/to/mydomain.crt'), // PEM format
ca: [
fs.readFileSync('/path/to/AddTrustExternalCARoot.crt'), // PEM format
fs.readFileSync('/path/to/COMODORSAAddTrustCA.crt'), // PEM format
fs.readFileSync('/path/to/COMODORSAExtendedValidationSecureServerCA.crt') // PEM format
]
};
https.createServer(cfg, function(req, res) {
// ...
}).listen(443);
Or you can use just pfx if you have your key, cert, and ca files all bundled into a single PFX/PKCS12-formatted file.

Resources