Создание "вещей" AWS IoT с помощью политик и сертификатов на лету
Я использую NodeJS вместе с AWS JS SDK и AWS IoT Device JS SDK, чтобы автоматически создавать новую вещь и назначать ей сертификаты и политики после ее подключения к моему серверу.
Я следовал статье "Just-in-Time Registration", чтобы создать, зарегистрировать и активировать свой сертификат CA. Насколько я могу судить, сертификат CA успешно добавлен в AWS IoT, активирован и включен для автоматической регистрации.
Я не понимаю, как выполняется этот шаг (цитата из упомянутой статьи):
Когда устройство пытается подключиться с помощью сертификата X.509, который не известен AWS IoT, но был подписан центром сертификации, который был зарегистрирован в AWS IoT, сертификат устройства будет автоматически зарегистрирован 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',
});
Но сейчас у меня нет сертификата и ключа для включения в keyPath
а также certPath
и я не могу создать экземпляр моего устройства без него.
Я пытался создавать сертификаты самостоятельно, используя createKeysAndCertificate()
из AWS SDK, сохраняя их на диск, прикрепляя политику вручную, присоединяя принципал вручную, даже пытался пометить сертификат как "активный" вручную, что-то вроде этого:
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...
но я никогда не видел ни одного сообщения на эту тему...
Что мне здесь не хватает? Как правильно запустить сертификат устройства и генерацию секретного ключа, просто используя мой сертификат Just-in-Time?
1 ответ
Если вы хотите использовать свой собственный CA, вам нужно создать сертификаты с этим CA и с использованием openssl. Это невозможно сделать с помощью AWS SDK, потому что вам нужно использовать свой закрытый ключ rootCA, а в AWS нет этого основного элемента, а в aws его не должно быть. Использование собственного ЦС предназначено для массового производства, в противном случае вы можете использовать AWS SDK для создания сертификатов с AWCA 'rootCA.
Использование только вовремя регистрации
Как вовремя работает регистрация?
- Вам нужно создать свой собственный закрытый ключ rootCA, и только он должен быть у вас. Вы должны защищать его должным образом. Вы создаете сертификат этого закрытого ключа, и он используется в AWS IoT для идентификации сертификатов, созданных вашим личным rootCA.
Так как вам нужно использовать openssl для создания сертификатов для ваших устройств, вы должны использовать инструкции ОС или найти библиотеку, которая может сделать это на уровне NodeJS. Вот пример для создания ваших сертификатов с помощью сценария 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
И эту инструкцию можно вызвать в NodeJ, используя что-то вроде этого:
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. Последняя часть темы - ваш идентификатор rootCA в IoT AWS.
И лямбда выглядит так:
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");
});
}
});
});
});
}
И это все. В первый раз, когда устройство попытается подключиться, произойдет сбой, но этот скрипт будет запускаться в лямбде, после успешного завершения при следующем подключении устройства оно должно работать, поскольку сертификат зарегистрирован и активен.
Использование SDK и Lambda.
Если у вас нет устройств массового производства, я рекомендую использовать SDK и 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, создаст объект, прикрепит политику к сертификатам и прикрепит сертификаты к объекту - и все это только в одной лямбда-функции. Вы можете вызвать эту лямбду, используя что-то вроде этого:
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" лямбда обратный вызов будет выполняться, когда вы вызываете функцию "обратного вызова" в лямбда-выражении, и вы можете получать данные из этого лямбда-выражения.
Предложения Вы должны создать политики IoT с ограничениями, и чтобы использовать все элементы, которые я описал здесь, вам необходимо настроить правильные разрешения.
С Уважением,