ESP32使用JITR方式连接AWS平台

ESP32使用JITR方式连接AWS平台

说明:JITR即即时注册,AWS(亚马孙平台)是国外的非常热的一个云平台,该平台采用了TLS 1.2双向认证体系,即意味着IOT设备端需要安装IOT设备证书,并且签发该证书所使用的CA证书需要被IOT Core授信,从而完成IOT Core对 IOT设备端的认证。JITR则是认证方式中的其中一个方法。

背景需要:

从AWS建立IOT设备必须获取证书,有三种方式
1、登陆账号,按照提示一步一步创建IOT事物并将证书下载并烧录进IOT设备,具体参考
缺点:步骤过于麻烦,当商业量产设备时不适用
2、利用JITR方式,好处:可以不需要在平台上一个一个的建立事物,为事物创建规则,直接在电脑的软件上运行就行。
缺点:依然需要将证书下在到IOT设备中,由于每个设备需要配对不一样的证书,故使用起来还是不太方便,但相对于上个方法来说,更好。
3、利用CVM方案(推荐),适用于设备在生产的过程中没有预装任何证书。当用户第一次使用设备的时候,设备会自动的在AWS平台上建立事物并自动下载证书便于下次连接。详细的在ESP32使用CVM方式连接AWS平台(还没写的)。

JITR实现步骤:

  1. 创建,注册和激活将用于签署设备证书的CA证书。
  2. 启用证书的自动注册。
  3. 创建由CA签名的设备证书并将其安装在您的设备(电脑)上。
  4. 使用激活证书的AWS Lambda操作创建并附加规则,然后创建策略并将其附加到证书。
  5. 使用设备证书连接到AWS IoT。

设备端:

一、创建CA证书:
使用 AWS CLI 和 OpenSSL:安装AWS CLI、安装 OpenSSL
将上面两个软件安装好之后,首先,使用OpenSSL来创建一个CA证书,输入命令

$ openssl genrsa -out sampleCACertificate.key 2048
$ openssl req -x509 -new -nodes -key sampleCACertificate.key -sha256 -days 365 -out sampleCACertificate.pem

如图所示:
ESP32使用JITR方式连接AWS平台_第1张图片
二、创建好CA证书之后(可以到安装软件的路径去查看),需要注册码对证书进行注册,使用 AWS CLI 获取随机生成的注册码,输入命令:

aws iot get-registration-code

将会返回一段注册码,如图所示:
ESP32使用JITR方式连接AWS平台_第2张图片
那个f8…就是注册码,然后先保存好注册码。

三、接下来使用OpenSSL 和注册码创建CSR,在CSR创建的过程中,会要求输入一些信息,可以直接按回车不用管,但【Common Name】字段中输入注册码,邮箱也不用管,如图所示:
ESP32使用JITR方式连接AWS平台_第3张图片
四、然后使用 CSR 和样本 CA证书创建新的证书 ,用OpenSSL输入:

$ openssl genrsa -out privateKeyVerification.key 2048
$ openssl req -new -key privateKeyVerification.key -out privateKeyVerification.csr

输入刚才一样的信息,如图:
ESP32使用JITR方式连接AWS平台_第4张图片
密码这些不用管,可以直接回车,然后看看你的路径是否存在下面圈起来的几个证书
ESP32使用JITR方式连接AWS平台_第5张图片
可以看到,在openssl的bin文件里已经存在证书,现在结合输出新的证书输入命令:

$ openssl x509 -req -in privateKeyVerification.csr -CA sampleCACertificate.pem -CAkey sampleCACertificate.key -CAcreateserial -out privateKeyVerification.crt -days 365 -sha256

得到privateKeyVerification.crt,如下图红圈,右边红圈是返回之前输入的信息,不用管:
ESP32使用JITR方式连接AWS平台_第6张图片
五、注册完CA证书后,使用CA证书签发设备证书
创建一个设备证书的私匙 device.key 和对应的证书请求文件 deviceCert.csr 运行下面两条指令:和签发设备证书 deviceCert.crt

$ openssl genrsa -out deviceCert.key 2048
$ openssl req -new -key deviceCert.key -out deviceCert.csr
$ openssl x509 -req -in deviceCert.csr -CA sampleCACertificate.pem -CAkey sampleCACertificate.key -CAcreateserial -out deviceCert.crt -days 365 -sha256

ESP32使用JITR方式连接AWS平台_第7张图片
将证书烧录进设备,设备端完成。

AWS平台端

一、调用lambda函数和IOT规则实现设备证书在AWS IOTCore上的激活和权限附加
(JITR的方式好处就在这里,可以自动完成证书的注册以及附加策略,不然需要手动注册)

当 IoT 设备第一次连接 AWS IoT Core 时,如果它集成的设备证书是由已在 Core 上注册的 CA 证书签发而来,那么相应的设备证书会实现自动注册,注册后的默认状态为“PENDING_ACTIVATION”,意味着虽然设备证书已经成功注册,但是还处于等待激活的状态。同时,这个连接动作默认会发一条消息到 AWS IoT Core 的 MQTT Topic “$aws/events/certificates/registered/” 上,这条消息事件会是如下的格式:

{
 "certificateId": "", 
"caCertificateId": "", 
"timestamp": "", 
"certificateStatus": "PENDING_ACTIVATION", 
"awsAccountId": "",
 "certificateRegistrationTimestamp": ""
 }

Lambda函数执行是可以由事件进行触发的,所以我们的设备需要向上面的那个主题发送消息来触发函数帮我们激活证书以及附加策略
1、创建Lambda函数,选择服务中的Lambda,进入创建函数
ESP32使用JITR方式连接AWS平台_第8张图片
选择重头开始创作 ,输入名称, 语言Node.js 10.x,
角色选择自定义角色
在这里插入图片描述
点击转到控制台
ESP32使用JITR方式连接AWS平台_第9张图片
然后我们创建策略
点击JSON,替换以下内容

{  
   "Version":"2012-10-17",
   "Statement":[  
      {  
         "Effect":"Allow",
         "Action":[  
            "logs:CreateLogGroup",
            "logs:CreateLogStream",
            "logs:PutLogEvents"
         ],
         "Resource":"arn:aws:logs:*:*:*"
      },
      {  
         "Effect":"Allow",
         "Action":[  
            "iot:UpdateCertificate",
            "iot:CreatePolicy",
            "iot:AttachPrincipalPolicy"
         ],
         "Resource":"*"
      }
   ]
}

ESP32使用JITR方式连接AWS平台_第10张图片
再保存即可。
创建好角色后,返回 Lambda 函数,替换一下内容:


/** 
	This node.js Lambda function code creates and attaches an IoT policy to the 
	just-in-time registered certificate. It also activates the certificate. The Lambda
	function is attached as a rule engine action to the registration topic 
	Saws/events/certificates/registered/
	**/
	
	var AWS = require('aws-sdk');
	
	exports.handler = function(event, context, callback) {
	
	//根据所需更改,
	var region = "us-east-2";
	
	var accountId = event.awsAccountId.toString().trim();
	
	var iot = new AWS.Iot({'region': region, apiVersion: '2015-05-28'});
	var certificateId = event.certificateId.toString().trim();
	
	//根据所需代替foo/bar
	var topicName = `foo/bar/${certificateId}`;
	
	var certificateARN = `arn:aws:iot:${region}:${accountId}:cert/${certificateId}`;
	var policyName = `Policy_${certificateId}`;
	
	//Policy that allows connect, publish, subscribe and receive
	var policy = {
	"Version": "2012-10-17",
	"Statement": [
	{
	"Effect": "Allow",
	"Action": [
	"iot:Connect"
	],
	"Resource": `arn:aws:iot:${region}:${accountId}:client/${certificateId}`
	},
	{
	"Effect": "Allow",
	"Action": [
	"iot:Publish",
	"iot:Receive"
	],
	"Resource": `arn:aws:iot:${region}:${accountId}:topic/${topicName}/*`
	},
	{
	"Effect": "Allow",
	"Action": [
	"iot:Subscribe",
	],
	"Resource": `arn:aws:iot:${region}:${accountId}:topicfilter/${topicName}/#`
	}
	]
	};
	
	/*
	Step 1) Create a policy
	*/
	iot.createPolicy({
	policyDocument: JSON.stringify(policy),
	policyName: policyName
	}, (err, data) => {
	//Ignore if the policy already exists
	if (err && (!err.code || err.code !== 'ResourceAlreadyExistsException')) {
	console.log(err);
	callback(err, data);
	return;
	}
	console.log(data);
	
	/*
	Step 2) Attach the policy to the certificate
	*/
	iot.attachPrincipalPolicy({
	policyName: policyName,
	principal: certificateARN
	}, (err, data) => {
	//Ignore if the policy is already attached
	if (err && (!err.code || err.code !== 'ResourceAlreadyExistsException')) {
	console.log(err);
	callback(err, data);
	return;
	}
	console.log(data);
	/*
	Step 3) Activate the certificate. Optionally, you can have your custom Certificate Revocation List (CRL) check
	logic here and ACTIVATE the certificate only if it is not in the CRL. Revoke the certificate if it is in the CRL
	*/
	iot.updateCertificate({
	certificateId: certificateId,
	newStatus: 'ACTIVE'
	}, (err, data) => {
	if (err) {
	console.log(err, err.stack); 
	callback(err, data);
	}
	else {
	console.log(data); 
	callback(null, "Success, created, attached policy and activated the certificate " + certificateId);
	}
	});
	});
	});
	
	}

点击右上角保存即可。

创建完lambda函数后,我们继续创建IOT规则
1、进入AWS IOT界面
2、选择“行动”,然后点击创建规则(在此之前先把CA证书的ID保存下来,去证书哪可以获取)
(273beace3113c6d0a7627cb85*****************831933870ce5fead5aa9e)CAID
填写名称、描述,下面填写这个(属性(Attribute)用*代替)

ESP32使用JITR方式连接AWS平台_第11张图片
在设置一个或多个操作中点击“添加操作”, 选择“调用 Lambda 函数,传递消息数据”,然后点击“配置操作”。(选择刚才建好的lambda函数),这样就可以实现JITR方式了。

最后:

看完这个可能步骤有点多,还有顺序有点乱,但为什么说这种方式会比方式一更好呢,原因在于,这些命令是只要一次的。接下来只要使用之前下载下来的证书和在平台上创建好的规则就行。
重复操作以下步骤就可以获新的证书,我用C 写了一个处理证书程序(由方式一可以知道将证书合成一个文件,和程序一起烧录进IOT设备)

1、首先,合成ca证书和设备证书的,windows命令如下:

copy deviceCert.crt sampleCACertificate.pem> deviceCertAndCACert.crt

路径,找到自己的证书路径
d:
cd Program Files\OpenSSL-Win64\bin
cat deviceCert.crt sampleCACertificate.pem> deviceCertAndCACert.crt

$ openssl genrsa -out deviceCert.key 2048
$ openssl req -new -key deviceCert.key -out deviceCert.csr
$ openssl x509 -req -in deviceCert.csr -CA sampleCACertificate.pem -CAkey sampleCACertificate.key -CAcreateserial -out deviceCert.crt -days 365 -sha256

合成后打开发现两个文本合成了一起,我们需要用这个新的证书作为我们的设备证书,将该证书、和设备的私匙device.key,还有官方的AWS IOT根证书烧进设备,然后向上面lambda函数中自定义的主题发送,就可以在云台上生成证书并激活,设备可以就直接使用

#include 
#include 
#include 

#define N 100   //足以读取完每个证书的每一行

/*
http://c.biancheng.net/view/2054.html

"r"   只允许读取
"w"   只写
"a"   追加,没有文件则创建文件,有则在末尾继续写入数据
"r+"  读写,文件必须存在
"w+"  读写,没有文件则创建文件,有则清空内容重新写入
"a+"  读写,没有文件则创建文件,有则在末尾继续写入数据

//控制读写方式(默认是文本)
t  文本
b  二进制


读写权限和读写方式可以组合使用,但是必须将读写方式放在读写权限的中间或者尾部(换句话说,不能将读写方式放在读写权限的开头)。例如:
将读写方式放在读写权限的末尾:"rb"、"wt"、"ab"、"r+b"、"w+t"、"a+t"
将读写方式放在读写权限的中间:"rb+"、"wt+"、"ab+"

*/


//读取字符串函数fgets
//把n设置变大,读完一行就不会往下继续读
//char *fgets(char *str,int n,FILE *fp)


//写字符串函数fputs()
//int fputs(char *str ,FILE *fp);
//str 写入的字符串,fp为文件指针,写入成功则返回非负数,失败返回EOF


//root根证书的合成函数
void RootCert_compose();

//ca和证书合成函数
void CACert_compose();

//密钥合成函数
void DeciveKey_compose();

FILE *aws_iot_certficates;//创建的文件






int main()
{


    //创建文件aws_iot_certficates
    while((aws_iot_certficates=fopen("C://Users//Administrator//Desktop//C语言命令//cert//aws_iot_certficates.c","a+t"))==NULL)
    printf("aws_iot_certficates.c is building\n");

    fputs("#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n",aws_iot_certficates);

    //合成root证书
    RootCert_compose();
    //合成密钥
    DeciveKey_compose();
    //合成ca证书
    CACert_compose();

    puts("File is building!");
    //关闭文件
    fclose(aws_iot_certficates);

    return  0;

}



void RootCert_compose()
{
  //打开文件
  FILE *RootCert;
  char str[N+1];
  int str_legth=0;//字符串长度

  fputs("const char aws_root_ca_pem[] = {\"",aws_iot_certficates);

  while((RootCert=fopen("C://Users//Administrator//Desktop//C语言命令//cert//root_cert.txt","rt"))==NULL);

  printf("RootCert_composing\n");

  //读取文件的一行
  //fgets()会读取换行符
  while(fgets(str,N,RootCert)!=NULL)
  {

    //得到长度
    str_legth=strlen(str);
    str[str_legth-1]='\\';//替换\n
    //进行证书末尾的判断
    if(strcmp(str,"-----END CERTIFICATE-----\\")==0)
    {
      strcat(str,"n\"};\n\n");//替换字符串
    }
    else
    {
     strcat(str,"n\\\n");//替换字符串
    }

    fputs(str,aws_iot_certficates);

  }

  fclose(RootCert);
  printf("RootCert_composed\n");

}




void DeciveKey_compose()
{
  //打开文件
  FILE *DeciveKey;
  char str[N+1];
  int str_legth=0;//字符串长度

  fputs("const char private_pem_key[] = {\"",aws_iot_certficates);

  while((DeciveKey=fopen("C://Users//Administrator//Desktop//C语言命令//cert//deviceCert.key","rt"))==NULL);

  printf("DeciveKey_composing\n\n");

  //读取文件的一行
  //fgets()会读取换行符
  while(fgets(str,N,DeciveKey)!=NULL)
  {

    //得到长度
    str_legth=strlen(str);
    str[str_legth-1]='\\';//替换\n
    //进行证书末尾的判断
    if(strcmp(str,"-----END RSA PRIVATE KEY-----\\")==0)
    {
      strcat(str,"n\"};\n\n");//替换字符串
    }
    else
    {
     strcat(str,"n\\\n");//替换字符串
    }

    fputs(str,aws_iot_certficates);

  }

  //关闭文件
  fclose(DeciveKey);
  printf("DeciveKey_composed\n\n");
}



void CACert_compose()
{
   //打开文件
  FILE *deviceCert;
  FILE *sampleCACertificate;
  char str[N+1];
  int str_legth=0;//字符串长度

  fputs("const char certificate_pem_crt[] = {\"",aws_iot_certficates);
  //打开文件
  while((deviceCert=fopen("C://Users//Administrator//Desktop//C语言命令//cert//deviceCert.crt","rt"))==NULL);
  while((sampleCACertificate=fopen("C://Users//Administrator//Desktop//C语言命令//cert//sampleCACertificate.pem","rt"))==NULL);

  printf("CACert_composing\n\n");


  //读取deviceCert文件的一行
  //fgets()会读取换行符
  while(fgets(str,N,deviceCert)!=NULL)
  {
    //得到长度
    str_legth=strlen(str);
    str[str_legth-1]='\\';//替换\n

    strcat(str,"n\\\n");//替换字符串

    fputs(str,aws_iot_certficates);

  }

  //读取sampleCACertificate文件的一行
  //fgets()会读取换行符
  while(fgets(str,N,sampleCACertificate)!=NULL)
  {

    //得到长度
    str_legth=strlen(str);
    str[str_legth-1]='\\';//替换\n
    //进行证书末尾的判断
    if(strcmp(str,"-----END CERTIFICATE-----\\")==0)
    {
      strcat(str,"n\"};\n\n#ifdef __cplusplus\n}\n#endif\n\n");//替换字符串
    }
    else
    {
     strcat(str,"n\\\n");//替换字符串
    }

    fputs(str,aws_iot_certficates);

  }

  //关闭文件
  fclose(deviceCert);
  fclose(sampleCACertificate);
  printf("CACert_composed\n\n");
}


只要用一个记事本,将上面的那4条命令写上去,再加一条用命令打开写的C语言执行程序,然后另保存为BAT格式,不断执行该文件就可以下载新的证书下来,或者用记事本将上面的那4条命令写上去,并另保存为BAT格式,然后在原有的C语言程序上加上一条指令打开该BAT文件,然后不断的运行这个程序就可以不断下载并处理好证书,直接可以按正常使用,然后将证书烧录进设备就可以了。

你可能感兴趣的:(esp8266/esp32)