Gerenciamento de certificados IoT autosserviço sem servidor - Parte 1.

Este arquivo foi traduzido automaticamente por IA, erros podem ocorrer
Estive trabalhando com várias soluções diferentes de IoT ao longo dos anos. Uma coisa que todas elas têm em comum é a necessidade de certificados confiáveis que possam ser usados tanto para estabelecer conexões quanto para identidades de dispositivos. Dispositivos e servidores precisam confiar uns nos outros para estabelecer comunicação segura, garantir a integridade dos dados e prevenir ataques maliciosos. Uma parte importante dessa confiança é a Infraestrutura de Chave Pública (PKI), onde certificados e Autoridades de Certificação (ACs) desempenham papéis vitais, além disso, há a necessidade de gerenciar uma grande quantidade de certificados de maneira fácil.
Em alguns projetos, temos implementado nossa própria solução interna de PKI, o que vem com complexidade e requisitos de segurança. Em outros, temos usado soluções SaaS, como DigiCert IoT Trust Manager, AWS IoT Core e Amazon Private CA.
No entanto, quando se tratava de desenvolvimento, e às vezes até ambientes de teste, não era incomum usarmos algum tipo de certificado autoassinado, com um portal autosserviço fácil para criar diferentes certificados.
Neste post, que é a primeira parte de duas, apresentaremos alguns conceitos básicos sobre certificados e PKI. Também começaremos a criar as fundações para uma API sem servidor que pode ser usada para criar um portal autosserviço para geração de certificados.
AVISO
A solução que construo nesta série de posts NÃO é adequada para uma configuração de produção. Isso é puramente para ambientes de desenvolvimento e para fins de aprendizado!
Em um ambiente de produção, precisamos de:
Para essas necessidades, serviços gerenciados como AWS Private CA, DigiCert IoT Trust Manager e Let’s Encrypt são ideais.
Nesta primeira versão da API, não estamos usando nenhuma forma de Autorização! Isso será adicionado na próxima parte!
Por que construir uma API autosserviço?
Como dito, em um sistema IoT, milhares de dispositivos precisam interagir com servidores, brokers IoT e entre si. Cada uma dessas interações deve ser segura, o que significa:
- Os servidores devem autenticar dispositivos para garantir que são legítimos.
- Os dispositivos devem autenticar servidores para garantir que estão se conectando a uma fonte confiável.
Portanto, certificados são emitidos para servidores e dispositivos, criando uma cadeia de confiança ancorada em uma AC Raiz.
Uma API autosserviço para gerenciamento de certificados permite:
- Automação: Dispositivos e servidores podem solicitar e renovar certificados programaticamente.
- Escalabilidade: À medida que nosso ambiente IoT cresce, a API pode lidar com a crescente demanda por certificados.
- Aprendizado e Teste: Antes de adotar um serviço gerenciado, construir seu próprio sistema de certificados ajuda a entender como a PKI funciona.
Visão geral de Certificados, ACs e Confiança
O que é uma Autoridade de Certificação (AC)?
Uma AC é uma entidade confiável responsável por emitir certificados digitais. Esses certificados vinculam uma chave pública a uma identidade (por exemplo, um servidor, dispositivo ou usuário), permitindo confiança entre entidades.
As ACs são estruturadas em uma hierarquia:
AC Raiz
- A âncora de confiança definitiva.
- Autoassinada e altamente segura.
- Nunca deve ser usada para emitir certificados de entidade final diretamente.
AC Intermediária
- Emitida e assinada pela AC Raiz.
- Delega a responsabilidade de emitir certificados para entidades finais (por exemplo, dispositivos e servidores).
- Limita a exposição da AC Raiz.
Certificados de Entidade Final, folha
- Certificados para servidores, dispositivos IoT ou usuários.
- Emitidos por uma AC Intermediária e usados na comunicação cliente-servidor ou autenticação mútua.
Cadeias de Certificados e Confiança
Uma cadeia de certificados é uma sequência de certificados, onde cada certificado na cadeia é assinado pelo certificado subsequente. Ela representa a relação hierárquica entre um certificado e seu emissor.

Uma cadeia pode consistir em um ou vários certificados intermediários. Na imagem acima, a cadeia é ilustrada com duas ACs Intermediárias.
Durante um handshake TLS
Simplificando muito, o handshake seria
- O servidor apresenta seu certificado ao cliente.
- O cliente valida o certificado do servidor rastreando a cadeia de volta para uma AC Raiz confiável em seu armazenamento de certificados. Ao usar uma AC Raiz autoassinada, você precisa garantir que o pacote esteja presente no armazenamento de confiança ou incluído na tentativa de conexão.
Em um cenário onde a autenticação mútua é necessária, o servidor executa o mesmo processo para o certificado do cliente.
Confiança em várias ACs Intermediárias
Em um ambiente IoT, é comum ter uma AC Intermediária emitindo certificados de servidor (por exemplo, para brokers IoT) e outra AC Intermediária emitindo certificados de cliente (por exemplo, para dispositivos). Para que um certificado de cliente (assinado por uma AC Intermediária) confie em um certificado de servidor (assinado por uma AC Intermediária diferente), ambos devem:
Fazer parte da mesma hierarquia de confiança.
- Ambas as ACs Intermediárias devem ser assinadas pela mesma AC Raiz.
- A AC Raiz é a âncora de confiança comum.
Ser validados contra a cadeia.
- O cliente verifica a cadeia de certificados do servidor, rastreando-a de volta para a AC Raiz.
- O servidor verifica a cadeia de certificados do cliente da mesma forma.
Essa configuração garante escalabilidade e separação de responsabilidades. A AC Intermediária de Servidor se concentra em servidores e brokers. A AC Intermediária de Dispositivo se concentra em dispositivos IoT.
Implementação
Agora vamos começar a implementar esta API autosserviço. Vamos construir isso completamente sem servidor usando Amazon API Gateway e funções Lambda, com armazenamento dos certificados em S3 e Certificate Manager. Esta primeira parte da série de blogs irá criar apenas a primeira parte muito básica da API, que iremos estender na segunda parte. Você pode encontrar todo o código-fonte em Serverless-Handbook Gerenciamento de Certificados IoT Autosserviço

Vamos configurar um API Gateway com três endpoints para criar nossa AC Raiz, AC Intermediária e certificado de Servidor. Tudo será armazenado em um bucket S3, e o certificado de servidor também será importado para o Certificate Manager.
API REST
Primeiro, vejamos a API REST e a estrutura.
| Endpoint | Método | Descrição |
|---|---|---|
/certificates/root | POST | Criar uma nova AC Raiz. |
/certificates/intermediate | POST | Criar uma nova AC Intermediária. |
/certificates/server | POST | Criar um novo certificado de servidor. |
Decidi usar caminhos separados (por exemplo, /certificates/root, /certificates/intermediate, /certificates/server) em vez de um único endpoint com um parâmetro de tipo (por exemplo, /certificates com tipo como entrada), pois acho que isso se alinha melhor aos princípios REST e melhora a legibilidade e usabilidade da API.
É mais fácil estender a API, com um novo tipo de certificado (por exemplo, dispositivo), pois podemos criar um novo caminho como /certificates/device sem impactar os caminhos existentes. Caminhos separados segmentam naturalmente os recursos, reduzindo a necessidade de filtragem no lado do cliente. Acho que recuperar todas as ACs Raiz é mais simples com GET /certificates/root do que GET /certificates?type=root.
Infraestrutura comum
Primeiro de tudo, vamos implantar a infraestrutura comum, neste caso, é apenas o bucket S3, adicionaremos mais coisas neste modelo mais tarde.
AWSTemplateFormatVersion: "2010-09-09"
Transform: "AWS::Serverless-2016-10-31"
Description: Infraestrutura Comum de Gerenciamento de Certificados Autosserviço
Parameters:
ApplicationName:
Type: String
Description: Nome do aplicativo proprietário
Default: image-moderation
Resources:
StorageBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
BucketName: !Sub ${ApplicationName}-storage-bucket
Outputs:
StorageBucketName:
Description: O nome do bucket de certificados
Value: !Ref StorageBucket
Export:
Name: !Sub ${AWS::StackName}:certificate-bucket-nameEm seguida, podemos configurar os endpoints usando SAM e AWS::Serverless::Api e as três funções Lambda que suportam a API.
AWSTemplateFormatVersion: "2010-09-09"
Transform: "AWS::Serverless-2016-10-31"
Description: Criar a API para gerenciamento autosserviço de certificados
Parameters:
ApplicationName:
Type: String
Description: Nome do aplicativo proprietário
CommonInfraStackName:
Type: String
Description: O nome da pilha comum que contém o EventBridge Bus e mais
Globals:
Function:
Timeout: 30
MemorySize: 2048
Runtime: python3.12
Environment:
Variables:
CERTIFICATE_BUCKET_NAME:
Fn::ImportValue: !Sub "${CommonInfraStackName}:certificate-bucket-name"
Resources:
LambdaGenerateRootCA:
Type: AWS::Serverless::Function
Properties:
CodeUri: Lambda/API/GenerateRootCA
Handler: handler.handler
Policies:
- S3FullAccessPolicy:
BucketName:
Fn::ImportValue: !Sub "${CommonInfraStackName}:certificate-bucket-name"
Events:
CreateRootCAApi:
Type: Api
Properties:
Path: /certificates/root
Method: post
RestApiId: !Ref GenerateCertificatesApi
LambdaGenerateIntermediateCA:
Type: AWS::Serverless::Function
Properties:
CodeUri: Lambda/API/GenerateIntermediateCA
Handler: handler.handler
Policies:
- S3FullAccessPolicy:
BucketName:
Fn::ImportValue: !Sub "${CommonInfraStackName}:certificate-bucket-name"
Events:
CreateIntermediateCAApi:
Type: Api
Properties:
Path: /certificates/intermediate
Method: post
RestApiId: !Ref GenerateCertificatesApi
LambdaGenerateDeviceCertificate:
Type: AWS::Serverless::Function
Properties:
CodeUri: Lambda/API/GenerateDeviceCert
Handler: handler.handler
Policies:
- S3FullAccessPolicy:
BucketName:
Fn::ImportValue: !Sub "${CommonInfraStackName}:certificate-bucket-name"
LambdaGenerateServerCertificate:
Type: AWS::Serverless::Function
Properties:
CodeUri: Lambda/API/GenerateServerCert
Handler: handler.handler
Policies:
- S3FullAccessPolicy:
BucketName:
Fn::ImportValue: !Sub "${CommonInfraStackName}:certificate-bucket-name"
- Version: "2012-10-17"
Statement:
Action:
- acm:*
Effect: Allow
Resource: "*"
Events:
CreateServerCAApi:
Type: Api
Properties:
Path: /certificates/server
Method: post
RestApiId: !Ref GenerateCertificatesApi
GenerateCertificatesApi:
Type: AWS::Serverless::Api
Properties:
Description: API para criar e gerenciar certificados
Name: !Sub ${ApplicationName}-api
StageName: prod
OpenApiVersion: '3.0.1'
AlwaysDeploy: true
EndpointConfiguration: REGIONALNossas funções Lambda estão em Python e usamos a biblioteca cryptography para criar os certificados, abaixo está a implementação para criar um certificado de servidor. Para uma implementação completa, visite Serverless-Handbook Gerenciamento de Certificados IoT Autosserviço.
import boto3
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization, hashes
import datetime
import os
import json
s3_client = boto3.client("s3")
acm_client = boto3.client("acm")
def create_server_certificate(
intermediate_private_key_pem,
intermediate_cert_pem,
fqdn,
country,
state,
organization,
validity_days,
):
# Carregar a chave privada e o certificado da AC Intermediária
intermediate_private_key = serialization.load_pem_private_key(
intermediate_private_key_pem, password=None
)
intermediate_cert = x509.load_pem_x509_certificate(intermediate_cert_pem)
# Gerar uma chave privada para o certificado de servidor
server_private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
server_private_key_pem = server_private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
)
subject = x509.Name(
[
x509.NameAttribute(NameOID.COUNTRY_NAME, country),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, state),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization),
x509.NameAttribute(NameOID.COMMON_NAME, fqdn),
]
)
server_certificate = (
x509.CertificateBuilder()
.subject_name(subject)
.issuer_name(intermediate_cert.subject) # Assinado pela AC Intermediária
.public_key(server_private_key.public_key())
.serial_number(x509.random_serial_number())
.not_valid_before(datetime.datetime.now(datetime.timezone.utc))
.not_valid_after(
datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=validity_days)
)
.add_extension(
x509.SubjectAlternativeName([x509.DNSName(fqdn)]),
critical=False,
)
.add_extension(
x509.BasicConstraints(ca=False, path_length=None),
critical=True,
)
.add_extension(
x509.KeyUsage(
digital_signature=True,
key_encipherment=True,
key_cert_sign=False,
crl_sign=False,
content_commitment=False,
data_encipherment=False,
encipher_only=False,
decipher_only=False,
key_agreement=False,
),
critical=True,
)
.sign(intermediate_private_key, hashes.SHA256())
)
server_cert_pem = server_certificate.public_bytes(serialization.Encoding.PEM)
return server_private_key_pem, server_cert_pem
def handler(event, context):
body = json.loads(event["body"])
bucket_name = os.environ.get("CERTIFICATE_BUCKET_NAME")
# Buscar a chave privada e o certificado da AC Intermediária do S3
intermediate_private_key = s3_client.get_object(
Bucket=bucket_name, Key="intermediate_ca/private_key.pem"
)["Body"].read()
intermediate_cert = s3_client.get_object(
Bucket=bucket_name, Key="intermediate_ca/certificate.pem"
)["Body"].read()
# Buscar a cadeia de certificados do S3
cert_chain_pem = s3_client.get_object(
Bucket=bucket_name, Key="intermediate_ca/certificate_chain.pem"
)["Body"].read()
# Criar Certificado de Servidor
server_private_key_pem, server_cert_pem = create_server_certificate(
intermediate_private_key,
intermediate_cert,
body["fqdn"],
body["country"],
body["state"],
body["organization"],
body["validity_days"],
)
s3_folder = f"server_certificates/{body["fqdn"]}/"
# Upload do Certificado de Servidor e Chave Privada para S3
s3_client.put_object(
Bucket=bucket_name,
Key=f"{s3_folder}private_key.pem",
Body=server_private_key_pem,
)
s3_client.put_object(
Bucket=bucket_name, Key=f"{s3_folder}certificate.pem", Body=server_cert_pem
)
# Importar o certificado para o ACM
response = acm_client.import_certificate(
Certificate=server_cert_pem,
PrivateKey=server_private_key_pem,
CertificateChain=cert_chain_pem,
)
return {
"statusCode": 200,
"body": json.dumps(
{
"message": "Certificado de servidor criado, enviado para S3 e importado para o ACM.",
"fqdn": body["fqdn"],
"s3_folder": s3_folder,
"acm_certificate_arn": response["CertificateArn"],
}
),
}Conclusão
Este blog cobriu tudo, desde cadeias de certificados até validar confiança em sistemas IoT. Construir sua própria API autosserviço é uma boa maneira de aprender sobre certificados e PKI. Em produção, no entanto, sempre opte por soluções gerenciadas que oferecem automação, conformidade e escalabilidade.
Certificados São a Base da Segurança IoT
Certificados e ACs garantem comunicação segura e autenticada nos ecossistemas IoT.
O Papel das ACs Intermediárias
Delegar a emissão de certificados para ACs Intermediárias melhora a escalabilidade e limita a exposição.
A Confiança é Construída em Hierarquias
A confiança entre dispositivos e servidores depende de ACs Raiz compartilhadas e cadeias de certificados validadas.
Construa para Aprender, Use Serviços Gerenciados para Produção
Enquanto esta API é uma ótima ferramenta de aprendizado, serviços como AWS Private CA ou Let’s Encrypt são mais adequados para produção.
Para obter o código-fonte completo e implantá-lo você mesmo, visite Serverless-Handbook Gerenciamento de Certificados IoT Autosserviço
Palavras Finais
Não se esqueça de me seguir no LinkedIn e X para mais conteúdo, e leia o restante dos meus Blogs
Como Werner diz! Agora Vá Construir!