Henk en Ingrid aan de RSA

Henk en Ingrid hebben een mening. Een politiek geladen mening die niet door iedereen gewaardeerd wordt, daarom houden ze hem liever voor zichzelf. Om met elkaar te kunnen praten gebruiken ze encryptie, Henk moet zijn boodschap ongezien bij Ingrid kunnen krijgen, terwijl Ingrid wel in staat moet zijn Henk zijn boodschap te lezen. En vice versa natuurlijk, ook Ingrid heeft een foute mening.

Dit doe je met Public Key Cryptografie. Bij Public Key Cryptografie heb je een publieke sleutel die iedereen kan zien en een privé sleutel die alleen van jou is. De publieke sleutel geef je aan iedereen die jou een bericht wil sturen, je kan hem zelfs op het internet zetten. De privé sleutel deel je niet, deze gebruik je om een versleuteld bericht leesbaar te maken.

Gelukkig kan Henk een beetje programmeren! En een beetje is precies genoeg, want zoals Henk zegt:

Wat weten ‘experts’ er nou van?

Dus daar gaan we. Met een kleine disclaimer trouwens: Dit is een oefening in RSA encryptie in Golang. Het is niet gemaakt om in productie gebruikt te worden. En in het echt heet ik geen Henk.

Kijk mee in de code: Clone de repository met het [Golang RSA voorbeeld][1] met:

git clone git@github.com:Grrrben/golang-rsa.git

We beginnen met een RSA identiteit. Een simpele Go struct met  een publieke- en privé sleutel, die struct maakt duidelijk waar we over praten en we kunnen er verschillende methods aan hangen.:

type RsaIdentity struct {
Public *rsa.PublicKey
private *rsa.PrivateKey
}
view raw rsa_identity.go hosted with ❤ by GitHub

Iedereen mag Public benaderen (let ook op de hoofdletter van Public, die de variabele in Go ook echt publiek maakt), private is alleen voor jou. Bij Public Key Cryptografie is het zo dat als Henk Ingrid een bericht wil sturen, hij de publieke sleutel van Ingrid gebruikt om het bericht te versleutelen:

// Encrypt's the message using EncryptOAEP which encrypts the given message with RSA-OAEP.
// https://en.wikipedia.org/wiki/Optimal_asymmetric_encryption_padding
// Returns the encrypted message and an error.
func (r *RsaIdentity) Encrypt(msg []byte, key *rsa.PublicKey) ([]byte, error) {
label := []byte("")
hash := sha256.New()
return rsa.EncryptOAEP(hash, rand.Reader, key, msg, label)
}
view raw rsa_encrypt.go hosted with ❤ by GitHub

Henk wil aan Ingrid een boodschap wil sturen:

Negenennegentig procent van de problemen in de wereld worden veroorzaakt door buitenlanders.

Henk gebruikt daarvoor de publieke sleutel van Ingrid, met de bovenstaande Encrypt functie wordt het onleesbaar.

func TestPublicKeyExample(t *testing.T) {
henk, _ := NewRsaIdentity()
ingrid, _ := NewRsaIdentity()
msg := []byte("Negenennegentig procent van de problemen in de wereld worden veroorzaakt door buitenlanders.")
// Lets encrypt it, we want to sent it to Ingrid, thus, we use her public key.
encryptedMessage, _ := henk.Encrypt(msg, ingrid.public)
fmt.Printf("%s\n", msg)
// Negenennegentig procent van de problemen in de wereld worden veroorzaakt door buitenlanders. �zmNOOe@{��}9������#-��^� ����Jh��l0����c2S���a>�AA/�z�2��*c�@^w�P�Xu�HK���hu_u̴Jsi���7
fmt.Printf("%s\n", encryptedMessage)
// zmNOOe@{��}9������#-��^� ����Jh��l0����c2S���a>�AA/�z�2��*c�@^w�P�Xu�HK���hu_u̴Jsi���7
// �w�n��Z,2��w��a���W*G3��'ZR�
// *�4�␤�� ��Q�M
// Ԓ┴L�H[)�@␤�≥���������>�⎽��VW�≥#��/P%�C���F
// ���@��F·┴�C�%�OҎ�≤B���◆8�@�┴EL��� ��������J─
}

Onleesbaar dus, de zin wordt iets als:

zmNOOe@{��}9������#-��^� ����Jh��l0����c2S���a>�AA/�z�2��*c�@^w�P�Xu�HK���hu_u̴Jsi���7 [etc]

Sterker nog, iedere keer als je de zin versleuteld zal de uitkomst anders zijn. De test TestEncryptionNeverTheSame in de  testfile laat dit zien.

Niemand kan het lezen, behalve als je Ingrid bent, want dan ben je in het bezit van de privésleutel die deze tekst kan ontsleutelen.

// Decrypt a message using your private key.
// A received message should be encrypted using the receivers public key.
func (r *RsaIdentity) Decrypt(msg []byte) ([]byte, error) {
label := []byte("")
hash := sha256.New()
return rsa.DecryptOAEP(hash, rand.Reader, r.private, msg, label)
}
view raw rsa_decrypt.go hosted with ❤ by GitHub

Die uitkeringstrekkers pikken al onze banen in

In de repository van Github zitten een paar tests. Die draai je met go test -v. De method TestEncryptDecrypt geeft een voorbeeld van een ontsleuteling van de boodschap met Decrypt.

func TestEncryptDecrypt(t *testing.T) {
henk, _ := NewRsaIdentity()
ingrid, _ := NewRsaIdentity()
// a message from Henk to Ingrid
msg := []byte("Die uitkeringstrekkers pikken al onze banen in.")
// Lets encrypt it, we want to sent it to Ingrid, thus, we use her public key.
encryptedMessage, err := henk.Encrypt(msg, ingrid.public)
if err != nil {
t.Errorf("Unable to encrypt Henk's message for Ingrid; %s", err)
}
plainTextMessage, err := ingrid.Decrypt(encryptedMessage)
if err != nil {
t.Errorf("Unable to decrypt Henk's message for Ingrid; %s", err)
}
if !bytes.Equal(plainTextMessage[:], msg[:]) {
t.Error("Unable to decrypt Henk's message for Ingrid; byte arrays are not the same")
}
}

Wil je wat meer informatie zien dan kun je in de testfile altijd even wat variabelen dumpen met fmt.Println(var). Om de verschillende Messages te dumpen kun je ze het beste even naar een string casten, door string(var) te gebruiken, of nog beter, de Go idioom fmt.Printf("%s", var).

Samenvattend; als iemand, wie dan ook, een bestand of tekst versleuteld met jouw publieke sleutel, ben jij de enige die het kan lezen, door het te ontcijferen met je privésleutel.

Het versleutelen van een boodschap is de voornaamste functionaliteit van encryptie zoals RSA. Je kan er echter nog meer mee, zoals de afzender bewijzen. Daarvoor hoeft je eigen tekst niet eens encrypted te zijn. Dit doe je met de methoden Sign en Verify, waarbij je een boodschap “ondertekend” met je privésleutel waarna iedereen kan verifieren dat het echt jouw boodschap is door de Verify method.

Het ondertekenen van Henk’s publieke boodschap msg, “wetenschap is ook maar een mening”, gebeurd met de private key van de boodschapper. De []byte return value is de signature die met de boodschap meegezonden moet worden.

Wetenschap is ook maar een mening

// Sign returns a signature made by combining the message and the signers private key
// With the r.Verify function, the signature can be checked.
func (r *RsaIdentity) Sign(msg []byte) ([]byte, error) {
return rsa.SignPKCS1v15(rand.Reader, r.private, crypto.SHA256, r.getHashSum(msg))
}
view raw rsa_sign.go hosted with ❤ by GitHub

Hierna kan iedereen verifieren dat de boodschap inderdaad van Henk is, door de Verify method met Henk’s publieke sleutel aan te roepen:

// Verify checks if a message is signed by a given Public Key
func (r *RsaIdentity) Verify(msg []byte, sig []byte, pk *rsa.PublicKey) error {
h := sha256.New()
h.Write(msg)
d := h.Sum(nil)
return rsa.VerifyPKCS1v15(pk, crypto.SHA256, d, sig)
}
view raw rsa_verify.go hosted with ❤ by GitHub

Zodra er iets veranderd aan de boodschap msg, de signature sig of de publieke sleutel pk zal Verify een error terug geven.

Wilders doet tenminste iets tegen de politiek

Dit kunnen we allemaal afvangen met een unittest:

func TestSignVerify(t *testing.T) {
henk, _ := NewRsaIdentity()
// A public message from Henk.
// note that the message is a byte array, not just a string.
msg := []byte("Wilders doet tenminste iets tegen de politiek.")
// Henk signs the message with his private key. This will show the recipient
// proof that this message is indeed from Henk
sig, err := henk.Sign(msg)
// now, if the message msg is public, anyone can read it.
// the signature sig however, proves this message is from Henk.
ingrid, _ := NewRsaIdentity()
hans, _ := NewRsaIdentity()
err = ingrid.Verify(msg, sig, henk.public)
if err != nil {
t.Errorf("Unable to verify Henk's signature; %s", err)
}
err = hans.Verify(msg, sig, henk.public)
if err != nil {
t.Errorf("Unable to verify Henk's signature; %s", err)
}
// Let's see if we can break the signature verification:
// (1) changing the message
err = hans.Verify([]byte("Wilders is een opruier"), sig, henk.public)
if err == nil {
t.Error("Expected an error as we changed the message")
}
// (2) changing the signature
err = hans.Verify(msg, []byte("I am not the signature"), henk.public)
if err == nil {
t.Error("Expected an error as we changed the signature")
}
// (3) changing the public key
err = hans.Verify(msg, sig, ingrid.public)
if err == nil {
t.Error("Expected an error as we changed the public key")
}
}

Nog ééntje van Ingrid, om het af te sluiten:

Misschien klopt het niet allemaal, maar het is wel waar.

Zie je iets aan de code dat je zelf anders zou doen? Schiet een PR in bij de repository.

rsa

Bronnen: