How to get certificate and convert for ESP32 to connect to paid HiveMQ broker? - mqtt

I am trying to connect to the HiveMQ broker using ESP32, a SIM7020 NB-IoT module and the library Magellan_SIM7020E found at:https://github.com/AIS-DeviceInnovation/Magellan_SIM7020E.
It requires an SSL certificate to be pasted in a header file with this format (here abbreviated), where the lines of XXXXXX are to be replaced with the characters of the certificate:
/= Certificate Authority info =/
/= CA Cert in PEM format =/
const char rootCA[] = {"-----BEGIN CERTIFICATE-----"
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
...
...
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
"XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
"-----END CERTIFICATE-----"};
const char clientCA[] = {"-----BEGIN CERTIFICATE-----"
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
...
...
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
"XXXXXXXXXXXXXXXX"
"-----END CERTIFICATE-----"};
const char clientPrivateKey[] = {"-----BEGIN RSA PRIVATE KEY-----"
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
...
...
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
"-----END RSA PRIVATE KEY-----"};
I managed to work with the free service with this setup (using the non SSL client in the library) but the I am failing with the SSL version used to access the paid broker.
By free service I mean broker.hivemq.com and by paid I mean xxxxxxx.s2.eu.hivemq.cloud.
I tried to use the certificate indicated in the FAQ (https://community.hivemq.com/t/frequently-asked-questions/514) which gives me a file called isrgrootx1.pem but it has a different format.
I tried cutting the .pem into sections with length that match each of the three entries required in the header file but the total length does not match.
The downloaded .pem file has a single block of 1856 characters whereas the ESP32 seems to need three blocks for root, client and client private with lengths of 1116, 1232 and 1592 respectively, adding up to much more than the PEM file length.
Is this the right file?
If so, how do I convert it to the format that I need?
If not, where from can I get the certificate?
I tried to follow a previous answer (Can't connect ESP32 to MQTT). That is for WiFi, not NB-IoT but it seems to require a similar format for the certificate. It is suggeseted to install and use OpenSSL but I can't figure out how to install it and I don't even know if it would actually do what I need if I did manage to install it.
As an alternative is there any way I can access the paid broker without SSL as I don't really need that level of security and it is an added complication, especially with regard to keeping the certificates up to date.

Related

Can Puma read TLS certificates from a variable?

We have a Rails app running in Windows that uses Puma. So far we've stored our SSL/TLS certificates on the filesystem, which seems to be the standard in general, and the way Puma is designed to take in that data when starting up.
We would like to instead keep only an encrypted PKCS#12 file (.12) on disk, that holds all certificate data (one or more certificates and the private key), pulls out the specific certs during puma start up into variables, and then feeds that directly into the Puma ssl_bind command.
So I'm trying to figure out if Puma can accept variables that hold certificate data, as opposed to providing the expected cert_path and key_path that point at plaintext files.
I've tried a few different ways of replacing the file paths with variables, but I only get errors so far (see below for example). I've output the cert key along side the same from the file system, and they look identical to me. I've read other somewhat related SO threads that suggest maybe I need to add newlines or otherwise slightly manipulate the data in my variables, but that line of thinking has confused me so far and I'm not sure if it really pertains to my scenario. I think it comes down to ssl_bind expecting a file path, and likely running "file open" logic under the hood. Does it simply not support taking it directly?
Here is an example of what works today:
# tls.key and tls.crt are already sitting on filesystem
ssl_bind '0.0.0.0', '443', {
key: 'certs/tls.key',
cert: 'certs/tls.crt',
no_tlsv1: true,
no_tlsv1_1: true,
verify_mode: 'none'
}
Here is an example of what we want to do
require 'openssl'
# get p12 password out of secrets at runtime
p12_password = Rails.application.credentials.p12[:password].to_s
# open encrypted p12 file
p12 = OpenSSL::PKCS12.new(File.binread('certs/tls.p12'), p12_password)
# pull out certificate and key from p12
leafkey = p12.key.to_pem
leafcertificate = p12.certificate.to_pem
ssl_bind '0.0.0.0', '443', {
key: leafkey,
cert: leafcertificate,
no_tlsv1: true,
no_tlsv1_1: true,
verify_mode: 'none'
}
The error we receive from the above is:
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/puma-4.3.8/lib/puma/minissl.rb:209:in `key=': No such key file '-----BEGIN EC PRIVATE KEY-----MDECAQEEIBccaYhSLodf 4TRzzWkOE5rr8t Ul0oQHcjYmmoiuvloAoGCCqGSM4jdu73-----END EC PRIVATE KEY-----' (ArgumentError)
This is the valid EC key data, but Puma/ssl_bind appears confused (not surprisingly) that it's not the expected path to a file on disk that contains this data. Can we trick Puma into accepting it directly this way?
Thank you for reading and taking the time to express any thoughts you may have!
This requirement was added as a enhancement in this PR
So far it looks like I was able to update Puma from 4.3.8 directly to 5.6.2 without any fuss. We did need to update 2 options to the *_pem versions, i.e.,
cert becomes cert_pem and
key becomes key_pem
With this in place, it JUST WORKED.
Example running with puma 5.6.2:
require 'openssl'
# using cleartext password for testing/simplicity
p12 = OpenSSL::PKCS12.new(File.binread('certs/tls.p12'), "1234")
leafcertificate = p12.certificate.to_pem
leafkey = p12.key.to_pem
ssl_bind '0.0.0.0', '443', {
cert_pem: leafcertificate,
key_pem: leafkey,
no_tlsv1: true,
no_tlsv1_1: true
}
Personal lessons learned: I should have prioritized digging into the Puma repo: pull requests, issues, etc.

Apple Wallet NFC encryptionPublicKey

In Apple's documentation for the keys available for a Wallet pass, there's an option for a dictionary for NFC-related data. I understand that use of this key requires special permission from Apple. Regardless ...
message is straight forward -- it's the data passed to a NFC terminal (usually a unique identifier for the customer).
encryptionPublicKey, however, has me confused. Apple states it is the public encryption key used by the Value Added Services protocol. Use a Base64 encoded X.509 SubjectPublicKeyInfo structure containing a ECDH public key for group P256.
Can anyone explain what this second sentence means and/or what a developer would have to do to generate this? From what would one even generate the public/private keys?
You'll need the following to generate the public and private key. The private key is used by the merchant hardware when reading the pass and decoding the payload.
The compressed public key is what goes into your pass.json.
openssl ecparam -name prime256v1 -genkey -noout -out nfcKey.pem
openssl ec -in nfcKey.pem -pubout -out nfcPubkey.pem -conv_form compressed
cat nfcPubkey.pem
Outputs:
-----BEGIN PUBLIC KEY-----
MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgAC/Bu9nyAtG1DQe7t7jszLb+dZ1GbX
oR8G0rIXoak67NM=
-----END PUBLIC KEY---
You'll need Base64 key (without the newline) for the encryptionPublicKey field.
E.g.
MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgAC/Bu9nyAtG1DQe7t7jszLb+dZ1GbXoR8G0rIXoak67NM=

How to validate x509 certificate against CRL byte array in iOS

I am using OpenSSL to get the byte array of CRL (certificateRevocationList) with a specific dn from my local ldap server.
The CRL data is an ASN1 byte array as follows:
<308202bb 308201a3 02010130 0d06092a 864886f7 ... d179fa>
My intention is to validate a X509 certificate against the CRL.
I think I can use X509_CRL_get0_by_cert() to verify a certificate
https://www.openssl.org/docs/man1.1.0/crypto/X509_CRL_get0_by_cert.html
But first I need to create a X509_CRL structure from the byte array.
After that I need to verify my certificate against the X509_CRL.
Please help me to find a way.
You need to create BIO first. After that use d2i_X509_CRL_bio to create crl.
Appoximatelly like this:
BIO* memoryBIO = BIO_new_mem_buf(buffer, bufferSize);
X509_CRL* crl = d2i_X509_CRL_bio(memoryBIO, NULL);

Convert NSData to PEM to Sign Certificate in Node.js

I am creating a CSR using this library on iOS and then encoding it as Base 64.
https://github.com/ateska/ios-csr
The library creates the CSR as NS Data on iOS.
I am able to send this data to my Node.JS server. I want to convert this to a PEM so that I can sign this CSR using the server's private key. Does anyone know how to do this?
Thanks
With SCCCSR from ios-csr library you will have a certificate request in PKCS#10 format, encoded in binary
let certificateRequest = sccsr.build(publicKey, privateKey: privateKey)
A PEM format requires a conversion to Base64 and around with the -----BEGIN CERTIFICATE REQUEST----- and -----END CERTIFICATE REQUEST----- headers
let certificateRequestB64 = certificateRequest.base64EncodedStringWithOptions(NSDataBase64EncodingOptions())
let certificateRequestPEM =
"-----BEGIN CERTIFICATE REQUEST-----\\n" + certificateRequestB64 + "\\n-----END CERTIFICATE REQUEST-----\\n"

How can I access HTTPS using direct ip without editing /etc/hosts in iOS?

By default, example.com resolve to 123.123.123.123,
But If I want it to be resolved to 100.100.100.100.
For http, I can simply change the url to http://100.100.100.100 with a header "Host: example.com".
But it's not working for HTTPS.(Error: SSL certificate problem: Invalid certificate chain).
My question is not why, and I do not want to skip the certificate validation.
How can I get the same effect in Objective-C like curl's
--resolve option:
--resolve <host:port:address>
Provide a custom address for a specific host and port pair. Using this, you can make the curl requests(s)
use a specified address and prevent the otherwise normally resolved address to be used. Consider it a sort
of /etc/hosts alternative provided on the command line. The port number should be the number used for the
specific protocol the host will be used for. It means you need several entries if you want to provide
address for the same host but different ports.
In other words, How to make custom DNS query in HTTPS requests in Objective-C?
When you are using https, the address that you use in your request, and the address given to you by the certificate returned by the server, must agree.
If you send a request to https://100.100.100.100 then the server must return a certificate for 100.100.100.100. Even if you connected successfully to https:// www.xyz.com, and www.xyz.com resolved to 100.100.100.100, connecting to https://100.100.100.100 isn't going to work, cannot work, and absolutely must not work, because the server will return a certificate for www.xyz.com and not for 100.100.100.100.
I see following options:
Use your own DNS server with corresponding configuration of host/ip entry
If you want to stick with Objective C, there is a guideline frome apple Overriding SSL Chain Validation Correctly
Use libcurl which supports the feature you mentioned: http://curl.haxx.se/libcurl/c/resolve.html
example
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
CURLcode res = CURLE_OK;
struct curl_slist *host = NULL;
/* Each single name resolve string should be written using the format
HOST:PORT:ADDRESS where HOST is the name libcurl will try to resolve,
PORT is the port number of the service where libcurl wants to connect to
the HOST and ADDRESS is the numerical IP address
*/
host = curl_slist_append(NULL, "example.com:80:127.0.0.1");
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_slist_free_all(host);
return (int)res;
}
Update:
Since author don't want to skip certificate validation this is not an option now:
You can try to ignore ssl certificate in AFNetworking in your case
I want to allow invalid SSL certificates with AFNetworking

Resources