【问题标题】:Creating AWS IoT "things" with policies and certificates on-the-fly使用策略和证书即时创建 AWS IoT“事物”
【发布时间】:2018-04-22 11:53:35
【问题描述】:

我将 NodeJS 与 AWS JS SDKAWS IoT Device JS SDK 一起使用,以便在连接到我的服务器后自动创建新事物并为其分配证书和策略。

我关注 "Just-in-Time Registration" article 是为了创建、注册和激活我的 CA 证书。据我所知,CA 证书已成功添加到 AWS IoT、激活并启用自动注册。

我不明白这一步是如何执行的(引用文章):

当设备尝试使用 AWS IoT 不知道但由已向 AWS IoT 注册的 CA 签名的 X.509 证书连接时,AWS IoT 将在新的 PENDING_ACTIVATION 中自动注册设备证书状态。

如何“尝试”连接?由于我使用的是aws-iot-device-sdk-js SDK,并带有手动创建的证书,我通常是这样连接我的设备的:

const device = deviceModule.device({
  host: 'myendpoint.iot.us-east-1.amazonaws.com',
  region: 'us-east-1',
  keyPath: `certs/${deviceID}.key`,
  certPath: `certs/${deviceID}.pem`,
  caPath: 'certs/rootCA.pem',
  clientId: deviceID,
  baseReconnectTimeMs: 4000,
  keepalive: 30,
  protocol: 'mqtts',
});

但现在我没有证书和密钥可包含在keyPathcertPath 中,没有它我无法实例化我的设备。

我尝试自己创建证书,使用 AWS SDK 中的 createKeysAndCertificate(),将它们保存到磁盘,手动附加策略,手动附加委托人甚至尝试手动将证书标记为“活动”,类似于以下内容:

iot.createThing({ thingName: deviceID }, (err, d) => {
  if (err) {
    console.log(err);
  } else {
    allThings[d.thingName] = d;
    iot.createKeysAndCertificate({ setAsActive: true }, (e, c) => {
      if (e) {
        console.log(e);
      } else {
        fs.writeFile(`certs/${deviceID}.pem`, c.certificatePem, (ef, f) => {
          if (ef) throw ef;
        });
        fs.writeFile(`certs/${deviceID}.key`, c.keyPair.PrivateKey, (ef, f) => {
          if (ef) throw ef;
        });
        iot.attachPrincipalPolicy({
          policyName: 'my-testing-policy',
          principal: c.certificateArn,
        }, (ee, cc) => {
          if (ee) {
            console.log(ee);
          } else {
            iot.attachThingPrincipal({
              principal: c.certificateArn,
              thingName: deviceID,
            }, (prerr, prdata) => {
              if (prerr) {
                console.log(prerr);
              } else {
                iot.acceptCertificateTransfer({
                  certificateId: c.certificateId,
                  setAsActive: true,
                }, (ce, cd) => {
                  if (err) {
                    console.log(err);
                  } else {
                    console.log('cert activated.');
                  }
                });
              }
            });
          }
        });
      }
    });
  }
});

但毕竟,当我尝试发布某些内容时,我遇到了一个错误:

Error: unable to get local issuer certificate
    at Error (native)
    at TLSSocket.<anonymous> (_tls_wrap.js:1092:38)
    at emitNone (events.js:86:13)
    at TLSSocket.emit (events.js:185:7)
    at TLSSocket._finishInit (_tls_wrap.js:610:8)
    at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:440:38)

我也尝试订阅特定主题,正如上面同一篇文章中提到的,aws/events/certificates/registered/e3f0a30... 但我从未见过关于该主题的一条消息...

我在这里缺少什么?如何仅使用我的即时证书正确触发设备证书和私钥生成?

【问题讨论】:

  • 首先,您必须使用您自己的 CA,您可以使用它为您的设备生成您自己的证书。您必须在 AWS IoT 中注册此 CA。当 AWS IoT 遇到此 CA 颁发的新证书时,它会将设备置于 PENDING_ACTIVATION 状态。您必须自己创建自己的证书,而不必为此使用 AWS 开发工具包。这是docs.aws.amazon.com/iot/latest/developerguide/… 的文档。 AWS IoT 很棘手,有很多要理解的地方
  • 我确实创建了自己的 CA,我可以在我的 IoT 控制台中看到它,它的状态为“已激活”并且“自动注册”设置为“启用”。一旦设备连接到我的服务器,我现在应该如何使用它来创建我的设备密钥和证书?如果可以以某种方式使用 AWS SDK 执行此操作,我想避免从 NodeJS 调用操作系统命令...
  • @Sapher 你分享的链接......所以,我做了一切到“使用自动/即时注册设备证书”部分......如果你看那段,你会还可以看到这句话:“当设备首次尝试连接到 AWS IoT 时,作为 TLS 握手的一部分,它必须出示已注册的 CA 证书和设备证书。”我从来没有设法做到这一点......“尝试连接”是什么意思?发布消息还是什么?如果我不自己创建“设备证书”,如何出示?我认为这个根 CA 应该为我做这件事?
  • 你的问题不大,不用担心:)。问题是您必须生成自己的证书,为此您必须使用openssl,肯定还有其他方法可以做到这一点,也许AWS-SDK中有一个神奇的功能可以做到,但我知道的方式是低谷openssl。在我给您的链接中,您需要的部分是“使用您的 CA 证书创建设备证书”。 “尝试连接”是指你的设备尝试连接亚马逊,如果亚马逊知道你的 CA,它就会知道这个他不知道的新证书来自你的 CA。
  • @Sapher 哦,好吧,所以我确实必须手动创建设备密钥和证书?那么这个设备证书应该处于“PENDING”状态吗?你能给我一个任何语言的设备“连接尝试”的例子吗? JS、Java、Python、bash 等等 :)

标签: javascript node.js amazon-web-services aws-sdk aws-iot


【解决方案1】:

当您想使用自己的 CA 时,您需要使用此 CA 并使用 openssl 创建证书。它不能通过使用 AWS 开发工具包来制作,因为您需要使用您的私有 rootCA 密钥,而 AWS 没有这个主要元素,aws 不应该有它。使用您自己的 CA 意味着大规模生产,否则您可以使用 AWS 开发工具包通过 AWS 的 rootCA 创建证书。

使用即时注册

即时注册如何运作?

  1. 您需要创建自己的私有 rootCA 密钥,并且只有您应该拥有它。您必须保护它应有的安全性。您创建此私钥的证书,AWS IoT 使用它来识别您的私有 rootCA 创建的 ccert。
  2. 由于您需要使用 openssl 为您的设备创建证书,您应该使用操作系统说明或找到可以在 NodeJS 级别执行此操作的库。以下是使用 OS bash 脚本创建证书的示例:

    mkdir /iot/certsTemp cd /iot/certsTemp openssl genrsa -out $1.key 2048 openssl req -new -key $1.key -out $1.csr -subj "/C=MX/ST=CDMX/L=CDMX/O=CompanyX/OU=IoT/CN=IoT" openssl x509 -req -in $1.csr -CA /iot/CACerts/CACertificate.pem -CAkey /iot/CACerts/CACertificate.key -CAcreateserial -out $1.crt -days 1850 -sha256 cat $1.crt /iot/CACerts/CACertificate.pem > $1-CA.crt cat $1.crt

在 NodeJs 中可以使用类似这样的方式调用此指令:

var sh = spawn('sh', ['bash/generateDeviceCerts.sh', deviceId]);
var pem;
sh.stdout.on('data', (data) => {
    if (data.indexOf('-----BEGIN CERTIFICATE-----') == 0) {
        pem = data.toString();
    }
});
sh.stderr.on('data', (data) => {
    //console.log(`cert stderr: ${data}`);
});
sh.on('close', (code) => {
    if (code == 0) {
        me.uploadCertificatesToS3(deviceId);
    }
});
  1. 获得此证书后,您可以在每台设备上仅使用一对。设备第一次尝试连接时,AWS 检测到证书是使用用户的 rootCA 创建的,您可以使用 IoT 规则引擎监听主题并调用 Lambda 来捕获此事件。主题的最后一部分是您在 AWS IoT 中的 rootCA 证书 ID

而 lambda 看起来像:

var AWS = require('aws-sdk');

exports.handler = function(event, context, callback) {

var region = "us-east-1";

var accountId = event.awsAccountId.toString().trim();

var iot = new AWS.Iot({
    'region': region,
    apiVersion: '2015-05-28'
});
var certificateId = event.certificateId.toString().trim();

var certificateARN = `arn:aws:iot:${region}:${accountId}:cert/${certificateId}`;
var policyName = < Policy name > ;
//Asign IoT Policy to certificate
iot.attachPrincipalPolicy({
    policyName: policyName,
    principal: certificateARN
}, (err, data) => {
    if (err && (!err.code || err.code !== 'ResourceAlreadyExistsException')) {
        callback(err, data);
        return;
    }
    //Active the certificate
    iot.updateCertificate({
        certificateId: certificateId,
        newStatus: 'ACTIVE'
    }, (err, data) => {
        if (err) callback(err, data);
        else iot.createThing(params, function(err, data) {//Create a thing for this certificate
            if (err) callback(err, null);
            else {
                var params = {
                    principal: certificateARN,
                    thingName: <NAME>
                };
                //Attach certificate to this thing
                iot.attachThingPrincipal(params, function(err, data) {
                    if (err) callback(err, null);
                    else callback(null, "SUCCESS");
                });
            }
        });
    });
});
}

这就是全部。设备第一次尝试连接时会失败,但会在 lambda 中运行此脚本,一旦成功完成,下次设备尝试连接时它应该可以工作,因为证书已注册并处于活动状态。

使用 SDK 和 Lambda。

如果您没有量产设备,我建议使用 SDK 和 Lambda 来执行此操作,lambda 代码是这样的:

var AWS = require('aws-sdk');

exports.handler = function(event, context, callback) {

const region = "us-east-1";
const accountId = < YOUR ACCOUNT ID > ;
const BUCKET = < YOUR BUCKET > ;

var certificateARN;

var iot = new AWS.Iot({
    'region': region,
    apiVersion: '2015-05-28'
});

event.data.id = getId();
iot.createKeysAndCertificate({
    setAsActive: true
}, function(err, data) {
    if (err) callback(err, null);
    else {
        certificateARN = `arn:aws:iot:${region}:${accountId}:cert/${data.certificateId}`;
        uploadCertificatesToS3(event.data.id, data);
        iot.attachPrincipalPolicy({
            policyName: < YOUR IOT POLICY NAME > ,
            principal: certificateARN
        }, (err, data) => {
            if (err) callback(err, data);
            else {
                var thingName = event.data.id;
                iot.createThing({
                    thingName: thingName,
                    attributePayload: {
                        attributes: {},
                        merge: false
                    }
                }, function(err, data) {
                    if (err) callback(err, null);
                    else {
                        iot.attachThingPrincipal({
                            principal: certificateARN,
                            thingName: thingName
                        }, function(err, data) {
                            if (err) callback(err, null);
                            else callback(null, event.data);
                        });
                    }
                });
            }
        });
    }
});

function getId() {
    return Math.trunc(new Date().getTime() / 1000) + '-' + Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}

function uploadCertificatesToS3(deviceId, certsData) {
    return Promise.all([
        new Promise(function(resolve, reject) {
            var base64data = new Buffer(certsData.certificatePem, 'binary');
            var s3 = new AWS.S3();
            s3.putObject({
                Bucket: BUCKET,
                Key: `${event.data.project}/devices-certificates/${deviceId}/${deviceId}.crt`,
                Body: base64data
            }, function(err, data) {
                if (err) console.log("Error S3", deviceId);
            });
        }),
        new Promise(function(resolve, reject) {
            var base64data = new Buffer(certsData.keyPair.PrivateKey, 'binary');
            var s3 = new AWS.S3();
            s3.putObject({
                Bucket: BUCKET,
                Key: `${event.data.project}/devices-certificates/${deviceId}/${deviceId}.key`,
                Body: base64data
            }, function(err, data) {
                if (err) console.log("Error S3", deviceId);
            });
        }),
        new Promise(function(resolve, reject) {
            var base64data = new Buffer(certsData.keyPair.PublicKey, 'binary');
            var s3 = new AWS.S3();
            s3.putObject({
                Bucket: BUCKET,
                Key: `${event.data.project}/devices-certificates/${deviceId}/${deviceId}.public.key`,
                Body: base64data
            }, function(err, data) {
                if (err) console.log("Error S3", deviceId);
            });
        })
    ]);
}
}

这将使用 AWS rootCA 创建证书,将它们保存在 s3 中,创建事物,将策略附加到证书并将证书附加到事物,所有这些都在一个 lambda 函数中完成。您可以使用以下方式调用此 lambda:

var AWS = require("aws-sdk");
AWS.config.update({
   region: "us-east-1"
});

new AWS.Lambda().invoke({
    FunctionName: 'createDevice',
    InvocationType: "RequestResponse",
    LogType: "Tail",
    Payload: JSON.stringify({
        data: {
            name: < NAME > ,
            description: < DESCRIPTION > ,
            project: < PROJECT >
        }
    })
}, function(err, data) {
    if (err) console.log(err);
    else console.log(JSON.parse(data.Payload));
});

在“RequestResponse”lambda 执行模式下,当您在 lambda 中调用“callback”函数时,回调将被执行,您可以从该 lambda 接收数据。

建议 您应该创建具有限制的 IoT 策略,并且要使用我在此处描述的所有元素,您需要配置正确的权限。

问候,

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-30
    • 1970-01-01
    • 2019-02-18
    • 1970-01-01
    • 2020-08-05
    • 1970-01-01
    • 2021-12-25
    • 1970-01-01
    相关资源
    最近更新 更多