Der public Key wird zunächst mit SHA-256 gehasht. Der Output dieser kryptografischen Hashfunktion wird dann mit RIPEMD-160 gehasht, einer anderen kryptografischen Hashfunktion, die als Output eine 160 Bit (20 Byte) lange Zahl erzeugt. Wir nennen diesen letzten Hash den public Key Hash (PKH). Wie hier im BPMN Flow dargestellt: …
Aber warum verwendtet man bei Bitcoin SHA-256 und dann RIPEMD-160?
RIPEMD160 als letzten kryptografischen Hash zu benutzen ist wahrscheinlich gewählt wurden, damit die PKHs möglichst kurz werden. Es ist ein gut balancierter Tradeoff zwischen Sicherheit und Grösse.
Es könnte sich auch, wenn eine der Hashfunktionen sich als nicht Pre-Image resistent herausstellen würde, die andere es immer noch sein. Wenn man also einen Input für RIPEMD-160 berechnen könnte, der einen bestimmten PKH Output ergibt, dann müsste man immer noch eine Pre-Image Attacke gegen SHA256 ausführen, um den public Key zu finden. Ebenso müsste man, wenn man einen Input für SHA-256 finden könnte, der einen bestimmten Output erzeugt, immer noch erst eine Pre-Image Attacke auf RIPEMD-160 ausführen, bevor man das Ergebnis zur Berechnung des public Keys hernehmen kann.
Es gibt ausserdem noch zu bedenken, dass unterschiedliche Organisationen die beiden kryptografischen Hashfunktionen entwickelt haben. RIPEMD-160 wurde an einer Europäischen Universität in offener Kollaboration mit einer grossen Gemeinde von Kryptografen entwickelt. SHA-256 wurde von der US National Security Agency (NSA) entwickelt.
Hier mal eine kleine Implementierung in Java, als kleines kommentiertes Beispiel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
package de.wenzlaff.twhash; import io.nayuki.bitcoin.crypto.Ripemd160; import io.nayuki.bitcoin.crypto.Sha256; import io.nayuki.bitcoin.crypto.Sha256Hash; /** * Bitcoin - Hashen des public Key zum PKH (public Key Hash) * * Vorteil: Kürzer (von 33 auf 20 Byte, gleich 13 Byte gespart) und * verschleierter den public Key. * * @author Thomas Wenzlaff */ public final class PublicKeyHash { /** * Public Key zu PKH * * @param args optional der public Key (33 Byte) oder wenn keiner übergeben wird * ein Beispiel Key verwendet */ public static void main(String[] args) { System.out.println("Public Key (von Bitcoin 33 Byte) zum PKH (public Key Hash 20 Byte) transformieren:\n"); String publicKey = null; if (args != null && args.length == 1) { publicKey = args[0]; } else { // public Key 33 Byte oder 66 Hex Zeichen z.B. // 0326d92f3db1cfc3b158f5c97425dba8961e95f30c40d1c0e69efc5ac89d8d6ba2 System.out.println("Verwende Beispiel public Key, da keiner übergeben wurde"); publicKey = "0326d92f3db1cfc3b158f5c97425dba8961e95f30c40d1c0e69efc5ac89d8d6ba2"; } System.out.println("Public Key (33 Byte): " + publicKey); // 1. SHA 256 Sha256Hash sha256Hash = Sha256.getHash(publicKey.getBytes()); // 32 Byte oder 64 Hex Zeichen z.B. // af5c9a40eaacdbbb6ca346143ddaba7e22a099e0fd567f3318945ebc6297917c System.out.println("1. SHA-256 Hash erzeugen"); System.out.println("SHA-256 Hash (32 Byte): " + sha256Hash); // 2. RIPEMD-160 byte[] pkh = Ripemd160.getHash(sha256Hash.toBytes()); System.out.println("2. RIPEMD-160 Hash erzeugen"); // 20 Byte oder 160 Bit PKH (Public Key Hash) oder 40 Hex Zeichen z.B. // 35c2fccec44bb363c62442c2a7f1fc9600893c30 System.out.println("Public Key Hash (PKH, 20 Byte): " + Transform.bytesToHex(pkh)); } } |
Hier der Output:
1 2 3 4 5 6 7 |
Public Key (von Bitcoin 33 Byte) zum PKH (public Key Hash 20 Byte) transformieren: Public Key (33 Byte): 02cbfb29329885196dd6c0d815e7279d019a9034da7423aab211bb58e1eb69ffc8 1. SHA-256 Hash erzeugen SHA-256 Hash (32 Byte): 2eea7e9a811cdf4cc09da63aa24a5e27972a9792cc3d28bf724d99d2f3a099a4 2. RIPEMD-160 Hash erzeugen Public Key Hash (PKH, 20 Byte): 982a7da6c35c59f1f1e8ee64d05ecab26fdfcdcb |
Man kann also gut sehen, das zumindestens 13 Byte eingespart wurden. Der ganze Code liegt auch in meinem Bitbucket-Repo.