繁体   English   中英

使用策略和证书即时创建 AWS IoT“事物”

[英]Creating AWS IoT “things” with policies and certificates on-the-fly

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

我正在关注“即时注册”文章以创建、注册和激活我的 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 时,您需要使用此 CA 并使用 openssl 创建证书。 它不能通过使用 AWS SDK 来实现,因为您需要使用您的私有 rootCA 密钥,而 AWS 没有这个主要元素,aws 不应该有它。 使用您自己的 CA 是为了大规模生产,否则您可以使用 AWS 开发工具包创建带有 AWS 的 rootCA 的证书。

使用及时注册

准时注册如何运作?

  1. 您需要创建自己的私有 rootCA 密钥,并且只有您应该拥有它。 您必须保护它应有的安全性。 您创建此私有密钥的证书,AWS IoT 使用它来识别您的私有 rootCA 创建的 ccerts。
  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. 拥有此证书后,您可以在每台设备上仅使用 1 对。 设备第一次尝试连接时,AWS 检测到证书是使用用户的 rootCA 创建的,您可以使用 IoT 规则引擎监听主题并调用 Lambda 来捕获此事件。 该主题的最后一部分是您在 AWS IoT 中的 rootCA 证书 ID

该主题的最后一部分是您在 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 中调用“回调”函数时将执行回调,并且您可以从该 lambda 接收数据。

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

问候,

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM