Het is algemeen bekend dat een blockchain een vorm van gedistribueerd grootboek is met transacties. Deze transacties worden verzameld in een blok en gekoppeld aan het vorige blok door middel van hashes zodat een blok, eenmaal toegevoegd, niet langer kan worden gewijzigd. Nou, in theorie kan een blok veranderen, maar gezien de benodigde rekenkracht voor het herberekenen van een blok, is het extreem moeilijk. U zou hiervoor 51% rekenkracht van alle nodes in een blockchain nodig hebben (https://learncryptography.com/cryptocurrency/51-attack).

Dit proces voor het berekenen van een hash voor een blok is een belangrijk onderdeel van de vertrouwde blockchain. Wat gebeurt er echter bij het versturen van transacties op fondsen die je niet daadwerkelijk bezit? Hoe komt het dat je niet zomaar een transactie kan publiceren die zegt “stuur 1000 van deze cryptocurrency aan iemand anders”.

Om te begrijpen wat hier voor nodig is, moeten we kijken naar een tweede deel van blockchain-technologie: public/private-sleutelparen en het gebruik ervan voor digitale handtekeningen.

We zullen C # code en .NET Core gebruiken om dit concept uit te leggen.

Publiek / privaat sleutelpaar

Asymmetrische cryptografie is een techniek die sets van sleutels gebruikt. Een set bestaat uit:

Een openbare sleutel, zichtbaar voor iedereen.
Een privésleutel, alleen bekend bij de eigenaar.

De private sleutel is in wezen een willekeurig gegenereerd nummer. De openbare sleutel kan worden afgeleid met behulp van zogeheten Elliptic Curve Cryptography. We kunnen ECC gebruiken voor encryptie, digitale handtekeningen, pseudo-willekeurige generatoren en andere taken. Bitcoin gebruikt een specifieke elliptische curve genaamd secp256k1. De letters “sec” staan voor Standards for Efficient Cryptography. De p staat voor het feit dat de standaard een set van 6 parameters vastlegt. Het getal 256 betreft het aantal bits. De k verwijst naar het gebruik van een koblitzkromme. Er wordt gebruik gemaakt van de elliptische kromme y2 = x3 + 7 over het eindige lichaam Fn met n = 2256 − 232 − 29 − 28 − 27 − 26 − 24 − 1 (bron: https://nl.wikipedia.org/wiki/Bitcoin).

Maak je geen zorgen we gaan hier geen wetenschappelijke verhandeling over ECC-algoritmen publiceren. Als je meer wilt weten, bekijk dan dit artikel: https://eng.paxos.com/blockchain-101-elliptic-curve-cryptography.

In eenvoudige code krijg je op de volgende manier een public key uit een willekeurige private key.

string privateKey = "123456789";

string publicKey = GetPublicKeyFromPrivateKey(privateKey);

Console.WriteLine(publicKey);

De methode GetPublicKeyFromPrivateKey ziet er zo uit:

private static string GetPublicKeyFromPrivateKey(string privateKey)

{

var p = BigInteger.Parse("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", NumberStyles.HexNumber);

var b = (BigInteger)7;

var a = BigInteger.Zero;

var Gx = BigInteger.Parse("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", NumberStyles.HexNumber);

var Gy = BigInteger.Parse("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", NumberStyles.HexNumber);

CurveFp curve256 = new CurveFp(p, a, b);

Point generator256 = new Point(curve256, Gx, Gy);

var secret = BigInteger.Parse(privateKey, NumberStyles.HexNumber);

var pubkeyPoint = generator256 * secret;

return pubkeyPoint.X.ToString("X") + pubkeyPoint.Y.ToString("X");

}

De klassen CurveFp en Point kan je vinden in de Github-repository voor dit artikel: https://github.com/sander-/working-with-digital-signatures

Als je de code uitvoert krijg je het volgende.

5004D7D9C2A3B2D675ADA618D9CEDA55D1F6A9FDF263E24DAA8CBEA586AF2B2BF1A40DB729D5E9E1B9CE60D4CE8F8F62A12C72E341EB3015B9D349805CD876CCB

Het is natuurlijk niet bepaald veilig om 123456789 als privésleutel te hebben. Maar als je de openbare sleutel hebt, is er geen manier om de waarde van de privésleutel af te leiden.

Digitale handtekeningen

Het ondertekenen van een bericht betekent enkel dat je een hash genereert die gebaseerd is op de inhoud van het bericht en je eigen private key. Zoals je weet wordt een hash slechts één kant op gemaakt. Het is dus niet mogelijk om de private key te herleiden aan de hand van de hash. Het is echter wel mogelijk om te verifiëren dat de hash klopt als u de openbare sleutel van het ondertekenaar hebt. Het schema voor digitale handtekeningen bestaat meestal uit 3 algoritmen:

  1. Een sleutelgeneratie-algoritme dat een private key selecteert uit een willekeurige set van mogelijke private keys. Het algoritme levert de private key en een bijbehorende public key
  2. Een ondertekeningsalgoritme dat aan de hand van een bericht en een private key, een handtekening produceert.
  3. Een handtekening verificatiealgoritme dat, gegeven het bericht, de public key en de handtekening, de authenticiteit van het bericht accepteert of verwerpt.

In een blockchain is het ondertekeningsalgoritme het Elliptic Curve Digital Signature Algorithm of ECDSA (https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm). We duiken niet in de wiskunde van dit algoritme. We gaan echter functies van het BouncyCastle-framework (https://www.bouncycastle.org/) gebruiken om met ECDSA te werken.

De stappen voor het maken van een handtekening voor een bericht zijn als volgt.

  1. Schrijf het bericht dat moet worden ondertekend.
  2. Maak een public/private sleutelpaar; om de publieke sleutel te genereren van de private sleutel gebruiken we het secp256k1-algoritme dat Bitcoin ook gebruikt.
  3. Genereer de handtekening voor het bericht.

Strikt genomen heb je alleen een privésleutel nodig om een bericht te ondertekenen. Het ondertekenen van het bericht zonder iemand de public key te geven om de handtekening te verifiëren is natuurlijk vrij zinloos.

De code om dit te realiseren ziet er zo uit.

 

// Step 1

var message = "Hello World!";

// Step 2

var privateKey = "68040878110175628235481263019639686";

var publicKey = GetPublicKeyFromPrivateKeyEx(privateKey);

// Step 3

var signature = GetSignature(privateKey, message);

Laten we eens kijken naar de methode om een public key te genereren.

public static string GetPublicKeyFromPrivateKeyEx(string privateKey)

{

var curve = SecNamedCurves.GetByName("secp256k1");

var domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);

var d = new BC.Math.BigInteger(privateKey);

var q = domain.G.Multiply(d);

var publicKey = new BC.Crypto.Parameters.ECPublicKeyParameters(q, domain);

return Base58Encoding.Encode(publicKey.Q.GetEncoded());

}

Voor de leesbaarheid is de retourwaarde van de methode gecodeerd met base58 (https://en.wikipedia.org/wiki/Base58).

Als we deze methode aanroepen krijgen we onze publieke sleutel, gebaseerd op de privésleutel die we als invoer hebben gegeven. Ons publiek/privé sleutelpaar bevat dus deze waarden respectievelijk:

Public key: Nr6MbFUfMovKCX4vd5YpQnRYsN4rq6pNPEEBKmicAEwwuYLpJrt5LsRvfvR2G8pJ5rMchEMWDYJ7rdYGY7PjxHEa

Private key: 68040878110175628235481263019639686

De GetSignature methode is deze.

public static string GetSignature(string privateKey, string message)

{

var curve = SecNamedCurves.GetByName("secp256k1");

var domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);

var keyParameters = new

BC.Crypto.Parameters.ECPrivateKeyParameters(new BC.Math.BigInteger(privateKey),

domain);

ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA");

signer.Init(true, keyParameters);

signer.BlockUpdate(Encoding.ASCII.GetBytes(message), 0, message.Length);

var signature = signer.GenerateSignature();

return Base58Encoding.Encode(signature);

}

De uitkomst van deze methode coderen we opnieuw naar base58 om iets leesbaarde te maken.

AN1rKpbUR7H24S46uQBuf4tY6saBQgV4tY6dk6K9LdkW7sQ4qvyJwXkRTvxJVKY9vCPaTaEzBVc2T5RQeugiwTAsXXagHC2eZ

We kunnen dit bericht doorgeven aan iemand anders. De andere partij heeft uiteraard een privésleutel om die handtekening opnieuw te maken. Maar we kunnen ook de publieke sleutel publiceren en de andere partij toestaan om twee dingen te doen.

  • De andere partij kan controleren of het bericht is ondertekend door de houder van de private key die toebehoort aan of matcht met de public key.
  • De andere partij kan ook verifiëren dat het bericht niet is gewijzigd door iemand anders dan degene met de private key. D.w.z. dat je kunt controleren of het bericht is aangepast door iemand die deze private key niet heeft.

Een wijziging van het bericht wordt onmiddellijk herkend. Alleen de oorspronkelijke maker van het bericht met zijn privésleutel kan ervoor zorgen dat de handtekening geldig is.

Om de handtekening te verifiëren gebruiken we deze code.

public static bool VerifySignature(string message, string publicKey, string signature)

{

var curve = SecNamedCurves.GetByName("secp256k1");

var domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);

var publicKeyBytes = Base58Encoding.Decode(publicKey);

var q = curve.Curve.DecodePoint(publicKeyBytes);

var keyParameters = new

BC.Crypto.Parameters.ECPublicKeyParameters(q,

domain);

ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA");

signer.Init(false, keyParameters);

signer.BlockUpdate(Encoding.ASCII.GetBytes(message), 0, message.Length);

var signatureBytes = Base58Encoding.Decode(signature);

return signer.VerifySignature(signatureBytes);

}

Zoals je kunt zien geven we nergens de privésleutel op. Het BouncyCastle-framework doet het zwaarste, of eigenlijk al het, werk door een signer-object te leveren dat gebruik maakt van ECDSA.

Transacties in de blockchain

Het feit dat we berichten van een bekende bron kunnen ontvangen en dat deze berichten niet door een derde partij kunnen worden gewijzigd is essentieel voor transacties in een blockchain. In plaats van een ongestructureerd bericht hebben transacties een duidelijke structuur. Deze structuur ziet er ongeveer zo uit:

class Transaction

{

public string FromPublicKey { get; internal set; }

public string ToPublicKey { get; internal set; }

public int Amount { get; internal set; }

public string Signature { get; internal set; }

public override string ToString()

{

return $"{this.Amount}:{this.FromPublicKey}:{this.ToPublicKey}";

}

}

De From- en To-eigenschappen van de transactie zijn geen eenvoudige adressen. Het zijn openbare sleutels die de verzender en de inhoud van de transactie helpen verifiëren. De transactie en de handtekening kunnen op deze manier worden gemaakt.

var privateKey = "68040878110175628235481263019639686";

var publicKey = GetPublicKeyFromPrivateKeyEx(privateKey);

var transaction = new Transaction();

transaction.FromPublicKey = publicKey;

transaction.ToPublicKey = "QrSNX7KxzGnQqauPiXKxP58nhukU252RKAmSqg17L8h7BpU984g4mxHck6cLzhArADz2p1xo3BwAsbiaLhQaziyu";

transaction.Amount = 10;

transaction.Signature = GetSignature(privateKey, transaction.ToString());

Door deze code te volgen, kunnen we zien dat het bericht is ondertekend door de eigenaar van de private key die hoort bij de public key. Door de handtekening te verifiëren, kunnen we bewijzen dat:

  • De maker van de transactie is de houder van de privésleutel van de afzender/maker van de transactie.    
  • De ontvanger is de oorspronkelijk bedoelde ontvanger.    
  • Het bedrag is niet gewijzigd.

Het wijzigen van één van de parameters (FromPublicKey, ToPublicKey of Amount) zou de handtekening ongeldig maken en daarom de hele transactie ongeldig maken. Het verifiëren van de transactie is eenvoudig.

bool isValidTransaction = VerifySignature(transaction.ToString(), publicKey, transaction.Signature);

Samengevat

Ondertekenen is een goede manier om te weten dat een persoon iets gedaan heeft. Dit betekent dat we erop kunnen vertrouwen dat iemand feitelijk doet wat hij of zij zegt te doen. In de echte wereld kunnen handtekeningen worden vervalst. Bij digitale handtekeningen lukt dat niet. Digitale handtekeningen werken als elektronische vingerafdrukken. In de vorm van een gecodeerd bericht koppelt de digitale handtekening aan een bericht in een geregistreerde transactie. Als u iets wilt weten of persoon A wat stuurt, laat het dan ondertekenen voordat u verdergaat. Als er een geschil is, controleer dan de handtekening. Dit is een essentieel onderdeel van de blockchain.

De broncode voor deze post is te vinden op: https://github.com/sander-/working-with-digital-signatures.