How to Sign with Google’s Private Key

[0xC001]
4 min readApr 2, 2018

--

April Fools’

As some have figured out, I never had true access to the Google server’s private key, I did however have access to Google’s TLS Server (then again, so does just about everyone else in the world).

The only screenshot that was ‘faked’ was the one showing “The Signing”, that’s just me cating a text file that has a real command, but the signature shown is copied from the real signature and pasted in.

Before I tell how I pulled off this little prank, let me talk about the background of how TLS works.

In the TLS handshake, the server will return a message signed by the server’s private key if the cipher suite is set to use ephemeral key exchanges. Here, I used ECDHE-ECDSA-AES128-GCM-SHA256, this has the added benefit (at least for Twitter) of using an ECDSA certificate, where the signature is much shorter (and can fit in a single tweet).

In the ‘old’ way of doing TLS, the server proved its possession of the private key by being the one entity that could decrypt a message from the client containing material used to make the TLS session key. Today, with modern key exchanges like ECDHE, the server still must prove possession of the private key, it does this by signing a message. The message signed in this case was defined by RFC 4492 in section 5.4.

struct {
ECParameters curve_params;
ECPoint public;
} ServerECDHParams;

The ECParams are straight out of the server’s key exchange message:

struct {
ECCurveType curve_type;
NamedCurve namedcurve;
} ECParameters;

The Signature is calculated by signing the hash of the client hello, server hello, and the EC params.

ServerKeyExchange.signed_params.sha_hash
SHA(ClientHello.random + ServerHello.random +
ServerKeyExchange.params);

The message from above that was signed is broken up as follows:
Client Hello (Where the message is placed)
70 72 6f 6f 66 20 6f 66 20 70 72 69 76 61 74 65 20 6b 65 79 20 63 74 72 6c 3b 6e 6f 6e 63 65 2d

Server Hello (random hex)
5a b9 bd a9 b8 39 4f ea 38 53 1e 00 b1 db b6 0c 15 62 a1 41 45 57 14 80 dd c6 71 64 8c f1 8a 28

Specify that a Named Curve is to be used
03
Hex Code for that specific named curve (nist-p256)
00 17
Length of the public key for the ECDHE key exchange
41
Public Key for the ECDHE key exchange

04 56 53 b5 01 fb 24 73 e3 7f f3 60 4c 38 4b d6 50 86 89 36 fc af 01 e9 e2 6a ab 40 ea 86 46 1c 6d 2c 05 72 22 a1 28 59 97 45 7d e0 16 50 da ee 7f 70 5d eb 06 8c 84 c9 92 0d 7c 3e 80 83 05 90 f4

Those hex values are concatenated together to form the server’s message to sign as the server’s proof of possession of the private key and is returned to the client for verification. Because the client hello is under the full control of the client, anything can be placed there, such as a message claiming key compromise.

How to do it

Open Wireshark, connect to the target server over TLS, copy out the client hello as a hex stream. Modify the first 32 bytes to a custom message, then (still capturing with Wireshark), send the modified client hello back to the server with Netcat:

echo ‘<modified client hello hex>’ | xxd -r -p | nc cloud.google.com 443

Wireshark — Client Hello with Message

Copy out the client random, server random, and server elliptic curve params as the “message”.

Next, copy the signature and save that off.

Wireshark — Signature

Finally get a copy of the public key so you can verify the signature.

As @sleevi_ said, “Any attempt to define how that demonstration of compromise is proved is to make it more difficult to report or demonstrate compromise.”

But, we also be careful and know what not to accept. Like an ECDSA signature over endpoints’ random numbers and EC Params or an RSA signature over an MD5 sum concatenated with a SHA-1 hash (this is the way RSA certs prove possession in TLS when used with DHE or ECDHE).

Special April Fools’ Day Message

Happy April!

--

--