Wednesday, April 15, 2020

Segurança com Java: Certificados

Nesse primeiro post falamos um pouco sobre criptografia com Java. Agora vamos nos aprofundar e falar sobre certificados.

Se você quiser que sua aplicação web (e-commerce, página estática, back-end, etc) viabilize a comunicação HTTPS com seu cliente, você precisa instalar um certificado TLS/SSL no seu web-server. Dessa forma toda a sessão entre a aplicação web e o cliente poderá ser estabelecida de forma segura, isto é, criptografada por meio de chaves públicas e privadas.



TLS/SSL

Os protocolos TLS (Transport Layer Security) e SSL (Secure Socket Layer) são frequentemente usados para se referir à mesma coisa: comunicação HTTPS. Ambos visam a segurança, integridade e privacidade durante a comunicação cliente-servidor. O SSL porém é considerado depreciado e sua versão moderna é o TLS 1.3.

A espinha dorsal do TLS é uma infraestrutura de chaves públicas que viabiliza a confiança mútua entre duas partes (cliente e servidor) que nunca trabalharam juntas. Isso tudo é realizado por um sistema de Certificate Authorities (CAs) que assinam certificados que passam a servir de credenciais para servidores, atestando sua identidade. Assim, quando um cliente tenta se conectar a esse servidor via HTTPS (TLS), esse servidor apresenta seu certificado ao cliente. Se a autoridade que assinou o certficado desse servidor estiver na lista de autoridades confiáveis do cliente, então um canal de comunicação segura é estabelecido entre esse cliente e o respectivo servidor.

Quando o cliente é uma aplicação java, a lista de autoridades confiáveis é chamada de truststore. Do lado do servidor [quando ele também é uma aplicação java], seu certificado (e as respectivas chaves) fica armazenado em uma keystore.

Geralmente, quando você é um servidor, trabalha mais com o keystore; quando você é cliente, está mais interessado no seu truststore.

O server apresenta seu certificado ao cliente, que confere no seu truststore se o certificado é confiável

Truststores do JDK

O Truststore na prática é um conjunto de certificados armazenados no JDK que a aplicação utiliza para determinar se confia ou não no servidor TLS que ela está acessando. Todo JDK vem com uma lista de CAs pré-definidos no Truststore.

O keytool é um programa em linha de comando que permite trabalhar com truststores e keystores.  Como dissemos, você trabalha mais com o keystore se for um server; ou trabalha mais com o truststore, se for um cliente.

O seguinte comando lista todas as autoridades pré-aceitas no JDK (o password default é changeit para rodar o comando):

keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts


Como exemplo, no browser Mozila, podemos checar o certificado de docs.amazon.aws clicando no icone do cadeado:


Em seguida clicando em more information:


Na pop-up que aparecer clique em view certficate:


Selecione o fingerprint desse certificado:


E confira se o JDK confia nesse CA:

keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts | grep ' fingerprint '


Como esse CA consta no truststore do JDK, podemos acessar essa url via código sem problemas:

String hostURL = "https://docs.aws.amazon.com";
URL url = new URL(hostURL);
HttpsURLConnection conn;
conn = (HttpsURLConnection)url.openConnection();
InputStream is = conn.getInputStream();

Experimente trocar por uma url cujo certificado não confiável, como por exemplo https://self-signed.badssl.com, e o código acima lança uma exception

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
 at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
 at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946)
 at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316)
 at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:310)
 at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1639)
 at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223)
 at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037)
 at sun.security.ssl.Handshaker.process_record(Handshaker.java:965)
 at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064)
 at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
 at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)
 at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)
 at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
 at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
 at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1570)
 at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498)
 at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:268)
 at ssl.Main03.main(Main03.java:17)

 javax.net.ssl.SSLHandshakeException Basicamente nos diz que o certificado desse host não é reconhecido pelo truststore default do JDK e que, portanto, não é possível estabelecer uma conexão segura com ele via TLS.

É comum que muitas vezes queiramos ignorar essa advertência e acessar o host mesmo assim (cenário comum em ambientes de teste). Neste caso podemos criar nossa própria truststore e   adicionar manualmente qualquer certficado.

O código abaixo cria uma nova TrsutStore e adiciona nela o certificado de https://self-signed.badssl.com, fazendo com que a aplicação consiga acessar a URL via HTTPS sem erros:

public class AdicionaCertificado {

    public static void main(String args[]) throws Exception {

        String hostURL = "https://self-signed.badssl.com";
        //String hostURL = "https://docs.aws.amazon.com";
        loadNewCertificate("/home/rafael/Library/Blog/Certs/badssl-com.pem");

        URL url = new URL(hostURL);
        HttpsURLConnection conn;
        conn = (HttpsURLConnection)url.openConnection();
        InputStream is = conn.getInputStream();
    }

    static void loadNewCertificate(String newCertficateFile) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {

        String trustStorePass = "123456";
        String clientTrutStorePath = "/home/rafael/Library/Blog/Certs/myNewTrustStore";
        String alias = "badssl.com";

        // criando uma nova trustStore (diferente da default cacerts)
        KeyStore clientTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());

        char[] password = trustStorePass.toCharArray();

        // para criar uma nova truststore vazia, passe null no 1o argumento...
        clientTrustStore.load(null, password);

        // ...e crie o arquivo fisico vazio
        FileOutputStream fos = new FileOutputStream(clientTrutStorePath);
        clientTrustStore.store(fos, password);
        fos.close();

        // como a keystore já existe, desta vez não passamos null ao metodo load
        FileInputStream in = new FileInputStream(clientTrutStorePath);
        clientTrustStore.load(in, password);
        in.close();

        // CertficateFactory é usada para gerar novos certificados
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream serverCertstream = new FileInputStream(newCertficateFile);

        // gera um objeto certificado e o inicializa com base nos dados do InputStream
        Certificate serverCertificate =  cf.generateCertificate(serverCertstream);

        // adiciona o novo certificado a nova keystore, com um apelido
        clientTrustStore.setCertificateEntry(alias, serverCertificate);

        // salvamos as alteracoes na nova keystore
        FileOutputStream out = new FileOutputStream(clientTrutStorePath);
        clientTrustStore.store(out, password);
        out.close();

        // dinamicamente alteramos o trsutstore default desta App para a nova trsutStore criada
        System.setProperty("javax.net.ssl.trustStore", clientTrutStorePath);
        System.setProperty("javax.net.ssl.trustStorePassword", trustStorePass);
    }
}

Você pode criar seu próprio certificado para posteriormente apresentá-lo aos clientes de sua aplicação. O comando a seguir cria uma chave privada e um certificado contendo a chave pública:
keytool -genkeypair -alias meu-alias -keyalg RSA -validity 7 -keystore minha-keystore

O próximo comando extrai o certificado do arquivo minha-keystore com a extensão .cer:
keytool -exportcert -alias meu-alias -keystore minha-keystore -rfc -file meu-certificado.cer

Nota: um detalhe para se estar ciente e que pode causar confusão é que, da perspectiva do keytool e do Java, keystores e truststores são arquivos keystore. Eles apenas contêm diferentes tipos de chaves. Tanto que a referência a clientTrustore no código foi representada pela classe java.security.KeyStore.


"Os fariseus perguntaram a Jesus sobre o momento em que chegaria o Reino de Deus. Jesus respondeu: 'O Reino de Deus não vem ostensivamente. Nem se poderá dizer: 'Está aqui' ou: 'está alí', porque o Reino de Deus está no meio de vocês.'"

Lucas 17:20-21

       

Friday, February 21, 2020

AWS: API Gateway e Lambda: parte 2

Na primeira parte deste artigo conhecemos o serviço Lambda da AWS. Com o AWS Lambda podemos criar funções que são disparadas por eventos diversos, sendo que toda a implementação de infraestrutura fica a cargo do cloud provider (neste caso AWS), assim podemos focar totalmente no código ao inves de questões relacionadas a servidores, etc. Também criamos uma função simples por linha de comando.


Neste artigo vamos explorar um caso de uso muito comum que é expor a função Lambda através de um recurso REST, para tanto, utilizaremos o serviço AWS API Gateway.


AWS API Gateway

Esse serviço permite criar recursos acessíveis via HTTP que aderem ao prtocolo REST, habilitando comunicação client-server usando os métodos padrões GET, PUT, POST, DELETE e PATCH.

O diagrama abaixo ilustra como a API Gateway pode integrar diversos clientes aos diversos serviços de back-end em um ambiente totalmente serverless:



Chamando a função Lambda via API Gateway

Passo 1: no console da API Gateway, encontre a seção REST API e clique em build:



Passo 2: na página seguinte:
  • embaixo de Choose the Protocol, escolha REST
  • embaixo de Create New API, escolha New API
  • em Settings, dê um nome a sua API (neste caso my-api)



Passso 3: na página seguinte, no botão menu Action, clique em Create Method:



Passo 4: na seção Resources, clique na combo box e escolha GET:



Passo 5: clique no método GET e na seção à direita:
  • em integration type, marque Lambda Function, o que significa que esse recurso irá chamar uma função Lambda
  • marque o check-box Use Lambda Proxy integration. Quando essa opção é marcada, todo envolope HTTP que chega à API Gateway é repassado integralmente à função Lambda sob a forma de um JSON. (Se esta opção não for marcada, o desenvolvedor então deve fazer o mapeamento customizado entre o input da API Gateway e o input da função Lambda)
  • em lambda function, digite o nome da função que será chamada e clique em Save.


Vai aparecer uma pop-up dizendo que será criada uma permissão para este recurso invocar a função Lambda, clique OK.


Passo 6: no botão menu Action clique na opção Deploy API. Vai aparecer uma pop up requisitando um stage para esse deployment (coloque stage ou test):


Em stages, clique no nome do stage que você criou. O metodo já está publicado, repare na invoke url:


Copie e cole essa url em uma nova aba do browser, ele vai chamar a função Lambda e exibir o valor que ela retornar:


Integração entre API Gateway e a função Lambda

Existem duas formas de configurar como a API Gateway vai repassar os dados do request para a função Lambda:


  1. Lambda Proxy Integration: é a opção mais simples, todo envelope HTTP recebido pela API Gateway é convertido em um stream de JSON e repassado integralmente ao Lambda
  2. Lambda Proxy Non-Integration (ou AWS): a função Lambda recebe um input diferente do envelope HTTP (geralmente um objeto do modelo de negócios), neste caso antes de chamar a função lambda, deve-se fazer um mapeamento entre o envelope HTTP e o modelo de dados requerido pela função Lmabda.
Exemplo de uma requisição HTTP no formato JSON repassada integralmente pela API Gateway à função Lambda quando se usa a opção Lambda Proxy Integration:

{
 "resource": "Resource path",
 "path": "Path parameter",
 "httpMethod": "Incoming request's method name"
 "headers": {String containing incoming request headers}
 "multiValueHeaders": {List of strings containing incoming request headers}
 "queryStringParameters": {query string parameters }
 "multiValueQueryStringParameters": {List of query string parameters}
 "pathParameters": {path parameters}
 "stageVariables": {Applicable stage variables}
 "requestContext": {Request context, including authorizer-returned key-value pairs}
 "body": "A JSON string of the request payload."
 "isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encode"
}

"Quem bebe dessa água vai ter sede de novo. Mas aquele que beber da água que eu vou dar, esse nunca mais terá sede. E a água que eu lhe darei, vai se tornar dentro dele uma fonte de água que jorra para a vida eterna."

Jo 4:13-14

       


Tuesday, January 28, 2020

Criptografia e Segurança com Java: parte 1


Criptografia é a prática e o estudo de técnicas que viabilizam uma comunicação segura entre duas partes, através de um canal inseguro (a internet, por exemplo) de modo que seja assegurada a total confidencialidade da mensagem trocada entre as partes, isto é, se algum terceiro interceptar a mensagem, ele não terá condições de entender seu conteúdo, porque a mesma estará criptografada. Caso ele consiga decifrar a mensagem, diz-se que a criptografia foi quebrada.


Em resumo, seguindo a ilustração acima, extraida do livro Understanding Cryptography de Christof Paar e Jan Pelzl, Oscar é um sujeito mau que tentará interceptar a mensagem trocada entre Alice e Bob. Quando se trata de criptografia, temos duas funções:

e(): criptografa (encrypt) o texto x usando a chave k e retorna y (y é x criptografado)

d(): descriptografa (decrypty usando a mesma chave k, de modo que o resultado seja o texto x original

Quando a chave que criptografa e descriptografa são iguais, diz-se que a cripografia é simétrica; quando são diferentes, a criptografia é asimetrica.

Existem dezenas de algoritimos que determinam as etapas para criptografar/descriptografar o texto usando uma determinada chave. Exemplos comuns:

DES: (Data Encryption Standard) durante quase 3 décadas foi o método mais popular de criptografia simétrica. Caiu em desuso no final dos anos 90, quando o barateamento do poder computacional viabilizou a quebra desse método.

RSA: (Rivest–Shamir–Adleman) este algoritmo usa chave asimétrcia, o que significa que a chave que criptografa é diferente da chave que descriptografa. Considerado seguro, é muito usado em autenticações SSH e SSL.

AES: (Advanced Encryption Standard) Largamente aceito hoje, substituiu o DES, tambem é simétrico. AES é o padrão pelo NIST. É usando principalmente em comunicações via HTTPS.

SHA: (Secure Hash Algorithm) Representa uma família de algoritmos (SHA-0, SHA-1, SHA-2, etc.). Transforma o texto usando uma função hash. A criptografia usando SHA só tem um-caminho, ou seja, uma vez criptografada a mensagem, é impossível recuperar a mensagem de volta a partir do hash. Uma aplicação comum do SHA é criptografia de senhas e assinaturas digitais.

MD5: (Message Digest 5) Assim como o SHA, o MD5 é uma função hash de mão única.

A relação completa dos algoritmos suportados pela plataforma Java está na documentação da arquitetura de criptografia do Java.

Exemplo de DES (Data Encryption Standard) em Java

Neste exemplo em java criptografamos uma mensagem utilzando o algoritmo DES, e depois descriptografamos de volta para a original. As classes e interfaces que provem operações de criptografia no java estão no pacote javax.cripto. O pacote java.security também dá suporte para essas operações (as declarações de import estão omitidas).

/**
 * DES (Data Encryption Standard)
 */
public class _01_DES_example {

    //javax.crypto.Cipher provê funcionalidade para Criptografar ou Descriptografar.
    private static Cipher eCipher;
    private static Cipher dCipher;

    // java.security.Key é a interface que representa todos tipos de chave em java
    private static Key secretKey;

    public static void main(String args[])
    {
        try
        {
            // gera uma chave secreta para o algoritmo especificado 'DES'
            secretKey = KeyGenerator.getInstance("DES").generateKey();

            // cria uma instansia de Cipher que irá implementar a transformação da mensagem de acordo com
            // algoritmo especificado, neste caso DES
            eCipher = Cipher.getInstance("DES");
            dCipher = Cipher.getInstance("DES");

            // inicializa este cipher, especificando que ele irá criptografar mensagens
            eCipher.init(Cipher.ENCRYPT_MODE, secretKey);

            // inicializa este cipher, especificando que ele irá DEScriptografar mensagens
            dCipher.init(Cipher.DECRYPT_MODE, secretKey);

            // como a chave que faz a criptografia e a descriptografia é a mesma, então a operação é simétrica

            // mensagem a ser criptografada
            String classifiedMessage = new String("Top secret - This is a classified message!");

            // metodo auxiliar
            String encryptedMsg = criptografar(classifiedMessage);
            System.out.println("\nmenssagem criptografada: "+encryptedMsg);

            // metodo auxiliar
            String decrypted = descriptografar(encryptedMsg);
            System.out.println("\nmensagem descriptografada: "+decrypted);
        }
        // exceções checadas durante o processo de des/criptografia
        catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e)
        {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }
    }

    static String criptografar(String classifiedMessage) throws UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
        // converte a String em bytes
        byte[] utf8 = classifiedMessage.getBytes("UTF-8");

        // criptografa a mensagem
        byte[] mensagemCriptografada = eCipher.doFinal(utf8);

        // converte em base64 para transmissao na rede
        mensagemCriptografada = Base64.getEncoder().encode(mensagemCriptografada);

        return new String(mensagemCriptografada);
    }

    // faz o caminho inverso do metodo criptografar()
    static String descriptografar(String encryptedMessage) throws BadPaddingException, IllegalBlockSizeException {

        // volta para base decimal
        byte[] dec = Base64.getDecoder().decode(encryptedMessage);

        // descriptografa a mensagem
        byte[] utf8 = dCipher.doFinal(dec);

        // retorna a mensagem original
        return new String(utf8);
    }
}

A conversão do texto criptografado em Base64 é necessária para garantir que todos os caracteres da mensagem cheguem integralmente ao destino, uma vez que, sem esse tratamento, durante o tráfego nas diferentes camadas da rede, alguns protocolos ou equipamentos poderiam interpretar errôneamente alguns caracteres e corromper a mensagem.

Resultado da execução do exemplo:

menssagem criptografada: +dIABfsau+f6eq021nwy/dN3ohqDFNcW7Gy6Yfs2PRFV6Fm+vLUc78QTlW8W4m3S

mensagem descriptografada: Top secret - This is a classified message!


"Estejam sempre alegres, rezem sem cessar. Dêem graças em todas as circunstâncias, porque esta é a vontade de Deus a respeito de vocês em Jesus Cristo. Não extingam o Espírito, não desprezem as profecias; examinem tudo e fiquem com o que é bom. Fiquem longe de toda espécie de mal."

1 Ts 5:16-22

s        

Wednesday, January 1, 2020

AWS: API Gateway e Lambda: parte 1

AWS Lambda e API Gateway


API Gateway é um serviço da AWS para criar, publicar, manter e monitorar de maneira muito simples APIs REST ou Web Socket que servem como porta de entrada para serviços que operam no back-end como acesso ao banco de dados, processamento de regras de negócios e diversas outras funcionalidades.


APIs REST são baseadas no protocolo HTTP e implementam os métodos padrões deste protocolo tais como GET, PUT, POST, DELETE e PATCH.

Neste artigo fazemos uma introdução a API Gateway com REST endpoints chamando outro serviço AWS no back-end, neste caso AWS Lambda.

AWS Lambda


Lambda é outro serviço da AWS que permite executar um código baseado em algum evento. O diferencial do Lambda é que ele é sevrerless, ou seja, você apenas cria sua lógica e a AWS cuida de toda infraestrutura necessária como escalonamento, memória, processamento, etc. Lambdas são como funções que rodam na nuvem, cujo código pode ser disparado sob diversas formas.

API Gateway + Lambda

Um caso de uso muito comum é integrar os serviços API Gateway e Lambdas, de modo que a chamada ao primeiro dispare a execução da lógica no segundo. Como exemplo dessa integração:
  1. Criamos uma função Lambda que cria um JSON e o retorna (parte 1)
  2. Um REST endpoint via API Gateway que chama a função e retorna seu resultado (parte 2)
Para executar o exemplo é necessário que você tenha pelo menos uma conta gratuita na AWS e instalar a ferramenta AWS CLI, que permite gerenciar os serviços da AWS por linha de comando.

Criando o Lambda

Utilizando sua IDE de preferencia (neste exemplo uso o INteliJ), crie um projeto MAVEN. O arquivo pom.xml contém as dependencias para se trabalhar com o Lambda e manipulação de JSON com a API gson google:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.finalexception</groupId>
    <artifactId>LambdaApiGatewayP01</artifactId>
    <version>1.0</version>
    <name>lambda-api</name>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-core</artifactId>
            <version>1.1.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <createDependencyReducedPom>false</createDependencyReducedPom>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

O plugin maven-shade permite compactar todas as dependencias junto com o jar final. Isso será necessário ao subir o arquivo compactado do projeto para executá-lo na nuvem.
Crie um pacote chamado service.lambda e crie classe Function01 cujo código é o que segue:

package service.lambda;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
import com.google.gson.JsonObject;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

public class Function01 implements RequestStreamHandler{

    @Override
    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException
    {
        //API para manipulacao de JSONs
        JsonObject responseJson = new JsonObject();
        JsonObject responseBody = new JsonObject();
        JsonObject headerJson = new JsonObject();

        //propriedade mensagem na resposta
        responseBody.addProperty("message", "it's everything OK here!");

        // um header aleatório na resposta para API Gateway
        headerJson.addProperty("x-custom-header", "my custom header value");

        // Http status code 200 OK
        responseJson.addProperty("statusCode", 200);
        responseJson.add("headers", headerJson);
        responseJson.addProperty("body", responseBody.toString());

        // serializa o JSON para um OutputStream
        OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8");
        writer.write(responseJson.toString());
        writer.close();
    }
}


A partir da raiz do seu projeto, rode o comando mvn clean package para empacotar o projeto, gerando o arquivo LambdaApiGatewayP01-1.0.jar

Fazendo o deploy do arquivo na AWS

Agora vamos enviar o arquivo do projeto para o serviço AWS Lambda. A partir da raiz do seu projeto, execute o seguinte comando:

aws lambda create-function --function-name funcao01 \
    --zip-file fileb://target/LambdaApiGatewayP01-1.0.jar \
    --handler service.lambda.Function01::handleRequest \
    --runtime java8 \
    --role arn:aws:iam::12121212121:role/developer

O valor do parâmetro --role deve ser qualquer role na sua conta AWS que tenha autorização para criar Lambdas. Quando você cria uma conta, no mínimo deve ter a role administrador, que possui todas as permissões.

Após criar a função, é retornado um JSON contendo várias propriedades da mesma. Observe e copie o valor da propriedade functionArn, representa o endereço dessa função específica no ambiente AWS. Ao invocar essa função, utilizaremos o valor desse functionArn.

Agora que você publicou a função, faça um teste invocando-a. Substitua o valor de functionArn perlo valor gerado na sua conta:

 aws lambda invoke --function-name arn:aws:lambda:us-east-1:205303771310:function:funcao01 \
      --invocation-type RequestResponse /tmp/outfile.txt

O parâmetro /tmp/outfile contém a resposta gerada pela função no formato JSON. Você pode checá-lo abrindo o arquivo:


Na segunda parte intergamos essa função com um endpoint REST via API Gateway. Dessa forma, requisições HTTP no endpoint farão a função disparar.

Referências:
https://aws.amazon.com/lambda/
https://aws.amazon.com/api-gateway/

"Quanto a vocês, não fiquem procurando o que vão de comer e o qque vão beber. Não fiquem inquietos. Porque são os pagãos deste mundo que procuram tudo isso. O Pai bem sabe que vocês têm necessidade dessas coisas. Portanto, busquem o Reino dele, e Deus dará a vocês essas coisas em acréscimo. Não tenha medo, pequeno rebanho, porque o Pai de vocês tem prazer em dar-lhes o Reino"

Lucas 12:29-31