为什么我的 Raspberry Pi 无法连接到 Google Cloud IoT?

Why won't my Raspberry Pi connect to Google Cloud IoT?

我已经将我的 rsa_private.pem 添加到我的项目的 certs 目录中,并在 IOT Core 控制台中将相应的 rsa_cert.pem public 密钥添加到我的设备。

我也在 certs 目录中 运行 wget https://pki.google.com/roots.pem

我不明白的是,现在生成的 roots.pem 文件有多个 -

----BEGIN CERTIFICATE-----
// RS256_X509 encoded cert here
-----END CERTIFICATE-----

各种Operating CA:Comodo GroupGoDaddyDigiCertEntrust DatacardGlobalSignGoogle Trust Services LLC,这是我第一次生成时原始 root.pem 的内容它。

我尝试在 CA CERTIFICATES 下的控制台中将 root.pem 添加到 my-registry,但出现 Certificate value is too big 错误 `

当我 运行 node gcloud.js 时,如果我在我的 [=33] 中包含 protocolId: 'MQIsdp',我要么在终端中得到 connecting...,然后是多个 close ping =] 被传递给 mqtt.connect() 或者我得到 Error: Connection refused: Not authorized 如果我离开它。

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()

首先,请注意 roots.pem。那里有多个证书,因为 roots.pem 正在覆盖自己 if/when Google 轮换授权证书。所以它包括一堆允许 roots.pem 有效更长时间,仅此而已。如果您可以确定哪个是活动证书(它可能每三四个月更改一次?)您实际上可以删除所有其他证书并只留下一个活动证书。当我在一个只有 200k space 的严重受限的 MC 上工作时,我不得不这样做一段时间。 roots.pem 太大,占用了我设备上的大部分存储空间。这里有更多信息:https://cloud.google.com/iot/docs/how-tos/mqtt-bridge#using_a_long-term_mqtt_domain 关于我们设置的长期域,以启用对长期域使用较小的根证书。

并且您不需要 want/need 将 roots.pem 添加到注册表级证书中,因为只有当您想要使用自己的证书颁发机构来验证正在注册的新设备时才需要这样做.有点乱,不过跟设备注册后授权没有任何关系,是为了防止有人黑你的项目注册自己的设备。

至于为什么代码不起作用:您的 JWT 无效,因为您的过期标签是:exp: parseInt(Date.now() / 1000) + 86400 * 60, // 1 day 比 JWT 的有效期长。 86400秒*60是1440小时...JWT超过24小时的被拒绝。所以这是一个糟糕的错误信息。我的意思是,它在技术上是正确的,因为密码是 JWT,它是无效的,所以它是一个无效的密码……但它可能会更好。我会带回团队,但请尝试将 86400 * 60 更改为 86400,它应该可以工作。

根据以下评论:这也是设备名称。由于它是动态生成的,因此缺少一个字符(破折号而不是下划线)。

旧答案: 至于其余的,我没有看到任何明显的东西,但是如果您删除 async/await 包装器并同步 运行 它会发生什么?另外只需验证您的 SSL 密钥是使用相同的协议(RSA,有或没有 X509 包装器)创建和注册的。我之前遇到过以一种方式注册的问题,而密钥实际上是另一种方式。