Jak weryfikować sygnatury AWS KMS w architekturach oddzielonych na dużą skalę

7 lipca 2021

Usługa AWS Key Management Service (AWS KMS) znacznie ułatwia tworzenie i zarządzanie kluczami kryptograficznymi w aplikacjach. Usługa obsługuje zarówno symetryczne, jak i asymetryczne klucze główne klienta (CMK).

Asymetryczne klucze główne klienta oferują możliwość podpisu cyfrowego, który konsumenci danych mogą użyć do zweryfikowania, czy dane te pochodzą od sprawdzonego producenta i nie są zmienione w transporcie.

AWS KMS zapewnia wygodne metody interfejsu API w asymetrycznych CMK, których możesz używać zarówno do podpisywania, jak i weryfikowania podpisów. Jednakże, kiedy musisz zweryfikować podpis w architekturach o wysokiej przepustowości lub oddzielonych, użycie interfejsu API AWS KMS może okazać się niepraktyczne. Ten artykuł opisuje przypadek użycia autoryzacji delegowanej, aby zilustrować sposób podejścia do tego problemu na dużą skalę. W tym przypadku użycia AWS KMS podpisuje dane, ale weryfikacja odbywa się w niezależnych, rozproszonych środowiskach.

Jeśli jesteś zainteresowany podpisywaniem JSON Web Tokens (JWT) z AWS KMS, a następnie weryfikowaniem tych podpisów w aplikacjach rozproszonych na dużą skalę, zapoznaj się z przykładowym przypadkiem użycia i przykładami kodu roboczego.

O weryfikacji podpisu

Podpisy cyfrowe

W kryptografii klucza publicznego tworzenie podpisu publicznego wymaga klucza prywatnego w parze kluczy publiczny/prywatny, podczas gdy weryfikacja podpisu wymaga tylko klucza publicznego. Wykorzystując standardowe podpisywanie, funkcje skrótu i parametry klucza, odbiorcy mogą weryfikować podpisy w dowolnym środowisku, które posiada dostęp do klucza publicznego.

W wielu przypadkach weryfikujesz podpisy częściej, niż je wydajesz – rozważ to, w jaki sposób token interfejsu API może być podpisany raz na godzinę, ale jest weryfikowany tysiące razy na sekundę. Podmioty podpisujące mogą także zostać odłączone w czasie weryfikacji – zastanów się, jak weryfikacja certyfikatów SSL/TLS nie wymaga połączenia z wystawcą certyfikatu.

Możesz wykorzystać te asymetrie na swoją korzyść, projektując wysoko parametrowe i rozproszone systemy delegowanej autoryzacji.

Weryfikowanie podpisów bez wywołania AWS KMS może być niezbędne w przypadku następujących scenariuszy:

  • AWS KMS nie jest dostępny z Twojego środowiska weryfikacji podpisów;
  • Twój system ma niskie opóźnienia lub wysoką przepustowość weryfikacji podpisów, przekraczając limity żądań AWS KMS API;
  • chcesz zoptymalizować koszty, minimalizując wywołania AWS KMS API.

Rozdzielone podpisywanie i weryfikacja

Rysunek nr 1 ilustruje sposób oddzielenia osoby podpisującej w oparciu o AWS KMS, od co najmniej jednego niezależnego weryfikatora. Proces ten obejmuje następujące kroki:

  1. Podczas konfiguracji systemu osoba podpisująca otrzymuje asymetryczną parę kluczy. Sygnatariusze tacy jak AWS KMS obsługują generowanie kluczy asymetrycznych i zarządzanie nimi przez moduł HMS – wewnętrzny sprzętowy moduł zabezpieczeń.
  2. Weryfikatory są skonfigurowane tak, aby ufać osobie podpisującej poprzez import offline klucza publicznego sygnatariusza.
  3. W czasie wykonywania AWS KMS jest proszony o zaszyfrowanie i utworzenie podpisu cyfrowego na niektórych oryginalnych danych. AWS KMS haszuje dostarczone dane i używa klucza prywatnego w asymetrycznej parze kluczy w celu obliczenia podpisu na podstawie skrótu. Oryginalne dane, wraz z podpisem, dostarczane są do klienta.
  4. Klient przekazuje dane i podpis do jednego lub większej liczby weryfikatorów i żąda dostępu do swoich chronionych zasobów.
  5. Weryfikatory weryfikują podpis, który jest powiązany z oryginalnymi danymi. Weryfikatory wykorzystują w tym procesie klucz publiczny sygnatariusza. Jeśli weryfikacja się powiedzie, oznaczając, że oryginalne dane, które zostały przekazane, są niezmienione i autentyczne, weryfikatory przyznają klientowi dostęp do chronionych zasobów.

Figure 1: Digital signature signing and verification in decoupled environments

Przykładowy przypadek użycia

Jeśli posiadasz już przypadek użycia i chcesz przejść bezpośrednio do kodu, możesz pominąć sekcję „Wdrażanie rozwiązania”. Jeśli chcesz dokładnie zgłębić przykładowy przypadek użycia, czytaj dalej.

Przyjrzyjmy się, w jaki sposób urząd opieki zdrowotnej w architekturze referencyjnej systemu COVID-19 Exposure Notification System (ENS) może używać AWS KMS, na czele którego stoi AWS Lambda do poświadczania (podpisywania) diagnozy pacjenta, która jest osadzona w oświadczeniach JWT. Ta certyfikacja pozwala niezależnym instytucjom opieki zdrowotnej na zweryfikowanie diagnozy pacjenta przy jednoczesnym zachowaniu jego prywatności.

Przykład ten wykorzystuje podpisy Elliptic Curve Digital Signature Algorithm (ECDSA), chociaż przedstawione techniki można równie dobrze zastosować do innych algorytmów kryptograficznych z kluczem publicznym, takich jak RSA. Twórcy zapewniają przykładowy kod w językach programowania Golang i Python, a także przykładowe polecenia OpenSSL. Poprzedni artykuł na blogu opisuje weryfikację podpisu AWS KMS RSA, wykorzystując komendy OpenSSL. Zobacz dodatek na końcu tego artykułu, aby uzyskać informacje na temat konfiguracji klucza ECDSA w AWS KMS.

Możesz uogólnić z tego przypadku użycia na własną architekturę, wykorzystując załączone przykłady kodów.

Scenariusz: powiadomienia o narażeniu w cyfrowym śledzeniu kontaktów na temat COVID-19

Jako część COVID-19 digital contact tracing system powiadomień o narażeniu (Exposure Notifications system) potwierdza, że wyniki testów pochodzą z legalnej, zaufanej placówki badawczej, jednocześnie chroniąc tożsamość pacjenta. Aby ułatwić śledzenie kontaktów z zachowaniem prywatności, system tworzy anonimowy łańcuch zaufania z podpisami cyfrowymi.

System umożliwia placówkom ochrony zdrowia wysyłanie powiadomień na temat potencjalnego narażenia za pośrednictwem urządzeń mobilnych do użytkowników, których telefon znajdował się w pobliżu użytkownika z potwierdzonym pozytywnym wynikiem diagnozy COVID-19. Aby chronić tożsamość i dane osobowe ani użytkownik inicjujący, ani odbiorcy nie znają nawzajem swojej tożsamości ani miejsca narażenia.

Rysunek nr 2 przedstawia architekturę. Serwer Weryfikacji Narażenia (EVS) zapewnia zestaw interfejsów API, które umożliwiają użytkownikowi mobilnemu (pacjentowi) oraz pracownikowi urzędu zdrowia publicznego dostarczenie dowodów kryptograficznych na temat tego, że pozytywna diagnoza została wydana przez zweryfikowane laboratorium.

Serwer Powiadomień o Narażeniu (ENS) otrzymuje zaufane, anonimowe informacje o narażeniu z zarejestrowanych aplikacji mobilnych, a następnie wysyła powiadomienie o potencjalnym narażeniu do wszystkich urządzeń mobilnych w skonfigurowanej domenie. Każda aplikacja mobilna wykorzystuje zagregowane, anonimowe informacje (Temporary Exposure Keys lub TEK) do obliczenia potencjalnego narażenia.

W tym przykładzie usługa EVS działa w chmurze AWS i wykorzystuje funkcję AWS Lambda z dostępem do AWS KMS do podpisywania. ENS działa gdzie indziej, zarządzany przez stronę trzecią.

Architektura Systemu Powiadamiania o Narażeniu posiada właściwości, które ograniczają bezpośrednie korzystanie z AWS KMS API:

  • Serwery EVS i ENS nie mają łączności i są obsługiwane przez różne podmioty;
  • Serwer ENS weryfikuje dużą liczbę podpisów, potencjalnie miliony dziennie.

Figure 2: COVID-19 Exposure Notification System with AWS-based EVS

 

Pora prześledzić każdy krok i pokazać, jak można wykorzystać weryfikację podpisu cyfrowego w trybie offline do przekazywania informacji przez łańcuch zaufania bez ujawniania tożsamości, jednocześnie pomagając w zapewnieniu możliwości wykrycia wszelkich manipulacji w wynikach diagnozy.

  1. Podczas konfigurowania systemu administrator EVS powiązany z placówką laboratoryjną generuje parę kluczy ECDSA poprzez wykorzystanie AWS KMS. Administrator, w imieniu placówki laboratoryjnej, dokonuje rejestracji w PDS, podając dane laboratorium oraz klucz publiczny pary kluczy ECDSA. To przekazywanie informacji obejmuje również metadane dotyczące klucza i parametrów podpisu, takich jak identyfikator klucza, krzywa eliptyczna użyta do utworzenia klucza oraz użyty algorytm haszujący. ENS buduje zaufanie poprzez umowny proces offline.
  2. Użytkownik uczestniczący w powiadomieniach o narażeniu został zdiagnozowany na COVID-19. Urząd Zdrowia Publicznego (PHA) wydaje krótkotrwały, czytelny dla człowieka, jednorazowy kod, który użytkownik może wprowadzić do aplikacji mobilnej, wydawany przez EVS. Jednorazowy kod jest krótki i łatwy w użyciu dla człowieka, ale w związku z tym jego zabezpieczenia są ograniczone. 
  3. Aplikacja wymienia jednorazowy kod na tymczasowy token API, co poprawia bezpieczeństwo późniejszej komunikacji. Aplikacja prowadzi użytkownika przez pytania związane ze stanem jego zdrowia bez sugerowania podawania jakichkolwiek informacji identyfikujących.
  4. TEK są generowane przez telefon i transmitowane do pobliskich telefonów za pomocą sygnałów Bluetooth Low Energy. Kiedy telefon wykrywa TEK na innym smartfonie, oznacza to, że dwa telefony znajdowały się fizycznie w pobliżu przez wystarczająco długi czas, aby stworzyć ryzyko zarażenia. Telefony nieprzerwanie przesyłają i zbierają TEK. W określonych odstępach czasu telefon zbiera zebrane TEK w danym okresie, stosuje funkcję skrótu i wysyła wynikowy skrót do EVS w celu podpisania. Tymczasowy token API zabezpiecza to wywołanie API.
  5. Funkcja Lambda w EVS wywołuje AWS KMS w celu podpisania JWT, wykorzystując klucz ECDSA, który został utworzony w kroku 1. JWS zawiera niestandardowe oświadczenia, że osoba powiązana z urządzeniem mobilnym została zdiagnozowana na COVID-19 w określonym dniu oraz skrót TEK, ale nie zawiera informacji identyfikujących osobę lub urządzenie. EVS zwraca podpisany token JWT do aplikacji mobilnej.
  6. Podpisane JWT i TEK są wysyłane do ENS w celu powszechnej dystrybucji TEK. ENS weryfikuje tożsamość EVS, wyodrębniając identyfikator klucza osadzony w odebranym tokenie JWT i dopasowując identyfikator klucza do jego wcześniej skonfigurowanej wartości z kroku 1. Następnie ENS weryfikuje kryptograficznie podpis cyfrowy. Po zakończeniu tego procesu ENS posiada dowód, że zaufane laboratorium dostarczyło wyniki, ale nie posiada informacji o rzeczywistym pacjencie.
  7. TEK są rozdzielane do docelowej topografii. Aplikacja na telefon komórkowy sprawdza pobrane TEK z własną listą wygenerowanych TEK i ostrzega użytkownika o potencjalnym narażeniu, jeśli zostanie znalezione dopasowanie.

Korzyści z architektury

Serwer ENS musi wiedzieć, że tokeny JWT przesłane do dystrybucji stanowią prawidłowe diagnozy. W skali krajowej wywoływanie zewnętrznego interfejsu API przy każdym sprawdzaniu podpisu cyfrowego byłoby zbyt kosztowne i skomplikowane. Poprzez sprawdzanie podpisów przesyłanych wraz z wiadomością w punkcie odbioru bez zewnętrznych zależności system może być szybszy i bardziej niezawodny.

Chociaż przedstawiciel urzędu zdrowia publicznego musi znać tożsamość pacjenta, serwer dystrybucji nie musi. Poprzez wykorzystywanie podpisów cyfrowych tworzysz łańcuch anonimowego zaufania – serwer powiadomień ufa serwerowi weryfikacji, a podpis JWT z serwera weryfikacji stosuje to zaufanie i wykrywa zmiany danych.

Podpisane JWT stanowią kluczowy komponent architektury, który umożliwia działanie tego systemu. Teraz, gdy już wiesz, dlaczego warto używać podpisanych tokenów JWT, sprawdź, w jaki sposób z nimi współpracować.

Wdrażanie rozwiązania

W tej sekcji autorzy udostępniają przykłady kodu, które implementują kroki w przykładowym scenariuszu. Zachowano kod podpisywania niezależny od domeny; możesz dostosować go do własnego przypadku użycia.

Podpisz JWT

W kroku 5 poprzedniej sekcji serwer ENV podpisuje token JWT, który wskazuje, że znane, zaufane laboratorium dostarczyło wyniki.

JWT jest podpisany zgodnie ze standardem ES265 (ECDSA przy użyciu krzywej P-256 i skrótu SHA-256), zgodnie z wymaganiami specyfikacji EVS. System EVS wykonuje następujące kroki:

  1. Tworzy nagłówek JWT, który przenosi metadane podpisywania, w tym podpis i algorytm haszujący (na przykład ES265) oraz używany identyfikator klucza.
  2. Tworzy ładunek JWT zawierający oświadczenia potwierdzające diagnozę COVID-19 pacjenta i skrót TEK.
  3. Tworzy nagłówek JWT i obiekty JSON ładunku w ciągi i koduje je w formacie base64url. Następnie system łączy ze sobą dwa ciągi oddzielone kropką, tworząc ciąg, który ma być podpisany.
  4. Wywołuje interfejs API AWS KMS, wprowadzając identyfikator ECDSA CMK, ciąg znaków do podpisania oraz używany algorytm podpisywania.
  5. Koduje bajty podpisu binarnego, które zostały zwrócone z usługi AWS KMS w formacie base64url.
  6. Dołącza ciąg podpisu base64url do pierwszych dwóch części tokenu JWT oddzielonych znakiem kropki, aby utworzyć końcowy token JWT.
  7. Zwraca token JWT z powrotem do aplikacji mobilnej pacjenta.

Powyższe kroki możesz wdrożyć w dowolnym języku programowania przy użyciu zestawu AWS KMS SDK lub AWS KMS http API. Poniższy kod przedstawia przykładową implementację w Golang, która używa zestawu AWS SKD for Go V2.

Uwaga: we wszystkich poniższych przykładach kodu musisz zmienić wartości oznaczone jako <your…>

 

GO
package
main import ( "context" "encoding/base64" "fmt" "log" "strings" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/kms" "github.com/aws/aws-sdk-go-v2/service/kms/types" ) func main() { // Load the Shared AWS Configuration (~/.aws/config) cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Fatal(err) } // Create an AWS KMS service client client := kms.NewFromConfig(cfg) // As per the JWT specification (https://tools.ietf.org/html/rfc7519), // the JWT header, payload, and signature are base64url encoded strings // concatenated by a period (‘.’) character. The JWT signing string // is the JWT header and payload strings joined by a period character. // Example signingString := "eyJhbGciOiJFUzI1NiIsImtpZCI6ImFsaWFzL0VOVlNfVlRfU0lHTklOR19LRVkiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhd3MtZXhwb3N1cmUtbm90aWZpY2F0aW9ucy12ZXJpZmljYXRpb24tc2VydmVyIiwiZXhwIjoxNTk5NTg5NTM0LCJqdGkiOiJxMHg1N0lrRkMvSU9hdzAvNkpYVWhvaHFnV3RqZFVSaUNWMGpuVEpvR1BxTkNsbHdBWWhtTVJLUk1YOXUwb1I2bEtyTXNVSkdGZFJ6aCtncEJiakpCTWR4dVJBN3llYzEyWmE1SzJUMEFWWjZhMVdjNklYQ1ZlNGR6aHkyckJFbiIsImlhdCI6MTU5OTU4OTIzNCwiaXNzIjoiYXdzLWV4cG9zdXJlLW5vdGlmaWNhdGlvbnMtdmVyaWZpY2F0aW9uLXNlcnZlciIsInN1YiI6Im5lZ2F0aXZlLiJ9" signingString := "<your-jwt-header-and-payload-b64url-joined-by-period>" // Populate signing parameters for the AWS KMS Sign() method // https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/kms#SignInput signingAlgorithm := types.SigningAlgorithmSpecEcdsaSha256 kid := "<your-aws-kms-key-id-or-alias>" signInput := kms.SignInput{ KeyId: &kid, Message: []byte(signingString), SigningAlgorithm: signingAlgorithm, } fmt.Printf("Sign Input: %#v\n", signInput) // Invoke the AWS KMS Sign() API // https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/kms#Client.Sign signOutput, err := client.Sign(context.TODO(), &signInput) if err != nil { panic(err) } fmt.Printf("Sign Output Signature: %v\n", signOutput.Signature) // Convert signature bytes to base-64 URL encoding (without padding) sigB64 := base64.RawURLEncoding.EncodeToString(signOutput.Signature) // Append signature to first two parts of JWT to produce signed JWT signedJwt := strings.Join([]string{signingString, sigB64}, ".") fmt.Printf("Signed JWT: %s\n", signedJwt) }

Zweryfikuj podpisy

Ze względu na to, że AWS KMS tworzy ustandaryzowane, interoperacyjne podpisy ECDSA, do weryfikacji podpisu można użyć dowolnej standardowej implementacji ECDSA. Ten krok jest wykonywany przez serwer powiadomień o narażeniu w przykładzie, ale możesz użyć tego kodu w dowolnym miejscu, w którym chcesz zweryfikować podpis utworzony w poprzedniej sekcji.

Wszystkie trzy z poniższych przykładów osiągają ten sam wynik końcowy – weryfikują podpis. Autorzy artykułu dostarczyli przykłady, które używają Golanga, Pythona i OpenSSL, aby zademonstrować elastyczność tego podejścia.

Weryfikacja podpisu w Golang

Kod Golang przedstawiony w tej sekcji wykorzystuje wbudowany pakiet kryptograficzny w celu zweryfikowania podpisu. Kod wykonuje następujące kroki:

  • Dzieli wejściowy ciąg JWT na poszczególne części (nagłówek, ładunek i podpis) oddzielone znakiem kropki („ . ”).
  • Oblicza skrót SHA-256 podpisanego ciągu, czyli pierwszych dwóch części ciągu JWT: nagłówka i ładunku.
  • Dekoduje klucz publiczny ECDSA w formacie PEM, który został wyodrębniony z AWS KMS w ramach ustanowienia zaufania EVS-to-ENS.
  • Konwertuje ciąg podpisu ze znaków base64url na binarny.
  • Rozpakowuje zakodowaną sygnaturę ASN.1 i wyodrębnia wartości całkowite R i S sygnatury.
  • Wywołuje metodę ecdsa.Verify() z kluczem publicznym, wartością skrótu, wartościami R i S argumentów podpisu i otrzymuje wynik weryfikacji.

 

GO
package main import ( "crypto/ecdsa" "crypto/sha256" "crypto/x509" "encoding/asn1" "encoding/base64" "encoding/pem" "fmt" "math/big" "strings" ) func main() { /* Input your JWT string for signature verification. Note: The code example below does not show extraction of ‘kid’, ‘iss’, and ‘aud’ fields from the input JWT and the matching of these fields against previously configured values (at the time of offline trust establishment). You should consider implementing these checks as a security measure. The code assumes the ECDSA elliptic curve (NIST P-256) and the hashing algorithm (SHA-256) used by the JWT, but consider extracting the curve and the algorithm from the input JWT (the “alg” field in the JWT header) to apply the proper algorithms at runtime. You may use the sample JWT string and the following sample public key to run this code. The signature in the sample JWT string below was produced using the private key paired with the following sample public key. */ // jwtStr := "eyJhbGciOiJFUzI1NiIsImtpZCI6ImFsaWFzL0VOVlNfVlRfU0lHTklOR19LRVkiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhd3MtZXhwb3N1cmUtbm90aWZpY2F0aW9ucy12ZXJpZmljYXRpb24tc2VydmVyIiwiZXhwIjoxNTk5NTg5NTM0LCJqdGkiOiJxMHg1N0lrRkMvSU9hdzAvNkpYVWhvaHFnV3RqZFVSaUNWMGpuVEpvR1BxTkNsbHdBWWhtTVJLUk1YOXUwb1I2bEtyTXNVSkdGZFJ6aCtncEJiakpCTWR4dVJBN3llYzEyWmE1SzJUMEFWWjZhMVdjNklYQ1ZlNGR6aHkyckJFbiIsImlhdCI6MTU5OTU4OTIzNCwiaXNzIjoiYXdzLWV4cG9zdXJlLW5vdGlmaWNhdGlvbnMtdmVyaWZpY2F0aW9uLXNlcnZlciIsInN1YiI6Im5lZ2F0aXZlLiJ9.MEYCIQCiLqsE2bxKdDi3NvX0mXqcHbvvDtI9zcCwPUHQiQutoQIhAJDhhCdRSlk_QYU_7_9X11yEcPzNHWF4qq2wRG66w7Lh" jwtStr := "<your-jwt-string>" jwtParts := strings.Split(jwtStr, ".") // Compute a SHA-256 hash of the header and payload parts of the JWT string hashInput := strings.Join(jwtParts[0:2], ".") digest := sha256.Sum256([]byte(hashInput)) /* Decode the ECDSA public key copied from AWS KMS Sample public key -- pubPemKey := "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErPPPHw8ilBwBNBhRZjyVOnKoHOri\n" + "nS1ifFDScjQR4GRIcAzsTwlKjblMOcmxwy9TNOGrGnHTjw1XnIrBBhOPhg==\n" + "-----END PUBLIC KEY-----" */ pubPemKey := "-----BEGIN PUBLIC KEY-----<your-key>-----END PUBLIC KEY-----" block, _ := pem.Decode([]byte(pubPemKey)) if block == nil || block.Type != "PUBLIC KEY" { panic("Failed to decode PEM public key") } pubEcdsaKey, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { panic("Failed to parse ECDSA public key") } // Decode the signature to extract a DER-encoded byte string sigDer, err := base64.RawURLEncoding.DecodeString(jwtParts[2]) if err != nil { panic(err) } // Unmarshal the R and S components of the ASN.1-encoded signature type ECDSASignature struct { R, S *big.Int } sigRS := &ECDSASignature{} _, err = asn1.Unmarshal(sigDer, sigRS) if err != nil { panic(err) } // Verify signature ok := ecdsa.Verify(pubEcdsaKey.(*ecdsa.PublicKey), digest[:], sigRS.R, sigRS.S) if !ok { panic("Signature verification failed") } fmt.Println("Signature verification successful") }

Weryfikacja podpisu w Pythonie

Kod Pythona przedstawiony w poniższej sekcji wykorzystuje moduł python-ecdsa w celu zweryfikowania sygnatury. Kod wykonuje następujące kroki:

  • Dzieli wejściowy ciąg JWT na poszczególne części (nagłówek, ładunek i podpis) rozdzielone znakiem kropki.
  • Konwertuje ciąg podpisu ze znaków base64url na binarny.
  • Weryfikuje podpis za pomocą klucza publicznego, podpisanego ciągu i argumentów algorytmu haszującego, a następnie otrzymuje wynik weryfikacji.
PYTHON
import base64 from hashlib import sha256 import ecdsa from ecdsa.util import sigdecode_der """ Input your JWT string for signature verification. You may use the sample JWT string and the following sample public key to run this code. The signature in the sample JWT string below was produced using the private key paired with the following sample public key. """ #jwtStr = "eyJhbGciOiJFUzI1NiIsImtpZCI6ImFsaWFzL0VOVlNfVlRfU0lHTklOR19LRVkiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhd3MtZXhwb3N1cmUtbm90aWZpY2F0aW9ucy12ZXJpZmljYXRpb24tc2VydmVyIiwiZXhwIjoxNTk5NTg5NTM0LCJqdGkiOiJxMHg1N0lrRkMvSU9hdzAvNkpYVWhvaHFnV3RqZFVSaUNWMGpuVEpvR1BxTkNsbHdBWWhtTVJLUk1YOXUwb1I2bEtyTXNVSkdGZFJ6aCtncEJiakpCTWR4dVJBN3llYzEyWmE1SzJUMEFWWjZhMVdjNklYQ1ZlNGR6aHkyckJFbiIsImlhdCI6MTU5OTU4OTIzNCwiaXNzIjoiYXdzLWV4cG9zdXJlLW5vdGlmaWNhdGlvbnMtdmVyaWZpY2F0aW9uLXNlcnZlciIsInN1YiI6Im5lZ2F0aXZlLiJ9.MEYCIQCiLqsE2bxKdDi3NvX0mXqcHbvvDtI9zcCwPUHQiQutoQIhAJDhhCdRSlk_QYU_7_9X11yEcPzNHWF4qq2wRG66w7Lh" jwtStr = "<your-jwt-string>" jwtParts = jwtStr.split(".") # Compute a SHA-256 hash of the header and payload parts of the JWT string signedStr = ".".join(jwtParts[0:2]).encode(encoding="ASCII") signature = base64.urlsafe_b64decode(jwtParts[2]) """ Decode the ECDSA public key copied from AWS KMS Sample public key -- """ """ pubPemKey = ("-----BEGIN PUBLIC KEY-----\n" "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErPPPHw8ilBwBNBhRZjyVOnKoHOri\n" "nS1ifFDScjQR4GRIcAzsTwlKjblMOcmxwy9TNOGrGnHTjw1XnIrBBhOPhg==\n" "-----END PUBLIC KEY-----") """ pubPemKey = "-----BEGIN PUBLIC KEY-----<your-key>-----END PUBLIC KEY-----" verifyKey = ecdsa.VerifyingKey.from_pem(pubPemKey) if verifyKey.verify(signature, signedStr, sha256, sigdecode=sigdecode_der): print ("Signature verification successful") else: print ("Signature verification failed")

Weryfikacja podpisu za pomocą OpenSSL

Aby zweryfikować podpis, możesz użyć narzędzia wiersza poleceń openssl, tak jak przedstawiono poniżej.

openssl dgst -sha256 -verify pubkey.pem -signature sig.der msg.txt

W tym poleceniu nakazujesz openssl obliczenie skrótu SHA-256 wiadomości wejściowej, przechowywanej w msg.txt. Kontynuując przykład JWT, ten plik będzie zawierał dwie pierwsze części ciągu JWT – nagłówek i ładunek oddzielone znakiem kropki. Jest to oryginalna wiadomość, która została zahaszowana, a wynikowa wartość skrótu zostanie zweryfikowana z dostarczonym podpisem przez OpenSSL. Przykładowy plik msg.txt znajduje się poniżej.

eyJhbGciOiJFUzI1NiIsImtpZCI6ImFsaWFzL0VOVlNfVlRfU0lHTklOR19LRVkiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhd3MtZXhwb3N1cmUtbm90aWZpY2F0aW9ucy12ZXJpZmljYXRpb24tc2VydmVyIiwiZXhwIjoxNTk5NjU3MDA1LCJqdGkiOiJNbU15dFM0MWxZWWV5OXNDcEl4OVZaS2l3emd5SWt0cFZlSjdrNElkL29aTGdGZk5MWDJ0NDh3dy9FRWx6WjVLYTdja3kwUHNIdnROanUyTGV0OG9vUFdNUUpERDJZSjBsdE1aU0ZxY2FiYlpKUWt3YnREVHpobE1YKytycHE5dCIsImlhdCI6MTU5OTY1NjcwNSwiaXNzIjoiYXdzLWV4cG9zdXJlLW5vdGlmaWNhdGlvbnMtdmVyaWZpY2F0aW9uLXNlcnZlciIsInN1YiI6Im5lZ2F0aXZlLiJ9
 

Określasz nazwę pliku, który zawiera klucz publiczny ECDSA – pubkey.pem. Jest to klucz publiczny ECDSA w formacie PEM, który został wyodrębniony z AWS KMS.

BASH

vi pubkey.pem

———BEGIN PUBLIC KEY———<your-key>———END PUBLIC KEY———

Przykładowy plik pubkey.pem został przedstawiony poniżej.

BASH
-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErPPPHw8ilBwBNBhRZjyVOnKoHOri nS1ifFDScjQR4GRIcAzsTwlKjblMOcmxwy9TNOGrGnHTjw1XnIrBBhOPhg== -----END PUBLIC KEY-----

Sig.der argument zawiera bajty podpisu w formacie DER. Za pomocą następującego polecenia można przekonwertować bajty podpisu zakodowane algorytmem base-64 w ciągu JWT na format DER.

BASH

echo -n <your signature in base-64 encoded format> | base64 -D > sig.der

Przykładowe bajty podpisu w base-64 zostały przedstawione poniżej.

BASH

MEUCIQCM7xBLFJF6rL3PCz4vqjtxkag2eCI4oFvphrXrgDw6EQIgPfLMrd83QI1XaIKGNdTsDwdmdPNtVOaU5566-i2oBSc

Komenda openssl wygeneruje następujące dane wyjściowe, jeśli wszystkie z parametrów zostały podane poprawnie.

BASH

Verified OK

Kiedy używać AWS KMS do weryfikacji podpisów

Chociaż ten post przedstawia, w jaki sposób weryfikować podpisy bez wywoływania AWS KMS w rozproszonym przypadku użycia, wiele scenariuszy korzysta z weryfikacji podpisów za pomocą AWS KMS. Takie korzyści obejmują:

  • Kontrolę autoryzacji wniosków o weryfikację podpisu za pomocą polityk AWS Identity and Access Management (IAM) i AWS KMS;
  • Osiągnięcie silnej i niezaprzeczalnej oryginalności podpisanej wiadomości;
  • Audyt wywołań API z natywną integracją AWS CloudTrial;
  • Monitorowanie ilości próśb o weryfikację dzięki natywnej integracji Amazon CloudWatch;
  • Natychmiastowe unieważnienie kluczy;
  • Zmienianie kolejności par kluczy, aby weryfikacja podpisu była zsynchronizowana ze zaktualizowanymi kluczami.

Aby uzyskać więcej wskazówek dotyczących używania kluczy publicznych poza usługą AWS KMS, zobacz Special considerations for downloading public keys.

Podsumowanie

Podpisy cyfrowe umożliwiają szybkie i niedrogie sprawdzanie autentyczności informacji bez konieczności sięgania do innego źródła.

AWS KMS jest potężnym i wygodnym narzędziem, które zostało zaprojektowane tak, aby dobrze rozwiązywać określony zestaw przypadków użycia. Jednakże czasami możesz projektować system, który musi dzielić zaufanie między różnymi stronami lub możesz potrzebować zweryfikować podpis za pomocą klucza publicznego, który został wygenerowany poza KMS bądź masz wymagania dotyczące wydajności, które uniemożliwiają wywołania AWS KMS. W takich przypadkach możesz użyć technik opisanych w tym artykule, aby zbudować zaufane systemy oparte na podpisach cyfrowych.

Załącznik: konfiguracja klucza ECDSA w AWS KMS

AWS KMS obsługuje dwa popularne mechanizmy podpisu cyfrowego – ECDSA i RSA. Autorzy w tym artykule wykorzystują ECDSA zgodnie z wymaganiami specyfikacji EVS.

Klucz ECDSA jest określany jako asymetryczny klucz główny klienta (CMK) w AWS KMS i może zostać utworzony zgodnie z opisem w sekcji Tworzenie kluczy w dokumentacji AWS KMS. Rysunek nr 3 przedstawia konfigurację kryptograficzną dla klucza ECDSA utworzonego w AWS KMS.

Figure 3: Cryptographic configuration of an ECDSA key pair in AWS KMS

Pole Typ Klucza w tej konfiguracji wskazuje, że klucz jest asymetryczny. Pole Origin wskazuje, że klucz ten został wygenerowany w AWS KMS, a nie został zaimportowany.

Wartość pola Key Spec ECC_NIST_P256 wskazuje, że tan klucz może zostać użyty do tworzenia podpisów ECDSA zgodnych z NIST FIPS 186-4 (sekcja 6.4), gdzie klucz został wygenerowany przy użyciu krzywej oznaczonej jako secp256r1.

Pole Użycie Klucza wskazuje, że ten klucz może zostać wykorzystany tylko do podpisywania u weryfikacji. Pole Algorytmy podpisywania wskazuje, że algorytm haszujący SHA-256 jest używany do obliczania skrótu podpisanej lub zweryfikowanej wiadomości wejściowej.

Pozostałe specyfikacje klucza ECDSA mogą być wybrane na podstawie wymagań bezpieczeństwa i wskazówek zawartych w dokumentacji AWS KMS.

źródło: AWS

 

Case Studies
Referencje

Z przyjemnością polecamy firmę Hostersi, z którą mieliśmy przyjemność współpracować przy okazji wdrożenia skalowalnej infrastruktury w Amazon Web Services, opartej o technologię Kubernetes i metodykę DevOps.  Hostersi okazali się niezwykle proaktywnym partnerem, który nie tylko wdrażał wskazane rozwiązania, ale proponował optymalne narzędzia i technologie, które sprawiły, że efekt wdrożenia jest dla nas w pełni satysfakcjonujący. Polecamy!

Grzegorz Lentzy
IT Director LINK Mobility
W skrócie o nas
Specjalizujemy się w dostarczaniu rozwiązań IT w obszarach projektowania infrastruktury serwerowej, wdrażania chmury obliczeniowej, opieki administracyjnej i bezpieczeństwa danych.