Reputation: 2558
I've added my rsa_private.pem
in a certs directory of my project and added the corresponding rsa_cert.pem
public key to my device in the IOT Core console.
I also ran wget https://pki.google.com/roots.pem
from within the certs
directory.
Something I don't understand is that the roots.pem
file that got generated now has multiple -
----BEGIN CERTIFICATE-----
// RS256_X509 encoded cert here
-----END CERTIFICATE-----
with various Operating CA:
s; Comodo Group
, GoDaddy
, DigiCert
, Entrust Datacard
, GlobalSign
and Google Trust Services LLC
which is what the original root.pem had when I first generated it.
I've tried adding the root.pem to my-registry
in the console under CA CERTIFICATES
but get an error that says Certificate value is too big
`
When I run node gcloud.js
I either get connecting...
followed by multiple close
pings in the terminal if I include protocolId: 'MQIsdp'
in my connectionArgs
that gets passed tomqtt.connect()
or I get Error: Connection refused: Not authorized
if I leave it out.
gcloud.js
const fs = require('fs')
const jwt = require('jsonwebtoken')
const mqtt = require('mqtt')
const exec = require('child_process').exec
const projectId = 'my-project'
const cloudRegion = 'us-central1'
const registryId = 'my-registry'
const mqttHost = 'mqtt.googleapis.com'
const mqttPort = 8883
const privateKeyFile = './certs/rsa_private.pem'
const algorithm = 'RS256'
var messageType = 'events'// or event
function createJwt (projectId, privateKeyFile, algorithm) {
var token = {
iat: parseInt(Date.now() / 1000),
exp: parseInt(Date.now() / 1000) + 86400 * 60, // 1 day
aud: projectId
}
const privateKey = fs.readFileSync(privateKeyFile)
return jwt.sign(token, privateKey, {
algorithm: algorithm
})
}
function fetchData () {}
async function configureDevice () {
// Get the device's serial number
await exec('cat /proc/cpuinfo | grep Serial', (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`)
return
}
// Concat serial number w/ project name to meet device id naming requirement starting w/ a letter
const deviceId = `my-project${stdout.split(' ')[1].split('\n')[0]}`
var mqttClientId = 'projects/' + projectId + '/locations/' + cloudRegion + '/registries/' + registryId + '/devices/' + deviceId
var mqttTopic = '/devices/' + deviceId + '/' + messageType
var connectionArgs = {
// protocolId: 'MQIsdp' if I add this property I see "connecting..." get logged in the console
// followed by "close" repeatedly being logged. If I don't add it I get
// "Error: Connection refused: Not authorized"
host: mqttHost,
port: mqttPort,
clientId: mqttClientId,
username: 'unused',
password: createJwt(projectId, privateKeyFile, algorithm),
protocol: 'mqtts',
secureProtocol: 'TLSv1_2_method'
}
console.log('connecting...')
var client = mqtt.connect(connectionArgs)
// Subscribe to the /devices/{device-id}/config topic to receive config updates.
client.subscribe('/devices/' + deviceId + '/config')
client.on('connect', function (success) {
if (success) {
console.log('Client connected...')
sendData()
} else {
console.log('Client not connected...')
}
})
client.on('close', function () {
debugger
console.log('close')
})
client.on('error', function (err) {
debugger
console.log('error', err)
})
client.on('message', function (topic, message, packet) {
console.log(topic, 'message received: ', Buffer.from(message, 'base64').toString('ascii'))
})
function sendData () {
var payload = fetchData()
payload = JSON.stringify(payload)
console.log(mqttTopic, ': Publishing message:', payload)
client.publish(mqttTopic, payload, { qos: 1 })
console.log('Transmitting in 30 seconds')
setTimeout(sendData, 30000)
}
})
}
configureDevice()
Upvotes: 0
Views: 630
Reputation: 3332
So first, just a note on the roots.pem. There's multiple certs in there because the roots.pem is covering itself for if/when Google rotates the authorizing certificate. So it includes a bunch to allow for the roots.pem to be valid for longer, that's all. If you can identify which is the active cert (it changes maybe every three or four months ish?) you can actually delete all the other certs and just leave the one active cert. I had to do that for awhile when I was working on a seriously constrained MC with only 200k space on it. The roots.pem was too big, took up most of my storage on the device. There's more info here: https://cloud.google.com/iot/docs/how-tos/mqtt-bridge#using_a_long-term_mqtt_domain about a long-term domain that we setup to enable using a smaller root cert against a long term domain.
And you don't want/need to add the roots.pem into the registry-level certificate, as that's only needed if you want to use your own certificate authority to validate against new devices being registered. It's a bit confusing, but it doesn't have anything to do with authorizing devices after they're registered, it's about guarding against someone hacking your project and registering their own devices.
As for why the code isn't working: Your JWT is invalid because your expiration tag is: exp: parseInt(Date.now() / 1000) + 86400 * 60, // 1 day
which is longer than JWTs are valid for. 86400 seconds * 60 is 1,440 hours... JWT's longer than 24 hours are rejected. So that's a bad error message. I mean, it's TECHNICALLY correct because the password is the JWT, and that's invalid, so it's an invalid pw...but it could probably be better. I'll take back to the team, but try changing that 86400 * 60 to just 86400 and it should work.
Per comment below: It was also the device name. Since it was dynamically generated, there was a missed character (dash instead of underscore).
Old Answer: As for the rest, I don't see anything blaringly obvious, but what happens if you remove the async/await wrapper, and run it synchronously? Also just verify that your SSL key was created and registered with the same protocol (RSA, with or without the X509 wrapper). I've had issues before with registering one way and have the key actually be the other way.
Upvotes: 1