grpc, http2 and wireshark

This page shows how to decrypt gRPC traffic in wireshark.

この頁は、wireshark でgRPC 通信を復号する方法を示す。

grpc クイックスタート

https://grpc.io/docs/quickstart/go.html

に従う。新し目の wireshark だと、

パケットを右クリックして、と、してデコード、現在、を HTTP2 にすると、grpc としてデコードまでしてくれる。

HyperText Transfer Protocol 2

Stream: Magic

Stream: SETTINGS, Stream ID: 0, Length 36

Stream: HEADERS, Stream ID: 1, Length 286, POST /helloworld.Greeter/SayHello

Stream: WINDOW_UPDATE, Stream ID: 1, Length 4

Stream: DATA, Stream ID: 1, Length 10

GRPC Message: /helloworld.Greeter/SayHello, Request

Compressed Flag: Not Compressed (0)

Message Length: 5

Message Data: 5 bytes

Protocol Buffers: application/grpc,/helloworld.Greeter/SayHello,request

Field[1]

.000 1... = Field Number: 1

.... .010 = Wire Type: Length-delimited (2)

Value Length: 3

Value: 796f75

Stream: WINDOW_UPDATE, Stream ID: 0, Length 4

Stream: PING, Stream ID: 0, Length 8

HyperText Transfer Protocol 2

Stream: HEADERS, Stream ID: 1, Length 119, 200 OK

Stream: DATA, Stream ID: 1, Length 18 (partial entity body)

Stream: HEADERS, Stream ID: 1, Length 30

GRPC Message: /helloworld.Greeter/SayHello, Response

Compressed Flag: Not Compressed (0)

Message Length: 13

Message Data: 13 bytes

Protocol Buffers: application/grpc,/helloworld.Greeter/SayHello,response

Field[1]

.000 1... = Field Number: 1

.... .010 = Wire Type: Length-delimited (2)

Value Length: 11

Value: 48656c6c6f2c20796f7521

wireshark で、 http2 with tls セッションを平文で見る

http://d.hatena.ne.jp/ozuma/20140413/1397397632

最新情報では、

https://wiki.wireshark.org/SSL

秘密鍵を入れる RSA keys list ダイアログで、 ip address=any , port=0 でもよくなった。

ssl debug file を指定すると、そこに、デコードがなぜ失敗したかわかるメッセージが出る。

ssl_decrypt_pre_master_secret: session uses Diffie-Hellman key exchange (cipher suite 0xC014 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) and cannot be decrypted using a RSA private key file.

なあんだ。Diffie-Hellman が使われていたからダメだったのか。

curl なら、引数で、使う暗号を指定できる。

https://unix.stackexchange.com/questions/208437/how-to-convert-ssl-ciphers-to-curl-format

見えた

サーバは、 nginx

$ curl --ciphers rsa_aes_256_sha https://localhost/

No. Time Source Destination Protocol Length Info

16 422.305987666 ::1 ::1 TLSv1.2 215 Client Hello

Secure Sockets Layer

TLSv1.2 Record Layer: Handshake Protocol: Client Hello

Content Type: Handshake (22)

Version: TLS 1.0 (0x0301)

Length: 124

Handshake Protocol: Client Hello

Handshake Type: Client Hello (1)

Length: 120

Version: TLS 1.2 (0x0303)

Random

Session ID Length: 0

Cipher Suites Length: 2

Cipher Suites (1 suite)

Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035) <=クライアントからの暗号提案

Compression Methods Length: 1

Compression Methods (1 method)

Extensions Length: 77

Extension: server_name

Extension: renegotiation_info

Extension: Application Layer Protocol Negotiation

Type: Application Layer Protocol Negotiation (0x0010)

Length: 14

ALPN Extension Length: 12

ALPN Protocol

ALPN string length: 8

ALPN Next Protocol: http/1.1

ALPN string length: 2

ALPN Next Protocol: h2 <=これが、 http2 with tls

Extension: signature_algorithms

18 422.306592241 ::1 ::1 TLSv1.2 1217 Server Hello, Certificate, Server Hello Done

Secure Sockets Layer

TLSv1.2 Record Layer: Handshake Protocol: Server Hello

Content Type: Handshake (22)

Version: TLS 1.2 (0x0303)

Length: 90

Handshake Protocol: Server Hello

Handshake Type: Server Hello (2)

Length: 86

Version: TLS 1.2 (0x0303)

Random

Session ID Length: 32

Session ID: cf40e6321e60297814e538c24702ac193360c18c99c1096a...

Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)

Compression Method: null (0)

Extensions Length: 14

Extension: renegotiation_info

Extension: Application Layer Protocol Negotiation

Type: Application Layer Protocol Negotiation (0x0010)

Length: 5

ALPN Extension Length: 3

ALPN Protocol

ALPN string length: 2

ALPN Next Protocol: h2

TLSv1.2 Record Layer: Handshake Protocol: Certificate

TLSv1.2 Record Layer: Handshake Protocol: Server Hello Done

No. Time Source Destination Protocol Length Info

20 422.313666387 ::1 ::1 TLSv1.2 428 Client Key Exchange, Change Cipher Spec, Finished

No. Time Source Destination Protocol Length Info

21 422.339419788 ::1 ::1 TLSv1.2 161 Change Cipher Spec, Finished

No. Time Source Destination Protocol Length Info

22 422.340368279 ::1 ::1 HTTP2 155 Magic

Secure Sockets Layer

TLSv1.2 Record Layer: Application Data Protocol: http2

Content Type: Application Data (23)

Version: TLS 1.2 (0x0303)

Length: 64

Encrypted Application Data: 9330bdfea71aac706c8f2c8a40fc2405e9edfe5b04df079c...

HyperText Transfer Protocol 2

Stream: Magic

Magic: PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n <=http2 の最初の電文

No. Time Source Destination Protocol Length Info

23 422.340507409 ::1 ::1 HTTP2 139 SETTINGS

HyperText Transfer Protocol 2

Stream: SETTINGS, Stream ID: 0, Length 0

Length: 0

Type: SETTINGS (4)

Flags: 0x00

0... .... .... .... .... .... .... .... = Reserved: 0x0

.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0

No. Time Source Destination Protocol Length Info

24 422.340686491 ::1 ::1 HTTP2 171 HEADERS

HyperText Transfer Protocol 2

Stream: HEADERS, Stream ID: 1, Length 26

Length: 26

Type: HEADERS (1)

Flags: 0x05

0... .... .... .... .... .... .... .... = Reserved: 0x0

.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1

[Pad Length: 0]

Header Block Fragment: 8284874186a0e41d139d097a8825b650c3abb615c153032a...

[Header Length: 125]

[Header Count: 6]

Header: :method: GET

Header: :path: /

Header: :scheme: https

Header: :authority: localhost

Header: user-agent: curl/7.51.0

Header: accept: */*

Padding: <MISSING>

No. Time Source Destination Protocol Length Info

26 422.403586354 ::1 ::1 HTTP2 299 SETTINGS, WINDOW_UPDATE, SETTINGS, HEADERS

HyperText Transfer Protocol 2

Stream: SETTINGS, Stream ID: 0, Length 18

Length: 18

Type: SETTINGS (4)

Flags: 0x00

0... .... .... .... .... .... .... .... = Reserved: 0x0

.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0

Settings - Max concurrent streams : 128

Settings - Initial Windows size : 65536

Settings - Max frame size : 16777215

Stream: WINDOW_UPDATE, Stream ID: 0, Length 4

Length: 4

Type: WINDOW_UPDATE (8)

Flags: 0x00

0... .... .... .... .... .... .... .... = Reserved: 0x0

.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0

0... .... .... .... .... .... .... .... = Reserved: 0x0

.111 1111 1111 1111 0000 0000 0000 0000 = Window Size Increment: 2147418112

Stream: SETTINGS, Stream ID: 0, Length 0

Length: 0

Type: SETTINGS (4)

Flags: 0x01

0... .... .... .... .... .... .... .... = Reserved: 0x0

.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0

Stream: HEADERS, Stream ID: 1, Length 109

Length: 109

Type: HEADERS (1)

Flags: 0x04

0... .... .... .... .... .... .... .... = Reserved: 0x0

.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1

[Pad Length: 0]

Header Block Fragment: 887689aa6355e580ae112e1f6196df697e94034a6e2d6a08...

[Header Length: 242]

[Header Count: 8]

Header: :status: 200

Header: server: nginx/1.12.1

Header: date: Tue, 04 Sep 2018 10:34:43 GMT

Header: content-type: text/html

Header: content-length: 3700

Header: last-modified: Tue, 15 Aug 2017 08:51:37 GMT

Header: etag: "5992b619-e74"

Header: accept-ranges: bytes

Padding: <MISSING>

No. Time Source Destination Protocol Length Info

27 422.404081133 ::1 ::1 HTTP2 139 SETTINGS

HyperText Transfer Protocol 2

Stream: SETTINGS, Stream ID: 0, Length 0

Length: 0

Type: SETTINGS (4)

Flags: 0x01

0... .... .... .... .... .... .... .... = Reserved: 0x0

.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0

No. Time Source Destination Protocol Length Info

28 422.425518693 ::1 ::1 HTTP2 3851 DATA

HyperText Transfer Protocol 2

Stream: DATA, Stream ID: 1, Length 3700

Length: 3700

Type: DATA (0)

Flags: 0x01

0... .... .... .... .... .... .... .... = Reserved: 0x0

.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1

[Pad Length: 0]

Data: 3c21444f43545950452068746d6c205055424c494320222d... <=これが、 index.html

Padding: <MISSING>

Wireshark SSL debug log

Wireshark version: 2.2.7 (wireshark-2.2.7)

ssl_init private key file /etc/pki/nginx/private/server.key successfully loaded.

ssl_decrypt_record ciphertext len 64

Ciphertext[64]:

| 93 30 bd fe a7 1a ac 70 6c 8f 2c 8a 40 fc 24 05 |.0.....pl.,.@.$.|

| e9 ed fe 5b 04 df 07 9c 69 c8 69 2d a4 7c e9 50 |...[....i.i-.|.P|

| 92 4c 62 84 fe df 7a f2 a6 29 8e 0f 24 52 67 e2 |.Lb...z..)..$Rg.|

| f1 37 e6 5b 31 8b a9 11 fa e7 63 84 0a d8 09 c0 |.7.[1.....c.....|

Plaintext[48]:

| 50 52 49 20 2a 20 48 54 54 50 2f 32 2e 30 0d 0a |PRI * HTTP/2.0..|

| 0d 0a 53 4d 0d 0a 0d 0a c2 76 5a a0 6f 7e 88 20 |..SM.....vZ.o~. |

| ce 2f e9 5f 5b dd 3d a6 7b ee cd fc 03 03 03 03 |./._[.=.{.......|

grpc with tls 電文を、デコードして見る

https://grpc.io/docs/guides/auth.html

に従って、 tls を使った通信を、見る。

そのままやったら、Diffie-Hellman になったので、使う暗号を制限しないといけない。

https://groups.google.com/forum/#!topic/grpc-io/6L0rfhtvP48

に、credential の関数を生で使えばできる、と書いてある。

できた

サンプルソースの差分

diff --git a/examples/helloworld/greeter_client/main.go b/examples/helloworld/greeter_client/main.go

index 4b99ff5..b83bba3 100644

--- a/examples/helloworld/greeter_client/main.go

+++ b/examples/helloworld/greeter_client/main.go

@@ -22,10 +22,14 @@ import (

"log"

"os"

"time"

+ "crypto/tls"

+ "crypto/x509"

+ "io/ioutil"

"golang.org/x/net/context"

"google.golang.org/grpc"

pb "google.golang.org/grpc/examples/helloworld/helloworld"

+ "google.golang.org/grpc/credentials"

)

const (

@@ -34,8 +38,22 @@ const (

)

func main() {

+ certFile := "./cacert.pem"

+ //creds, err := credentials.NewClientTLSFromFile(certFile, "")

+ b, err := ioutil.ReadFile(certFile)

+ if err != nil {

+ log.Fatalf("cert file: %s", err)

+ }

+ cp := x509.NewCertPool()

+ if !cp.AppendCertsFromPEM(b) {

+ log.Fatal("credentials: failed to append certificates")

+ }

+ creds := credentials.NewTLS(&tls.Config{ServerName: "", RootCAs: cp,

+ CipherSuites: []uint16{

+ tls.TLS_RSA_WITH_AES_256_GCM_SHA384,

+ }})

// Set up a connection to the server.

- conn, err := grpc.Dial(address, grpc.WithInsecure())

+ conn, err := grpc.Dial(address, grpc.WithTransportCredentials(creds))

if err != nil {

log.Fatalf("did not connect: %v", err)

}

diff --git a/examples/helloworld/greeter_server/main.go b/examples/helloworld/greeter_server/main.go

index 702a3b6..f96b862 100644

--- a/examples/helloworld/greeter_server/main.go

+++ b/examples/helloworld/greeter_server/main.go

@@ -28,6 +28,7 @@ import (

"google.golang.org/grpc"

pb "google.golang.org/grpc/examples/helloworld/helloworld"

"google.golang.org/grpc/reflection"

+ "google.golang.org/grpc/credentials"

)

const (

@@ -47,7 +48,13 @@ func main() {

if err != nil {

log.Fatalf("failed to listen: %v", err)

}

- s := grpc.NewServer()

+ certFile := "./server.crt"

+ keyFile := "./server.key"

+ creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)

+ if err != nil {

+ log.Fatalf("cred: %s", err)

+ }

+ s := grpc.NewServer(grpc.Creds(creds))

pb.RegisterGreeterServer(s, &server{})

// Register reflection service on gRPC server.

reflection.Register(s)

以上