├── CoinAddressGen.png ├── LICENSE.txt ├── README.md ├── import Coin.png ├── lib ├── bridj-0.7.0.jar ├── java-json.jar ├── webcam-capture-smal-0.3.12.jar └── zxing.jar ├── release ├── Bitcoin_Address_GeneratorV2_4.jar ├── Bitcoin_Address_Generator_V2_6.jar ├── Bitcoin_Address_Generator_V2_7.jar ├── CoinAddressGeneratorV3.1.0.jar ├── CoinAddressGeneratorV3.1.1.jar └── CoinAddressGeneratorV3.4.3.jar └── src ├── BTClib3001 ├── AES256.java ├── Bech32Address.java ├── BitcoinAddr.java ├── ByteArrayList.java ├── Calc.java ├── CoinParameter.java ├── Convert.java ├── HMAC.java ├── PkScript.java ├── PrvKey.java ├── Ripemd160.java ├── SHA256.java ├── ScryptHash.java ├── SigScript.java ├── Transaktion.java ├── Twofish.java ├── TxPrinter.java ├── TxSigniererLegancy.java ├── TxSigniererWitness.java └── Witness.java ├── CoinGen ├── Action.java ├── Config.java ├── Print.java ├── QRCodeZXING.java ├── QrCapture.java └── Web.java ├── ECDSA ├── EXPList.java ├── Math_Modulo.java └── Secp256k1.java ├── GUI ├── GUI.java ├── GUI_CoinList.java └── GUI_Wallet.java ├── Wallet ├── Crypt.java ├── CryptDialog.java └── Wallet.java └── seedExtractor └── SeedExtractor.java /CoinAddressGen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrMaxweII/Bitcoin-Address-Generator/abb7250a9349b48f28e5daa9fcfa3ad5e2a2086c/CoinAddressGen.png -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | Disclaimer 4 | ---------- 5 | 6 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 7 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 8 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 9 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 10 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 11 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 12 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 13 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 14 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 15 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 16 | POSSIBILITY OF SUCH DAMAGE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coin-Address-Generator 2 | 3 | https://github.com/MrMaxweII/Bitcoin-Address-Generator/releases/download/V3.4.4/CoinAddressGenV3.4.4.jar 4 | 5 | Hash SHA-256 "CoinAddressGeneratorV3.4.4.jar" = 68a3f11c178875ce899ef1ed3e708d003f31b96787681b0d75f5b346cdf68dbc 6 | 7 | 8 | 9 | ![CoinAddressGenV3](https://raw.githubusercontent.com/MrMaxweII/Bitcoin-Address-Generator/refs/heads/master/CoinAddressGen.png) 10 | 11 | 12 | 13 | The Coin-AddressGenerator creates a private key in Bitcoin Wallet Import Format WIF, 14 | as well as the associated coin address and public key. 15 | A paper wallet with QR code can be created and printed out. 16 | 17 | 18 | #### This is a Java application, so you need to install Java! 19 | https://java.com/de/download/ 20 | 21 | 22 | #### Start the program 23 | The "Coin_Address_Generator.jar" file is located in the "release" folder. 24 | You can start this under Windows if Java is installed simply by double-clicking. 25 | On Linux, type in the console: java -jar CoinAddressGeneratorV3.1.0.jar 26 | 27 | 28 | 29 | #### Compile code 30 | The project was created with eclipse. 31 | You can either import it back into eclipse or use the java source files. 32 | All necessary source files are in the src folder. 33 | To import to eclipse go to File / Open Projects from File System, select the ZIP archive, finish! 34 | All required libraries are already included in the project (in the lib folder). So you can start the project directly. 35 | 36 | 37 | 38 | #### Input: 39 | There are three ways to create the private key: 40 | 41 | 1. Entry as text: 42 | Any text can be entered. 43 | This text then becomes the private key with a hash, 44 | Public key and the Bitcoin address generated. 45 | 46 | 2. cube sign: 47 | There can be 100 dice characters in Base6 (also characters between 1 and 6). 48 | This includes: 1=1, 2=2, 3=3, 4=4, 5=5, 6=0. 49 | 50 | 3. The private key can also be entered directly in all common formats: 51 | Hexa, Base58, Base58 compressed and Base64. 52 | Checksum check is implemented. 53 | 54 | 55 | 56 | #### output 57 | - The format of the generated keys and addresses can be set under "Settings" 58 | - the public key is given in hexa 59 | - The coin address can be output in WIF-uncompressed, WIF-compressed, P2SH and Bech32 60 | - The QR code of the private key and the coin address is displayed 61 | 62 | 63 | 64 | #### Issue of the coin amount 65 | - If the internet connection is active, the coin amount belonging to the key is displayed 66 | - The amount is queried on a suitable website 67 | - If no internet connection is available, nothing is displayed. 68 | 69 | 70 | 71 | #### QR code check 72 | For safety, the program reads the QR code back in as an image, 73 | scanned and checked. 74 | This prevents an incorrect QR code from being displayed. 75 | 76 | 77 | 78 | #### Save and open the wallet 79 | 80 | An encrypted wallet with any number of keys can be saved. 81 | A strong password must be entered for this. 82 | To increase security, encryption is carried out in succession using AES and Twofish. 83 | In addition, the encryption contains a certain brute force protection, which extends the runtime with the help of a scrypt hash. 84 | 85 | 86 | 87 | #### Create a paper wallet. 88 | The surface of the program can be printed out or saved as an image. 89 | 90 | 91 | 92 | #### Contact 93 | If you find bugs, ideas for improvements, or just have questions, 94 | I am happy about every mail: Maxwell-KSP@gmx.de 95 | 96 | 97 | #### donate 98 | If you like the Coin Address Generator, I would be very happy to receive a donation: 99 | #### 12zeCvN7zbAi3JDQhC8tU3DBm35kDEUNiB 100 | 101 | 102 | 103 | 104 | #### Disclaimer 105 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 106 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 107 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 108 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 109 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 110 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 111 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 112 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 113 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 114 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 115 | POSSIBILITY OF SUCH DAMAGE. 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /import Coin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrMaxweII/Bitcoin-Address-Generator/abb7250a9349b48f28e5daa9fcfa3ad5e2a2086c/import Coin.png -------------------------------------------------------------------------------- /lib/bridj-0.7.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrMaxweII/Bitcoin-Address-Generator/abb7250a9349b48f28e5daa9fcfa3ad5e2a2086c/lib/bridj-0.7.0.jar -------------------------------------------------------------------------------- /lib/java-json.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrMaxweII/Bitcoin-Address-Generator/abb7250a9349b48f28e5daa9fcfa3ad5e2a2086c/lib/java-json.jar -------------------------------------------------------------------------------- /lib/webcam-capture-smal-0.3.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrMaxweII/Bitcoin-Address-Generator/abb7250a9349b48f28e5daa9fcfa3ad5e2a2086c/lib/webcam-capture-smal-0.3.12.jar -------------------------------------------------------------------------------- /lib/zxing.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrMaxweII/Bitcoin-Address-Generator/abb7250a9349b48f28e5daa9fcfa3ad5e2a2086c/lib/zxing.jar -------------------------------------------------------------------------------- /release/Bitcoin_Address_GeneratorV2_4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrMaxweII/Bitcoin-Address-Generator/abb7250a9349b48f28e5daa9fcfa3ad5e2a2086c/release/Bitcoin_Address_GeneratorV2_4.jar -------------------------------------------------------------------------------- /release/Bitcoin_Address_Generator_V2_6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrMaxweII/Bitcoin-Address-Generator/abb7250a9349b48f28e5daa9fcfa3ad5e2a2086c/release/Bitcoin_Address_Generator_V2_6.jar -------------------------------------------------------------------------------- /release/Bitcoin_Address_Generator_V2_7.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrMaxweII/Bitcoin-Address-Generator/abb7250a9349b48f28e5daa9fcfa3ad5e2a2086c/release/Bitcoin_Address_Generator_V2_7.jar -------------------------------------------------------------------------------- /release/CoinAddressGeneratorV3.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrMaxweII/Bitcoin-Address-Generator/abb7250a9349b48f28e5daa9fcfa3ad5e2a2086c/release/CoinAddressGeneratorV3.1.0.jar -------------------------------------------------------------------------------- /release/CoinAddressGeneratorV3.1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrMaxweII/Bitcoin-Address-Generator/abb7250a9349b48f28e5daa9fcfa3ad5e2a2086c/release/CoinAddressGeneratorV3.1.1.jar -------------------------------------------------------------------------------- /release/CoinAddressGeneratorV3.4.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrMaxweII/Bitcoin-Address-Generator/abb7250a9349b48f28e5daa9fcfa3ad5e2a2086c/release/CoinAddressGeneratorV3.4.3.jar -------------------------------------------------------------------------------- /src/BTClib3001/Bech32Address.java: -------------------------------------------------------------------------------- 1 | package BTClib3001; 2 | import java.io.ByteArrayOutputStream; 3 | import java.io.IOException; 4 | import java.util.Objects; 5 | 6 | 7 | 8 | /******************************************************************************************************* 9 | * V1.0 vom 29.02.2020 * 10 | * Gehört zur BTClib3001 * 11 | * Diese statische Klasse Codiert eine Hash160-Adresse zu einer Bech32-Adresse mit "bc" beginnend * 12 | * Achtung: Der Hash160 muss aus einem komprimierten Public-Key stammen! (2 oder 3 am Anfang) * 13 | * Zum Testen: https://slowli.github.io/bech32-buffer/ * 14 | *******************************************************************************************************/ 15 | 16 | 17 | 18 | public class Bech32Address 19 | { 20 | private static final String ALPHABET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; 21 | private static final int[] GENERATOR = {0x3B6A57B2, 0x26508E6D, 0x1EA119FA, 0x3D4233DD, 0x2A1462B3}; 22 | private static final int CHECKSUM_LEN = 6; 23 | 24 | 25 | 26 | /** Codiert eine Hash160-Adresse zu einer Bech32-Adresse mit "bc" beginnend 27 | @param bech32Prefix Das Präfix für die resultierende Zeichenfolge bei BTC = "bc" 28 | @param version Witness Versionsnummer. Bei Bitcoin = 0; 29 | @param hash160 Die rohen Zeugen-Daten, oder die Hash160 Adresse. 30 | @return Gibt die fertige Bech32-Adresse als Text-String zurück. **/ 31 | public static String segwitToBech32(String bech32Prefix, int version, byte[] hash160) 32 | { 33 | Objects.requireNonNull(bech32Prefix); 34 | Objects.requireNonNull(hash160); 35 | if(version < 0 || version > 16) throw new IllegalArgumentException("Invalid witness version"); 36 | if(hash160.length < 2 || hash160.length > 40) throw new IllegalArgumentException("Invalid witness data length"); 37 | ByteArrayOutputStream data = new ByteArrayOutputStream(); 38 | assert(version >>> 5) == 0; 39 | data.write(version); 40 | int inputIndex = 0; 41 | int bitBuffer = 0; 42 | int bitBufferLen = 0; 43 | while(inputIndex < hash160.length || bitBufferLen > 0) 44 | { 45 | assert 0 <= bitBufferLen && bitBufferLen <= 8 + 5 - 1; 46 | assert(bitBuffer << bitBufferLen) == 0; 47 | if(bitBufferLen < 5) 48 | { 49 | if(inputIndex < hash160.length) 50 | { 51 | bitBuffer |= (hash160[inputIndex] & 0xFF) << (32 - 8 - bitBufferLen); 52 | inputIndex++; 53 | bitBufferLen += 8; 54 | } 55 | else bitBufferLen = 5; 56 | } 57 | assert bitBufferLen >= 5; 58 | data.write(bitBuffer >>> (32 - 5)); 59 | bitBuffer <<= 5; 60 | bitBufferLen -= 5; 61 | } 62 | return bitGroupsToBech32(bech32Prefix, data.toByteArray()); 63 | } 64 | 65 | 66 | 67 | 68 | // ------------------------------------------------------------- Private Methoden ---------------------------------- 69 | 70 | 71 | private static String bitGroupsToBech32(String bech32Prefix, byte[] data) 72 | { 73 | Objects.requireNonNull(bech32Prefix); 74 | Objects.requireNonNull(data); 75 | char[] human = bech32Prefix.toCharArray(); 76 | checkHumanReadablePart(human); 77 | for(byte b : data) {if ((b >>> 5) != 0) throw new IllegalArgumentException("Expected 5-bit groups");} 78 | if(human.length + 1 + data.length + 6 > 90) throw new IllegalArgumentException("Output too long"); 79 | int checksum; 80 | try 81 | { 82 | ByteArrayOutputStream temp = expandHumanReadablePart(human); 83 | temp.write(data); 84 | temp.write(new byte[CHECKSUM_LEN]); 85 | checksum = polymod(temp.toByteArray()) ^ 1; 86 | } 87 | catch(IOException e) {throw new AssertionError(e);} 88 | StringBuilder sb = new StringBuilder(bech32Prefix).append('1'); 89 | for(byte b : data) sb.append(ALPHABET.charAt(b)); 90 | for(int i = 0; i < CHECKSUM_LEN; i++) 91 | { 92 | int b = (checksum >>> ((CHECKSUM_LEN - 1 - i) * 5)) & 0x1F; 93 | sb.append(ALPHABET.charAt(b)); 94 | } 95 | return sb.toString(); 96 | } 97 | 98 | 99 | 100 | private static void checkHumanReadablePart(char[] s) 101 | { 102 | int n = s.length; 103 | if(n<1 || n>83) throw new IllegalArgumentException("Invalid length of human-readable part string"); 104 | for(char c : s) 105 | { 106 | if(c<33 || c>126) throw new IllegalArgumentException("Invalid character in human-readable part string"); 107 | if('A'<=c && c<='Z') throw new IllegalArgumentException("Human-readable part string must be lowercase"); 108 | } 109 | } 110 | 111 | 112 | 113 | private static ByteArrayOutputStream expandHumanReadablePart(char[] s) 114 | { 115 | ByteArrayOutputStream result = new ByteArrayOutputStream(); 116 | for(char c : s) result.write(c >>> 5); 117 | result.write(0); 118 | for(char c : s) result.write(c & 0x1F); 119 | return result; 120 | } 121 | 122 | 123 | 124 | private static int polymod(byte[] data) 125 | { 126 | int result = 1; 127 | for(byte b : data) 128 | { 129 | assert 0<=b && b<32; 130 | int x = result >>> 25; 131 | result = ((result & ((1 << 25) - 1)) << 5) | b; 132 | for(int i = 0; i < GENERATOR.length; i++) result ^= ((x >>> i) & 1) * GENERATOR[i]; 133 | assert(result >>> 30) == 0; 134 | } 135 | return result; 136 | } 137 | } -------------------------------------------------------------------------------- /src/BTClib3001/BitcoinAddr.java: -------------------------------------------------------------------------------- 1 | package BTClib3001; 2 | 3 | 4 | 5 | /*************************************************************************************************************** 6 | * Version 1.3 Autor: Mr. Maxwell vom 21.02.2020 * 7 | * Nicht statische Klasse die eine Coin-Adresse erstellt. * 8 | * Diese Klasse ist für mehrere Coins verwendbar. Mit dem Präfix Argument wird der jeweilige Coin angegeben. * 9 | ****************************************************************************************************************/ 10 | 11 | 12 | 13 | 14 | public class BitcoinAddr 15 | { 16 | 17 | private byte[] pref_Pub; // Das Prefix welches den jeweiligen Coin repräsentiert 18 | private byte[] hash160; // Die Coin-Adresse als 20Byte Hash160 19 | 20 | 21 | 22 | // ---------------------------------------------------- Konstruktoren -------------------------------------------- 23 | 24 | /** @param hash160 Dem Konstruktor wird die BitcoinAdresse als Hash160 20 Byte-Array übergeben. 25 | @param pref_pubKey Das Präfix aus den CoinParametern für die Bitcoin Adresse **/ 26 | public BitcoinAddr(byte[] hash160, byte[] pref_pubKey) 27 | { 28 | this.pref_Pub = pref_pubKey; 29 | if(hash160.length != 20) throw new IllegalArgumentException("Error in \"Hash160\": length ist not 20Byte!"); 30 | else this.hash160 = hash160; 31 | } 32 | 33 | 34 | 35 | /** Dem Konstruktor wird die BitcoinAdresse als Base58 String übergeben. 36 | @param addr Base58 String der Bitcoin-Adresse 37 | @param pref_pubKey Das Präfix aus den CoinParametern für die Bitcoin Adresse **/ 38 | public BitcoinAddr(String addr, byte[] pref_pubKey) throws IllegalArgumentException 39 | { 40 | this.pref_Pub = pref_pubKey; 41 | String address = Convert.Base58ToHexString(addr, 50); 42 | if(address.substring(0,2).equals(Convert.byteArrayToHexString(pref_pubKey))) 43 | { 44 | String m = address.substring(0,42); 45 | String h = Calc.getHashSHA256_from_HexString(Calc.getHashSHA256_from_HexString(m)); // 2 x SHA256 46 | h=h.substring(0, 8); 47 | if(h.equals(address.substring(42, 50))) hash160 = Convert.hexStringToByteArray(address.substring(2,42)); 48 | else throw new IllegalArgumentException("Error in \"BitcoinAddr\" : False Address-Hash!"); 49 | return; 50 | } 51 | else throw new IllegalArgumentException("Error in \"BitcoinAddr\" : False Network-Version!"); 52 | } 53 | 54 | 55 | 56 | 57 | // ---------------------------------------------------------- Bitcoin-Adress Methoden -------------------------------- 58 | 59 | /** Gibt den Hash160 der Bitcoin-Adresse als 20Byte-Array zurück. */ 60 | public byte[] getHash160() 61 | { 62 | return hash160; 63 | } 64 | 65 | 66 | 67 | /** @return Gibt die Bitcoin-Adresse als Base58 String im WIF-Format zurück (compressed wird selbst erkannt)**/ 68 | public String getBase58Address() 69 | { 70 | String prefix = Convert.byteArrayToHexString(pref_Pub); 71 | String h160 = Convert.byteArrayToHexString(hash160); 72 | String addr = prefix+h160; 73 | String hash = Calc.getHashSHA256_from_HexString(Calc.getHashSHA256_from_HexString(addr)); 74 | return Convert.hexStringToBase58(addr+hash.substring(0,8)); 75 | } 76 | 77 | 78 | 79 | /** @param prefix_P2SH Es muss das P2SH-Prefix als Zahlen-String (so wie es aus den CoinParametern kommt) übergeben werden. 80 | @return Gibt die Bitcoin-Adresse als Base58 String im P2SH-Witness Format (mit 3 beginnend) zurück. **/ 81 | public String getP2SHAddress(byte[] prefix_P2SH) 82 | { 83 | String prefix = Convert.byteArrayToHexString(prefix_P2SH); 84 | String h160 = Convert.byteArrayToHexString(getRedeemScript()); 85 | String addr = prefix+h160; 86 | String hash = Calc.getHashSHA256_from_HexString(Calc.getHashSHA256_from_HexString(addr)); 87 | return Convert.hexStringToBase58(addr+hash.substring(0,8)); 88 | } 89 | 90 | 91 | 92 | // Erstellt das RedeemScript für eine SegWit P2SH Adresse die mit 3 beginnt. 93 | // Es wird der Hash160 übergeben so wie er üblicherweise berechnet wird. Allerdings muss er von einem komprimierten PubKey stammen! 94 | // Zurück gegeben wird das "RedeemScript" welches dem neuem Hash160 entspricht, der dann so in eine Base58 Adresse codert werden kann. 95 | private byte[] getRedeemScript() 96 | { 97 | final byte[] scriptPrefix = {0x00,0x14}; // Das Script Prefix 98 | byte[] script = new byte[22]; 99 | script[0] = scriptPrefix[0]; 100 | script[1] = scriptPrefix[1]; 101 | System.arraycopy(hash160, 0, script, 2, 20); 102 | return Calc.getHashRIPEMD160(Calc.getHashSHA256(script)); 103 | } 104 | } -------------------------------------------------------------------------------- /src/BTClib3001/ByteArrayList.java: -------------------------------------------------------------------------------- 1 | package BTClib3001; 2 | 3 | 4 | 5 | /*************************************************************************************************************************** 6 | * V1.0 Mr.Maxwell vom 22.01.2021 * 7 | * BTClib3001 Klasse * 8 | * Diese Implementierung der ByteArrayList Klasse ist eine allgemeine Daten-Typ-Klasse * 9 | * die verschiedene wichtige Methode für Byte-Arrays implementiert. * 10 | * - Besitzt keine Abhängigkeiten aus anderen Klassen der BTClib3001 * 11 | ****************************************************************************************************************************/ 12 | 13 | 14 | 15 | 16 | public class ByteArrayList 17 | { 18 | 19 | private byte[] data; // Das Byte-Array als Datentyp dieser Klasse 20 | 21 | 22 | 23 | 24 | // ----------------------------- Konstruktor ----------------------------// 25 | 26 | 27 | /** Dem Konstruktor wird das Byte-Array übergeben. **/ 28 | public ByteArrayList(byte[] data) 29 | { 30 | this.data = data; 31 | } 32 | 33 | 34 | 35 | 36 | // ----------------------------- Public Methoden -----------------------// 37 | 38 | 39 | 40 | 41 | /** @param b Hängt das angegebene Byte hinten an die Liste an. **/ 42 | public void add(byte b) 43 | { 44 | byte[] out = new byte[data.length+1]; 45 | System.arraycopy(data, 0, out, 0, data.length); 46 | out[data.length] = b; 47 | data = out; 48 | } 49 | 50 | 51 | 52 | /** @param b Hängt das angegebene Byte-Array hinten an die Liste an. **/ 53 | public void add(byte[] b) 54 | { 55 | int len = b.length; 56 | byte[] out = new byte[data.length + len]; 57 | System.arraycopy(data, 0, out, 0, data.length); 58 | System.arraycopy(b, 0, out, data.length, len); 59 | data = out; 60 | } 61 | 62 | 63 | 64 | /** Entfernt einen ausgewählten Bereich, zwischen from und to. 65 | Nachfolgende Bytes füllen die Lücke auf, in dem sie nach vorne geschoben werden. 66 | @param from Start-Position ab der Bytes entfernt werden sollen. 67 | @param to End-Position bis an diese Stelle werden Bytes entfernt. **/ 68 | public void remove(int from, int to) 69 | { 70 | int len = to-from; 71 | byte[] out = new byte[data.length - len]; 72 | System.arraycopy(data, 0, out, 0, from); 73 | System.arraycopy(data, to, out, from, data.length -to); 74 | data = out; 75 | } 76 | 77 | 78 | 79 | /** Fügt ein neues Byte-Array in die angegebene Position ein und verschiebt die restlichen Elemente nach hinten. 80 | @param b Das Byte-Array welches eingefügt werden soll. 81 | @param pos Position der Stelle an dem das neue Byte-Array eingefügt werden soll. **/ 82 | public void insert(byte[] b, int pos) 83 | { 84 | int len = b.length; // Länge des einzufügenden Arrays. 85 | byte[] out = new byte[data.length + len]; // neues Array 86 | System.arraycopy(data, 0, out, 0, pos); // Vorderteil wird kopiert 87 | System.arraycopy(b, 0, out, pos, len); // Mittelteil wird kopiert 88 | System.arraycopy(data, pos, out, pos+len, data.length-pos); // End-Teil wird kopiert 89 | data = out; 90 | } 91 | 92 | 93 | 94 | /** Fügt ein einzelnes Byte in die angegebene Position ein und verschiebt die restlichen Elemente nach hinten. 95 | @param b Das Byte welches eingefügt werden soll. 96 | @param pos Position der Stelle an dem das neue Byte eingefügt werden soll. **/ 97 | public void insert(byte b, int pos) 98 | { 99 | byte[] out = new byte[data.length + 1]; // neues Array 100 | System.arraycopy(data, 0, out, 0, pos); // Vorderteil wird kopiert 101 | out[pos] = b; // Das Byte wird kopiert 102 | System.arraycopy(data, pos, out, pos+1, data.length-pos); // End-Teil wird kopiert 103 | data = out; 104 | } 105 | 106 | 107 | 108 | /** @return Gibt das Element an der angegebenen Position in dieser Liste zurück. **/ 109 | public byte get(int index) 110 | { 111 | return data[index]; 112 | } 113 | 114 | 115 | 116 | /** Gibt ein Byte-Array in dem angegebenem Bereich zurück. **/ 117 | public byte[] getArray(int from, int to) 118 | { 119 | byte[] out = new byte[to-from]; 120 | System.arraycopy(data, from, out, 0, to-from); 121 | return out; 122 | } 123 | 124 | 125 | 126 | /** @return gibt das komplette Byte-Array zurück. **/ 127 | public byte[] getArrayAll() 128 | { 129 | return data; 130 | } 131 | 132 | 133 | 134 | /** @return Gibt die Anzahl der Elemente in dieser Liste zurück. **/ 135 | public int size() 136 | { 137 | return data.length; 138 | } 139 | } -------------------------------------------------------------------------------- /src/BTClib3001/Calc.java: -------------------------------------------------------------------------------- 1 | package BTClib3001; 2 | import java.io.UnsupportedEncodingException; 3 | import java.math.BigInteger; 4 | import java.net.ConnectException; 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | import java.util.Base64; 8 | import ECDSA.Secp256k1; 9 | 10 | 11 | 12 | 13 | /******************************************************************************************************************** 14 | * Version 1.8 Autor: Mr. Maxwell vom 03.01.2024 * 15 | * Letzte Änderung: getDoubleHashSHA256() hinzugefügt. (Doppelter SHA256) * 16 | * Hier werden gundlegende Crypt-Berechnungen durchgefürt. * 17 | *********************************************************************************************************************/ 18 | 19 | 20 | 21 | public class Calc 22 | { 23 | 24 | 25 | 26 | // ------------------------------------------------- Secp256k1 ------------------------------------------------------// 27 | 28 | /** Berechnet den PublicKey aus einem Private-Key. 29 | @param str Übergeben wird der Private-Key als Hex-String (32Byte) 30 | @param compressed wenn "true" dann wird der Pub-Key komprimiert (nur X-Koordinate) 31 | @return Gibt den Public Key als Hex-String zurück. 32 | Das Erste Byte ist ein Status-Byte mit den folgenden Informationen. 33 | 02 : komprimierter Pub-Key, enthält nur die X-Koordinate, die Y-Koordinate ist positiv (33Byte) 34 | 03 : komprimierter Pub-Key, enthält nur die X-Koordinate, die Y-Koordinate ist negativ (33Byte) 35 | 04 : unkomprimierter Pub-Key mit X und Y Koordinaten (65Byte) **/ 36 | public static String getPublicKey(String str, boolean compressed) 37 | { 38 | byte[] b = getPublicKey(Convert.hexStringToByteArray(str),compressed); 39 | return Convert.byteArrayToHexString(b); 40 | } 41 | 42 | 43 | 44 | 45 | /** Berechnet den PublicKey aus einem Private-Key. 46 | @param privateKey Übergeben wird der Private-Key als Byte-Array (32Byte) 47 | @param compressed wenn "true" dann wird der Pub-Key komprimiert (nur X-Koordinate) 48 | @return Gibt den Public Key als Byte-Array zurück. 49 | Das Erste Byte ist ein Status-Byte mit den folgenden Informationen. 50 | 02 : komprimierter Pub-Key, enthält nur die X-Koordinate, die Y-Koordinate ist positiv (33Byte) 51 | 03 : komprimierter Pub-Key, enthält nur die X-Koordinate, die Y-Koordinate ist negativ (33Byte) 52 | 04 : unkomprimierter Pub-Key mit X und Y Koordinaten (65Byte) **/ 53 | public static byte[] getPublicKey(byte[] privateKey, boolean compressed) 54 | { 55 | Secp256k1 spec = new Secp256k1(); 56 | BigInteger[] bi = spec.multiply_G(new BigInteger(1,privateKey)); 57 | return Secp256k1.comp(bi, compressed); 58 | } 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | // ------------------------------------------------- Hash SHA256 ------------------------------------------------------// 68 | 69 | /** Berechnet den Hash(SHA256) aus einem Hex-String und gibt ihn als Hex-String zurück. */ 70 | public static String getHashSHA256_from_HexString(String str) 71 | { 72 | byte[] b = getHashSHA256(Convert.hexStringToByteArray(str)); 73 | return Convert.byteArrayToHexString(b); 74 | } 75 | 76 | 77 | /** Berechnet den doppelten Hash(SHA256) aus einem Hex-String und gibt ihn als Hex-String zurück. */ 78 | public static String getDoubleHashSHA256_from_HexString(String str) 79 | { 80 | byte[] b = getDoubleHashSHA256(Convert.hexStringToByteArray(str)); 81 | return Convert.byteArrayToHexString(b); 82 | } 83 | 84 | 85 | /** Berechnet den Hash(SHA256) aus einem normalen Text und gibt ihn als Hex-String zurück. */ 86 | public static String getHashSHA256(String str) 87 | { 88 | try 89 | { 90 | byte[] b = getHashSHA256((str).getBytes("UTF-8")); 91 | return Convert.byteArrayToHexString(b); 92 | } 93 | catch (UnsupportedEncodingException e) { 94 | System.out.println("Error getHashSHA256()"); 95 | System.out.println(e.getMessage()); 96 | return "-1"; 97 | } 98 | 99 | } 100 | 101 | 102 | /** Berechnet SHA-256 aus Byte-Array. Rückgabe ist Byte-Array. **/ 103 | public static byte[] getHashSHA256(byte[] b) 104 | { 105 | MessageDigest sha; 106 | try 107 | { 108 | sha = MessageDigest.getInstance("SHA-256"); 109 | return sha.digest(b); 110 | } 111 | catch (NoSuchAlgorithmException e) 112 | { 113 | System.out.println("Error getHashSHA256()"); 114 | System.out.println(e.getMessage()); 115 | return null; 116 | } 117 | } 118 | 119 | 120 | /** Berechnet den doppelten SHA-256 aus Byte-Array. Rückgabe ist Byte-Array. **/ 121 | public static byte[] getDoubleHashSHA256(byte[] b) 122 | { 123 | return getHashSHA256(getHashSHA256(b)); 124 | } 125 | 126 | 127 | //------------------------------------------------- Hash RIPEMD-160 neu ----------------------------------------------------// 128 | 129 | 130 | public static String getHashRIPEMD160_from_HexString(String str) throws NoSuchAlgorithmException 131 | { 132 | byte[] b = getHashRIPEMD160(Convert.hexStringToByteArray(str)); 133 | return Convert.byteArrayToHexString(b); 134 | } 135 | 136 | 137 | public static byte[] getHashRIPEMD160(byte[] b) 138 | { 139 | return Ripemd160.getHash(b); 140 | } 141 | 142 | 143 | 144 | 145 | 146 | 147 | // ------------------------------------------------- SHA1 ----------------------------------------------------------------// 148 | 149 | /** Gibt den SHA1 Hash in Base64 zurück **/ 150 | public static String getHashSHA1(String in) 151 | { 152 | try 153 | { 154 | MessageDigest crypt = MessageDigest.getInstance("SHA-1"); 155 | crypt.update(in.getBytes("UTF-8")); 156 | return Base64.getEncoder().encodeToString(crypt.digest()); 157 | } 158 | catch (NoSuchAlgorithmException | UnsupportedEncodingException e) 159 | { 160 | System.out.println("Error getHashSHA1()"); 161 | System.out.println(e.getMessage()); 162 | return null; 163 | } 164 | } 165 | 166 | 167 | 168 | 169 | 170 | // ------------------------------------------------------ Compact-Size ---------------------------------------------------// 171 | 172 | /** parst einen Datensatz mit Variabler Länge Compact-Size: https://en.bitcoin.it/wiki/Protocol_documentation 173 | "pos" zeigt auf den Startwert des ersten Compact-Size Bytes. 174 | Rückgabe ist ein Byte-Array mit den Nutzdaten. **/ 175 | public static byte[] parseCompactSize(byte[] data, int pos) throws ConnectException 176 | { 177 | int[] sizeData = decodeCompactSize(data,pos); // Die Werte für "start" und "len" werden hier gesetzt 178 | int start = sizeData[0]; // Position des Data-Byte-Array´s bei der die Nutzdaten beginnen 179 | int len = sizeData[1]; // Länge der Nutzdaten 180 | if(len<0) {throw new ConnectException("Error in Calc.parseCompactSize, max size 2147483647 Byte!"); } 181 | byte[] out = new byte[len]; // Das Ausgabe Array wird erstellt 182 | System.arraycopy(data,start,out,0,len); // Die Nutzdaten werden in das Ausgabe-Array kopiert. 183 | return out; 184 | } 185 | 186 | 187 | 188 | /** Decodiert die Längeninformationen aus einem Compact-Size Format: https://en.bitcoin.it/wiki/Protocol_documentation 189 | Übergeben wird ein Byte-Array welches Compact-Size Daten beinhaltet. 190 | Das Übergebene Array wird hier nicht verändert sondern nur Analysiert. 191 | "pos" zeigt auf den Startwert des Compact-Size Bytes welches Analysiert werden soll. 192 | Das Rückgabe-Array ist 2 Elemente lang: int[0]="start" und int[1]="len" 193 | int[0]="start" ist der Startwert ab welchem Byte die Nutzdaten beginnen. 194 | int[1]="len" ist die Länge der Nutzdaten. **/ 195 | public static int[] decodeCompactSize(byte[] data, int pos) 196 | { 197 | int[] out = new int[2]; 198 | if((data[pos]&0xff) < 253) // Auswahl <0xFD 199 | { 200 | byte[] b = {data[pos]}; // Das erste Feld welches die Länge enthält wird zwischengespeichert 201 | out[1] = Convert.byteArray_to_int(b); // Die Länge wird in Integer konvertiert 202 | out[0] = pos+1; 203 | } 204 | 205 | if(data[pos]==(byte)0xFD) // Auswahl == 0xFD 206 | { 207 | byte[] b = {data[pos+1],data[pos+2]}; // Das zweite Feld welches die Länge enthält wird zwischengespeichert 208 | Convert.swapBytes(b); // Byte-Reihenfolge wird vertauscht 209 | out[1] = Convert.byteArray_to_int(b); // Die Länge wird in Integer konvertiert 210 | out[0] = pos+3; 211 | } 212 | 213 | if(data[pos]==(byte)0xFE) // Auswahl == 0xFE 214 | { 215 | byte[] b = {data[pos+1],data[pos+2],data[pos+3],data[pos+4]}; // Das dritte Feld welches die Länge enthält wird zwischengespeichert 216 | Convert.swapBytes(b); // Byte-Reihenfolge wird vertauscht 217 | out[1] = Convert.byteArray_to_int(b); // Die Länge wird in Integer konvertiert 218 | out[0] = pos+5; 219 | } 220 | return out; 221 | } 222 | 223 | 224 | 225 | /** Codiert CompactSize Daten, in dem die entsprechende Bytes voran gestellt werden. 226 | @param dataIn Nutzdaten die Kodiert werden sollen. 227 | @return Nutzdaten mit den Vorangestellten Bytes **/ 228 | public static byte[] encodeCompactSize(byte[] dataIn) 229 | { 230 | int size = dataIn.length; 231 | if(size < 253) 232 | { 233 | byte[] out = new byte[size+1]; 234 | out[0] = (byte) (size&0xff); 235 | System.arraycopy(dataIn, 0, out, 1, size); 236 | return out; 237 | } 238 | if(size >= 253 && size < 65536 ) 239 | { 240 | byte[] out = new byte[size+3]; 241 | out[0] = (byte) 0xfd; 242 | byte[] s = Convert.swapBytesCopy(Convert.int_To_2_ByteArray(size)); 243 | System.arraycopy(s, 0, out, 1, 2); 244 | System.arraycopy(dataIn, 0, out, 3, size); 245 | return out; 246 | } 247 | if(size >= 65536) 248 | { 249 | byte[] out = new byte[size+5]; 250 | out[0] = (byte) 0xfe; 251 | byte[] s = Convert.swapBytesCopy(Convert.int_To_4_ByteArray(size)); 252 | System.arraycopy(s, 0, out, 1, 4); 253 | System.arraycopy(dataIn, 0, out, 5, size); 254 | return out; 255 | } 256 | return null; 257 | } 258 | } -------------------------------------------------------------------------------- /src/BTClib3001/Convert.java: -------------------------------------------------------------------------------- 1 | package BTClib3001; 2 | import java.math.BigInteger; 3 | import java.nio.ByteBuffer; 4 | import java.text.DecimalFormat; 5 | import java.util.Arrays; 6 | 7 | 8 | 9 | /************************************************************************************************************ 10 | * Version 1.11 Autor: Mr. Maxwell vom 02.02.2024 * 11 | * letzte Änderung: valueToHex(); Fehler behoben und getestet. * 12 | * Hier werden verschiedene Konvertierungen vorgenommen. * 13 | ************************************************************************************************************/ 14 | 15 | 16 | 17 | public class Convert 18 | { 19 | 20 | 21 | 22 | /** Decodiert bekannte Magic-Werte von 4byte-Array nach String. 23 | @param MAGIC 4Byte MAGIC Wert 24 | @return String, der das Netzwerk des Magic-Wertes beschreibt. 25 | Unbekannte MAGIC-Werte werden mit "Unknown network" zurückgegeben. **/ 26 | public static String decodeMAGIC(byte[] MAGIC) 27 | { 28 | if(MAGIC==null) return "No MAGIC value was entered"; 29 | if(MAGIC.length!=4) return "No 4 bytes were passed"; 30 | final byte[] MAINNET = {(byte) 0xf9,(byte) 0xbe,(byte) 0xb4,(byte) 0xD9}; 31 | final byte[] TESTNET = {(byte) 0xfa,(byte) 0xbf,(byte) 0xb5,(byte) 0xda}; 32 | final byte[] TESTNET3 = {(byte) 0x0b,(byte) 0x11,(byte) 0x09,(byte) 0x07}; 33 | if(Arrays.equals(MAGIC,MAINNET)) return "Bitcoin-MainNet"; 34 | if(Arrays.equals(MAGIC,TESTNET)) return "Bitcoin-TestNet"; 35 | if(Arrays.equals(MAGIC,TESTNET3))return "Bitcoin-TestNet3"; 36 | return "Unknown network"; 37 | } 38 | 39 | 40 | 41 | /** Wandelt ein Byte in Boolean um. 42 | @param b Wenn das Byte nicht 0x00 ist wird true zurück gegeben. **/ 43 | public static boolean byteToBool(byte b) 44 | { 45 | return (b != 0x00) ; 46 | } 47 | 48 | 49 | 50 | /** Wandelt ein Boolean in Byte um. 51 | @param bool Wenn bool = false wird, 0x00 zurück gegeben. 52 | Wenn bool = true, wird 0xff zurück gegeben. **/ 53 | public static byte boolToByte(boolean bool) 54 | { 55 | if(bool) return (byte)0xff; 56 | else return 0x00; 57 | } 58 | 59 | 60 | 61 | /** Hex-String wird in ein Byte Array konvertiert. 62 | @param hex Es dürfen nur Zeichen zwichen [0-f] oder [0-F] mit einer geraden Anzahl übergeben werden. 63 | @return Byte-Array welches dem Hex-String entspricht. 64 | @exception IllegalArgumentException bei ungerader Zeichenfolge!**/ 65 | public static byte[] hexStringToByteArray(String hex) 66 | { 67 | if((hex.length()%2)==1) throw new IllegalArgumentException("Ungerade String-Zeichenfolge!"); 68 | int l = hex.length(); 69 | byte[] data = new byte[l/2]; 70 | for (int i = 0; i < l; i += 2) 71 | { 72 | data[i/2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i+1), 16)); 73 | } 74 | return data; 75 | } 76 | 77 | 78 | 79 | /** Hex-String wird in ein Byte Array konvertiert. Dabei werden hier auch ungerade Zeichenfolgen akzepiert! 80 | Ist die Länge ungerade, wird eine 0 voranngestellt: Beispeiel 123 ---> 0123 81 | @param hex Zeichen zwichen [0-f] oder [0-F] mit einer geraden oder ungeraden Anzahl. 82 | @return Byte-Array welches dem Hex-String entspricht. **/ 83 | public static byte[] hexStringToByteArray_oddLength(String hex) 84 | { 85 | if((hex.length()%2)==1) hex = "0"+hex; 86 | int l = hex.length(); 87 | byte[] data = new byte[l/2]; 88 | for (int i = 0; i < l; i += 2) 89 | { 90 | data[i/2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i+1), 16)); 91 | } 92 | return data; 93 | } 94 | 95 | 96 | 97 | /** Byte Array wird in einen Hexa String konvertiert. */ 98 | public static String byteArrayToHexString(byte[] a) 99 | { 100 | StringBuilder sb = new StringBuilder(a.length * 2); 101 | for(byte b: a) 102 | sb.append(String.format("%02x", b)); 103 | return sb.toString(); 104 | } 105 | 106 | 107 | 108 | /** Konvertiert ein Double in 8 Byte-Array **/ 109 | public static byte[] double_to_8Bytes(double value) 110 | { 111 | byte[] bytes = new byte[8]; 112 | ByteBuffer.wrap(bytes).putDouble(value); 113 | return bytes; 114 | } 115 | 116 | 117 | 118 | /** Wandelt ein 8 Byte-Array in Double um 119 | @param b Es werden maximal nur die ersten 8 Bytes verwendet. 120 | Wenn weniger als 8Bytes übergeben wurde, wird wird vorne mit Nullen aufgefüllt. **/ 121 | public static double byteArray_to_double(byte[] b) 122 | { 123 | if(b.length < 8) 124 | { 125 | byte[] out = new byte[8]; 126 | System.arraycopy(b, 0, out, 8-b.length, b.length); 127 | return ByteBuffer.wrap(out).getDouble(); 128 | } 129 | return ByteBuffer.wrap(b).getDouble(); 130 | } 131 | 132 | 133 | 134 | /** Konvertiert das normalte Integer Format in ein korrekt aufsteigendes Integer Format 135 | 0x00000000 = -2.147.483.648; 136 | 0xFFFFFFFF = 2.147.483.647; */ 137 | public static int int_convert_int(int in) 138 | { 139 | return in ^ -2147483648; 140 | } 141 | 142 | 143 | 144 | /** Wandelt ein Base58 Text-String in Hex-String um. 145 | In "laenge" wird die Anzahl der Ausgabe-Zeichen übergeben. */ 146 | public static String Base58ToHexString(String str, int laenge) 147 | { 148 | char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); 149 | char[] a = str.toCharArray(); 150 | BigInteger erg = new BigInteger("0"); 151 | BigInteger b58 = new BigInteger("58"); 152 | int e = a.length-1; 153 | for(int j=0;j0;) 203 | { 204 | z1 = z1.divide(base); 205 | i = z1.doubleValue(); 206 | laenge++; 207 | } 208 | char[] Key = new char[laenge]; 209 | for(int i=laenge; i>0; i--) 210 | { 211 | rest = z.mod(base); 212 | Key[i-1] = ALPHABET[rest.intValue()]; 213 | z = z.divide(base); 214 | } 215 | int nullLänge = 0; 216 | for(int i=0; k[i]==0 && i i) 234 | { 235 | tmp = array[j]; 236 | array[j] = array[i]; 237 | array[i] = tmp; 238 | j--; 239 | i++; 240 | } 241 | } 242 | 243 | 244 | 245 | /** Dreht die Reihenfolge eines ByteArray´s und gibt das Kopierte Array zurück. */ 246 | public static byte[] swapBytesCopy(byte[] in) 247 | { 248 | byte[] array = in.clone(); 249 | if (array == null) return null; 250 | int i = 0; 251 | int j = array.length - 1; 252 | byte tmp; 253 | while (j > i) 254 | { 255 | tmp = array[j]; 256 | array[j] = array[i]; 257 | array[i] = tmp; 258 | j--; 259 | i++; 260 | } 261 | return array; 262 | } 263 | 264 | 265 | 266 | /** Wandelt Byte-Array in Integer um, nur positive Zahlen. 267 | Es werden maximal nur die ersten 4 Bytes verwendet. 268 | Im Fehlerfall wird -1 zurückgegeben 269 | Maximaler Byte Wert für positive Werte: 7f ff ff ff = 2147483647 = Maximaler positiver Integer Wert */ 270 | public static int byteArray_to_int(byte[] b) 271 | { 272 | if(b.length==1) return b[0]&0xff; 273 | if(b.length==2) return ((0xFF & b[0]) << 8) | (0xFF & b[1]); 274 | if(b.length==3) return ((0xFF & b[0]) << 16) | ((0xFF & b[1]) << 8) | (0xFF & b[2]); 275 | if(b.length>=4) return ((0xFF & b[0]) << 24) | ((0xFF & b[1]) << 16) | ((0xFF & b[2]) << 8) | (0xFF & b[3]); 276 | return -1; 277 | } 278 | 279 | 280 | 281 | /** Wandelt Byte-Array in Integer um, nur positive Zahlen. 282 | Hier kann auch ein Integer-Array verwendet weren 283 | Es werden maximal nur die ersten 4 Bytes verwendet. 284 | Im Fehlerfall wird -1 zurückgegeben 285 | Maximaler Byte Wert für positive Werte: 7f ff ff ff = 2147483647 = Maximaler positiver Integer Wert */ 286 | public static int byteArray_to_int(int[] b) 287 | { 288 | if(b.length==1) return b[0]&0xff; 289 | if(b.length==2) return ((0xFF & b[0]) << 8) | (0xFF & b[1]); 290 | if(b.length==3) return ((0xFF & b[0]) << 16) | ((0xFF & b[1]) << 8) | (0xFF & b[2]); 291 | if(b.length>=4) return ((0xFF & b[0]) << 24) | ((0xFF & b[1]) << 16) | ((0xFF & b[2]) << 8) | (0xFF & b[3]); 292 | return -1; 293 | } 294 | 295 | 296 | 297 | /** Wandelt Byte-Array in Long um, nur positive Zahlen. 298 | Wenn mehr als 8 Byte übergeben werden, werden nur die vordersten 8 Byte zur Berechnung benutzt. 299 | Maximaler Byte Wert für positive Werte: 7f ff ff ff ff ff ff ff = 2^63-1 Maximaler positiver Long wert */ 300 | public static long byteArray_to_long(byte[] b) 301 | { 302 | if(b.length==1) return b[0]&0xff; 303 | if(b.length==2) return ((long)(0xFF & b[0]) << 8) | ((long)(0xFF & b[1])); 304 | if(b.length==3) return ((long)(0xFF & b[0]) << 16) | ((long)(0xFF & b[1]) << 8) | ((long)(0xFF & b[2])); 305 | if(b.length==4) return ((long)(0xFF & b[0]) << 24) | ((long)(0xFF & b[1]) << 16) | ((long)(0xFF & b[2]) << 8) | ((long)(0xFF & b[3])); 306 | if(b.length==5) return ((long)(0xFF & b[0]) << 32) | ((long)(0xFF & b[1]) << 24) | ((long)(0xFF & b[2]) << 16) | ((long)(0xFF & b[3]) << 8) | ((long)(0xFF & b[4])); 307 | if(b.length==6) return ((long)(0xFF & b[0]) << 40) | ((long)(0xFF & b[1]) << 32) | ((long)(0xFF & b[2]) << 24) | ((long)(0xFF & b[3]) << 16) | ((long)(0xFF & b[4]) << 8) | ((long)(0xFF & b[5])); 308 | if(b.length==7) return ((long)(0xFF & b[0]) << 48) | ((long)(0xFF & b[1]) << 40) | ((long)(0xFF & b[2]) << 32) | ((long)(0xFF & b[3]) << 24) | ((long)(0xFF & b[4]) << 16) | ((long)(0xFF & b[5]) << 8) | ((long)(0xFF & b[6])); 309 | if(b.length>=8) return ((long)(0xFF & b[0]) << 56) | ((long)(0xFF & b[1]) << 48) | ((long)(0xFF & b[2]) << 40) | ((long)(0xFF & b[3]) << 32) | ((long)(0xFF & b[4]) << 24) | ((long)(0xFF & b[5]) << 16) | ((long)(0xFF & b[6]) << 8) | ((long)(0xFF & b[7])); 310 | return -1; 311 | } 312 | 313 | 314 | 315 | /** Wandelt ein Integer in 2 Byte um. */ 316 | public static byte[] int_To_2_ByteArray(int data) 317 | { 318 | byte[] out = new byte[2]; 319 | out[0] = (byte) ((data & 0x0000FF00) >> 8); 320 | out[1] = (byte) ((data & 0x000000FF) >> 0); 321 | return out; 322 | } 323 | 324 | 325 | 326 | /** Wandelt ein Integer in 4 Byte um. */ 327 | public static byte[] int_To_4_ByteArray(int data) 328 | { 329 | byte[] out = new byte[4]; 330 | out[0] = (byte) ((data & 0xFF000000) >> 24); 331 | out[1] = (byte) ((data & 0x00FF0000) >> 16); 332 | out[2] = (byte) ((data & 0x0000FF00) >> 8); 333 | out[3] = (byte) ((data & 0x000000FF) >> 0); 334 | return out; 335 | } 336 | 337 | 338 | 339 | /** Wandelt ein Integer in 4 Byte um und swapt die Byte-Reihenvolge. */ 340 | public static byte[] int_To_4_ByteArray_swap(int data) 341 | { 342 | byte[] out = new byte[4]; 343 | out[3] = (byte) ((data & 0xFF000000) >> 24); 344 | out[2] = (byte) ((data & 0x00FF0000) >> 16); 345 | out[1] = (byte) ((data & 0x0000FF00) >> 8); 346 | out[0] = (byte) ((data & 0x000000FF) >> 0); 347 | return out; 348 | } 349 | 350 | 351 | 352 | /** Wandelt ein Long in 8 Byte um. 353 | Es werden immer genau 8Byte erzeugt und mit Nullen vorne aufgefüllt */ 354 | public static byte[] long_To_8_ByteArray(long data) 355 | { 356 | ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); 357 | buffer.putLong(data); 358 | return buffer.array(); 359 | } 360 | 361 | 362 | 363 | /** Beschneidet ein ByteArray belibiger länge auf eine fest definierte Länge "len". 364 | - Wenn "data" kleiner als "len" ist wird es vorne mit Nullen aufgefüllt. 365 | - Wenn "data" länger als "len" ist, wird es hinten abgeschnitten. */ 366 | public static byte[] to_fixLength(byte[] data, int len) 367 | { 368 | if(data.length < len) 369 | { 370 | byte[] out = new byte[len]; 371 | System.arraycopy(data, 0, out, len-data.length, data.length); 372 | return out; 373 | } 374 | if(data.length > len) return Arrays.copyOf(data, len); 375 | return data; 376 | } 377 | 378 | 379 | 380 | /** Entfernt alle führenden Nullen eines Byte-Arrays 381 | @return Null wenn das Byte-Array nur Nullen enthällt. **/ 382 | public static byte[] removeLeadingZeros(byte[] data) 383 | { 384 | int i=0; 385 | for(;i=a && z<= b)return true; 396 | else return false; 397 | } 398 | 399 | 400 | 401 | /** Konvertiert einen BitcoinBetrag von ByteArray nach Text-String. z.B. (0,03198327) 402 | Es wird Komma als Trennzeichen angezeigt. 403 | Es werden immer 8 Nachkommazeichen vollständig angezeigt. 404 | Methode getestet mit Referenz (BitcoinCore über RPC). Zahlenraum vollständig getestet von 0.00000000 bis 0.15000000 **/ 405 | public static String byteArrayToValue(byte[] hexValue) 406 | { 407 | byte[] b = Convert.swapBytesCopy(hexValue); 408 | return new DecimalFormat("0.00000000").format((double)Convert.byteArray_to_long(b)/100000000); 409 | } 410 | 411 | 412 | 413 | /** Konvertiert ein Bitcoin Betrag von einem Zahlen-Wert (z.B. 0,03198327) in ein ByteArray so wie der Betrag in der Transaktion gepeichert wird. 414 | Es kann Komma oder Punkt als Trennzeichen verwendet werden. 415 | Bis zu 8 Nachkommastellen erlaubt. Nachkommastellen als "0" können weggelassen werden. 416 | Methode getestet mit Referenz (BitcoinCore über RPC). Zahlenraum vollständig getestet von 0.00000000 bis 0.15000000 **/ 417 | public static byte[] valueToHex(String inValue) 418 | { 419 | double d = Double.parseDouble(inValue.replace(",", ".")); 420 | String str = String.format("%.8f", d); 421 | str = str.replaceFirst(",", ""); 422 | BigInteger bi = new BigInteger(str); 423 | bi.longValue(); 424 | Long l = bi.longValue(); 425 | return Convert.swapBytesCopy(Convert.long_To_8_ByteArray(l)); 426 | } 427 | } -------------------------------------------------------------------------------- /src/BTClib3001/HMAC.java: -------------------------------------------------------------------------------- 1 | package BTClib3001; 2 | import java.util.Arrays; 3 | 4 | 5 | 6 | /**************************************************************************************************************** 7 | * Version 1.0 Autor: Mr. Maxwell vom 22.02.2023 * 8 | * HMAC SHA256 * 9 | * Es wird ein HMAC mit SHA256 berechnet: "https://de.wikipedia.org/wiki/HMAC" * 10 | * Statische Klasse die nur eine statische Methode hat. * 11 | * Es gibt nur eine Abhängigkeit zur SHA256 Hash Klasse * 12 | * Getestet durch Referenzimplementierung von "javax.crypto.Mac;" mit 10000000 rand. Vektoren, rand. Längen. * 13 | ****************************************************************************************************************/ 14 | 15 | 16 | 17 | 18 | public class HMAC 19 | { 20 | 21 | 22 | 23 | /** HMAC Funktion die SHA256 verwendet **/ 24 | public static byte[] getHMAC_SHA256(byte[] data, byte[] key) throws Exception 25 | { 26 | byte[] k; 27 | if(key.length>64) k = Arrays.copyOf(SHA256.getHash(key),64); 28 | else k = Arrays.copyOf(key, 64); 29 | byte[] opad = new byte[64]; 30 | byte[] ipad = new byte[64]; 31 | for(int i=0; i<64;i++) opad[i]=0x5c; 32 | for(int i=0; i<64;i++) ipad[i]=0x36; 33 | byte[] b = SHA256.getHash(add(xor(k,ipad), data)); 34 | byte[] out = SHA256.getHash(add(xor(k,opad), b)); 35 | return out; 36 | } 37 | 38 | 39 | 40 | // ------------------------------------------------- private Methoden ------------------------------------------- 41 | 42 | // verbindet die beiden Arrays hintereinander 43 | private static byte[] add(byte[] a, byte[] b) 44 | { 45 | byte[] out = new byte[a.length + b.length]; 46 | System.arraycopy(a, 0, out, 0, a.length); 47 | System.arraycopy(b, 0, out, a.length, b.length); 48 | return out; 49 | } 50 | 51 | 52 | // XOR von a und b. a unb b müssen gleich lang sein! 53 | private static byte[] xor(byte[] a, byte[] b) throws Exception 54 | { 55 | if(a.length != b.length) throw new Exception("a and b are not the same length"); 56 | byte[] out = new byte[a.length]; 57 | for(int i=0;i 0) throw new IllegalArgumentException("Error in \"PrvKey\": Private Key is > Max!"); 186 | } 187 | 188 | 189 | 190 | // erkennt den String als private-Key in den 3 möglichen Formaten, und gibt ihn als Hexa-PrivateKey zurück 191 | // Hexa, Base58, Base6 192 | private byte[] txtToHexPrivKey(String str) throws IllegalArgumentException 193 | { 194 | str = str.trim(); 195 | int format = getFormat(str); 196 | switch(format) 197 | { 198 | case-1: throw new IllegalArgumentException("Error in \"PrvKey\": false format"); //-1 = Fehler kein richtiges Format erkannt 199 | case 0: throw new IllegalArgumentException("Error in \"PrvKey\": Null-String"); // 0 = Null String 200 | case 16: return Convert.hexStringToByteArray(str); // 16 = Hexa 201 | case 58: return base58_PrivateKey_to_HexPrivateKey(str); // 58 = Base58 202 | case 6: return Convert.hexStringToByteArray(base6_PrivateKey_to_HexPrivateKey(str)); // 6 = Base6 203 | default: break; 204 | } 205 | return null; 206 | } 207 | 208 | 209 | 210 | // gibt das Format des Strings zurück 211 | // -1 = Fehler kein richtiges Format erkannt 212 | // 0 = Null String 213 | // 16 = Hexa 214 | // 58 = Base58 215 | // 6 = Base6 216 | private int getFormat(String str) 217 | { 218 | if(str.equals("")) return 0; // prüfen ob leer String 219 | if(str.length()==64 && str.matches("[0-9a-fA-F]+")) return 16; // prüfen auf Hexa 220 | if( str.matches("[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+")) return 58; // prüfen auf Base58 221 | if(str.length()==109 && str.matches("[123456-]+")) return 6; // prüfen auf Base6 222 | return -1; 223 | } 224 | 225 | 226 | 227 | // Bekommt den PrivateKey in Base 58 übergeben 228 | // - Wird in Hexa konvertiert mit allen Status-Byes und dem Hash 229 | // - Anhand der CoinParameter wird der Coin-Typ identifiziert 230 | // - Nun wird der Key in die entsprechenden Teile zerlegt: [Status-Bytes | Raw-PrivateKey | compressed-byte | 4byte Hash] 231 | // - Die Status-Bytes und der Hash, werden geprüft. 232 | // - Wenn alles richtig ist, wird der raw-PrivateKey ohne die status-Bytes und ohne den Hash zurück gegeben. 233 | // dabei werden alle status-bytes und Hashes entfernt 234 | private byte[] base58_PrivateKey_to_HexPrivateKey(String str) throws IllegalArgumentException 235 | { 236 | byte[] prv = Convert.removeLeadingZeros(Convert.hexStringToByteArray_oddLength(Convert.Base58ToHexString(str,str.length()*2))); 237 | if(!Arrays.equals(pref_PrivKey, Arrays.copyOfRange(prv,0,pref_PrivKey.length))) throw new IllegalArgumentException("Error in \"PrvKey\": Private key has an incorrect coin-parameter!"); 238 | byte[] h32 = Calc.getHashSHA256(Calc.getHashSHA256(Arrays.copyOfRange(prv, 0, prv.length-4))); // 32Byte langer Hash 239 | byte[] h4 = Arrays.copyOfRange(h32,0,4); // 4Byte langer Hash 240 | byte[] hash = Arrays.copyOfRange(prv, prv.length-4, prv.length); // Angehängter original Hash 241 | if(!Arrays.equals(hash, h4)) throw new IllegalArgumentException("Error in \"PrvKey\": Private key hash incorrect!"); 242 | prv = Arrays.copyOfRange(prv, pref_PrivKey.length, prv.length-4); 243 | if(prv.length>33 || prv.length<32) throw new IllegalArgumentException("Error in \"PrvKey\": Private key size incorrect!"); 244 | if(prv.length==32) { compressed = 2; return prv;} // Unkompressed Priv Key 245 | if(prv.length==33 && prv[32]==0x01) {compressed = 1; return Arrays.copyOfRange(prv, 0, 32);} // Compressed Priv Key 246 | throw new IllegalArgumentException("Error in \"PrvKey\": Private key incorrect Format!"); 247 | } 248 | 249 | 250 | 251 | // Hier werden die Würfelzeichen in HEX konvertiert 252 | // 1=1, 2=2, 3=3 4=4, 5=5, 6=0 253 | // Mamimaler Würfelwerd (Mod) = 1621416542-2615626236-3462631235-4363525141-6636261141-4266313436-1546433233-1342224233-3313325535-4344331641 254 | private String base6_PrivateKey_to_HexPrivateKey(String str) 255 | { 256 | BigInteger mod = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",16); // Modulo des Privatkey / überlauf 257 | str = str.replaceAll("-",""); // Platzhalter Zeichen "-" wird aus dem String entfernt 258 | str = str.replaceAll("6","0"); // Die Ziffer 6 wird mit 0 ersetzt 259 | BigInteger dec = new BigInteger(str,6); // nach BigInteger zur Bases 6 260 | dec = dec.mod(mod); // Private Key Modulo FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 261 | String erg = dec.toString(16); // nach String zur Bases 16 262 | while(erg.length() < 64) erg="0"+erg; // String wird vorne mit nullen aufgefüllt 263 | return erg; 264 | } 265 | 266 | 267 | //Erstellt das RedeemScript für eine SegWit P2SH Adresse die mit 3 beginnt. 268 | //Es wird der Hash160 übergeben so wie er üblicherweise berechnet wird. Allerdings muss er von einem komprimierten PubKey stammen! 269 | //Zurück gegeben wird das "RedeemScript" welches dem neuem Hash160 entspricht, der dann so in eine Base58 Adresse codert werden kann. 270 | private byte[] getRedeemScript(byte[] hash160) 271 | { 272 | final byte[] scriptPrefix = {0x00,0x14}; // Das Script Prefix 273 | byte[] script = new byte[22]; 274 | script[0] = scriptPrefix[0]; 275 | script[1] = scriptPrefix[1]; 276 | System.arraycopy(hash160, 0, script, 2, 20); 277 | return Calc.getHashRIPEMD160(Calc.getHashSHA256(script)); 278 | } 279 | } -------------------------------------------------------------------------------- /src/BTClib3001/Ripemd160.java: -------------------------------------------------------------------------------- 1 | package BTClib3001; 2 | import static java.lang.Integer.rotateLeft; 3 | import java.util.Arrays; 4 | import java.util.Objects; 5 | 6 | 7 | 8 | 9 | /************************************************************************ 10 | * RIPEMD-160 hash * 11 | ************************************************************************/ 12 | 13 | 14 | 15 | public final class Ripemd160 16 | { 17 | 18 | private static final int BLOCK_LEN = 64; 19 | 20 | public static byte[] getHash(byte[] b) 21 | { 22 | Objects.requireNonNull(b); 23 | int[] state = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0}; 24 | int off = b.length / BLOCK_LEN * BLOCK_LEN; 25 | compress(state, b, off); 26 | byte[] block = new byte[BLOCK_LEN]; 27 | System.arraycopy(b, off, block, 0, b.length - off); 28 | off = b.length % block.length; 29 | block[off] = (byte)0x80; 30 | off++; 31 | if (off + 8 > block.length) 32 | { 33 | compress(state, block, block.length); 34 | Arrays.fill(block, (byte)0); 35 | } 36 | long len = (long)b.length << 3; 37 | for (int i = 0; i < 8; i++) block[block.length - 8 + i] = (byte)(len >>> (i * 8)); 38 | compress(state, block, block.length); 39 | byte[] result = new byte[state.length * 4]; 40 | for (int i = 0; i < result.length; i++) result[i] = (byte)(state[i / 4] >>> (i % 4 * 8)); 41 | return result; 42 | } 43 | 44 | 45 | 46 | /*------------------------------------------------ Private Methoden -------------------------------------------------------------*/ 47 | 48 | private static void compress(int[] state, byte[] blocks, int len) 49 | { 50 | if (len % BLOCK_LEN != 0) throw new IllegalArgumentException(); 51 | for (int i = 0; i < len; i += BLOCK_LEN) 52 | { 53 | int[] schedule = new int[16]; 54 | for (int j = 0; j < BLOCK_LEN; j++) schedule[j / 4] |= (blocks[i + j] & 0xFF) << (j % 4 * 8); 55 | int al = state[0], ar = state[0]; 56 | int bl = state[1], br = state[1]; 57 | int cl = state[2], cr = state[2]; 58 | int dl = state[3], dr = state[3]; 59 | int el = state[4], er = state[4]; 60 | for (int j = 0; j < 80; j++) 61 | { 62 | int temp; 63 | temp = rotateLeft(al + f(j, bl, cl, dl) + schedule[RL[j]] + KL[j / 16], SL[j]) + el; 64 | al = el; 65 | el = dl; 66 | dl = rotateLeft(cl, 10); 67 | cl = bl; 68 | bl = temp; 69 | temp = rotateLeft(ar + f(79 - j, br, cr, dr) + schedule[RR[j]] + KR[j / 16], SR[j]) + er; 70 | ar = er; 71 | er = dr; 72 | dr = rotateLeft(cr, 10); 73 | cr = br; 74 | br = temp; 75 | } 76 | int temp = state[1] + cl + dr; 77 | state[1] = state[2] + dl + er; 78 | state[2] = state[3] + el + ar; 79 | state[3] = state[4] + al + br; 80 | state[4] = state[0] + bl + cr; 81 | state[0] = temp; 82 | } 83 | } 84 | 85 | 86 | private static int f(int i, int x, int y, int z) 87 | { 88 | assert 0 <= i && i < 80; 89 | if (i < 16) return x ^ y ^ z; 90 | if (i < 32) return (x & y) | (~x & z); 91 | if (i < 48) return (x | ~y) ^ z; 92 | if (i < 64) return (x & z) | (y & ~z); 93 | return x ^ (y | ~z); 94 | } 95 | 96 | 97 | private static final int[] KL = {0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E}; 98 | private static final int[] KR = {0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000}; 99 | 100 | private static final int[] RL = 101 | { 102 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 103 | 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, 104 | 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, 105 | 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, 106 | 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 107 | }; 108 | 109 | private static final int[] RR = 110 | { 111 | 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 112 | 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, 113 | 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, 114 | 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, 115 | 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 116 | }; 117 | 118 | private static final int[] SL = 119 | { 120 | 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 121 | 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, 122 | 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, 123 | 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, 124 | 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 125 | }; 126 | 127 | private static final int[] SR = 128 | { 129 | 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 130 | 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, 131 | 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, 132 | 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 133 | 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 134 | }; 135 | } -------------------------------------------------------------------------------- /src/BTClib3001/SHA256.java: -------------------------------------------------------------------------------- 1 | package BTClib3001; 2 | import static java.lang.Integer.rotateRight; 3 | import java.util.Arrays; 4 | import java.util.Objects; 5 | 6 | 7 | 8 | /**************************************** 9 | * SHA256 Hash * 10 | * Benchmark: 800ns / Hash * 11 | ****************************************/ 12 | 13 | 14 | 15 | 16 | public class SHA256 17 | { 18 | private static final int BLOCK_LEN = 64; 19 | private static final int[] INITIAL_STATE = {0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,}; 20 | private static final int[] ROUND_CONSTANTS = 21 | { 22 | 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 23 | 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 24 | 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 25 | 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 26 | 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 27 | 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 28 | 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 29 | 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, 30 | 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 31 | 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 32 | 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 33 | 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 34 | 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 35 | 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 36 | 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 37 | 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2, 38 | }; 39 | 40 | 41 | 42 | 43 | 44 | 45 | /*-------------------------------------------- Public Methode ------------------------------------*/ 46 | 47 | 48 | public static byte[] getHash(byte[] b) 49 | { 50 | return getHash(b, INITIAL_STATE.clone(), 0); 51 | } 52 | 53 | 54 | 55 | /*-------------------------------------------- Private Methoden -----------------------------------*/ 56 | 57 | 58 | private static byte[] getHash(byte[] msg, int[] initState, int prefixLen) 59 | { 60 | Objects.requireNonNull(msg); 61 | int[] state = initState; 62 | int off = msg.length / BLOCK_LEN * BLOCK_LEN; 63 | compress(state, msg, off); 64 | byte[] block = new byte[BLOCK_LEN]; 65 | System.arraycopy(msg, off, block, 0, msg.length - off); 66 | off = msg.length % block.length; 67 | block[off] = (byte)0x80; 68 | off++; 69 | if (off + 8 > block.length) 70 | { 71 | compress(state, block, block.length); 72 | Arrays.fill(block, (byte)0); 73 | } 74 | long len = ((long)msg.length + prefixLen) << 3; 75 | for (int i = 0; i < 8; i++) block[block.length - 1 - i] = (byte)(len >>> (i * 8)); 76 | compress(state, block, block.length); 77 | byte[] result = new byte[state.length * 4]; 78 | for (int i = 0; i < result.length; i++) result[i] = (byte)(state[i / 4] >>> ((3 - i % 4) * 8)); 79 | return result.clone(); 80 | } 81 | 82 | 83 | 84 | private static void compress(int[] state, byte[] blocks, int len) 85 | { 86 | if (len < 0 || len % BLOCK_LEN != 0) throw new IllegalArgumentException(); 87 | for (int i = 0; i < len; i += BLOCK_LEN) 88 | { 89 | int[] s = new int[64]; 90 | for (int j = 0; j < BLOCK_LEN; j++) s[j / 4] |= (blocks[i + j] & 0xFF) << ((3 - j % 4) * 8); 91 | for (int j = 16; j < 64; j++) 92 | { 93 | s[j] = s[j-16] + s[j-7] + (rotateRight(s[j-15], 7) ^ rotateRight(s[j-15], 18) ^ (s[j-15] >>> 3)) + (rotateRight(s[j- 2], 17) ^ rotateRight(s[j- 2], 19) ^ (s[j- 2] >>> 10)); 94 | } 95 | int a = state[0]; 96 | int b = state[1]; 97 | int c = state[2]; 98 | int d = state[3]; 99 | int e = state[4]; 100 | int f = state[5]; 101 | int g = state[6]; 102 | int h = state[7]; 103 | for (int j = 0; j < 64; j++) 104 | { 105 | int t1 = h + (rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25)) + (g ^ (e & (f ^ g))) + ROUND_CONSTANTS[j] + s[j]; 106 | int t2 = (rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22)) + ((a & (b | c)) | (b & c)); 107 | h = g; 108 | g = f; 109 | f = e; 110 | e = d + t1; 111 | d = c; 112 | c = b; 113 | b = a; 114 | a = t1 + t2; 115 | } 116 | state[0] += a; 117 | state[1] += b; 118 | state[2] += c; 119 | state[3] += d; 120 | state[4] += e; 121 | state[5] += f; 122 | state[6] += g; 123 | state[7] += h; 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /src/BTClib3001/ScryptHash.java: -------------------------------------------------------------------------------- 1 | package BTClib3001; 2 | 3 | 4 | 5 | /******************************************************************************************************************************************************************** 6 | * Version 1.0 Autor: Mr. Maxwell vom 21.02.2023 * 7 | * * 8 | * ScryptHash ist ein Hash zur Laufzeitverlängerung der erhöhte CPU und Speicher Resourcen verbraucht. * 9 | * ScryptHash benötigt nur die HMAC Kasse, sonnst keine Abhängigkeiten, der ScryptHash wird vollständig in dieser Klasse berechnet. * 10 | * Es gibt nur eine statische Methode die den ScryptHash berechnet. * 11 | * Es werden mehrere Parameter benötigt mit denen die Laufzeit und die benötigten Resourcen angepasst werden. * 12 | * Jede Änderung an den Parametern hat natürlich IMMER zur Folge, dass der ScryptHash sich ändert und damit inkompatibel zu vorherigen Berechnungen wird. * 13 | * Diese Klasse wurde mit einer Referenzimplementierung von bouncycastle aureichendt getestet, mehrere Tausend Random Testvektoren, mit verschiedenen Parametern. * 14 | * * 15 | * Beschreibung der Parameter: * 16 | * - data: Die Zeichenkette, die gehasht werden soll * 17 | * - salt: Eine Zeichenfolge, die den Hash zum Schutz vor Rainbow Table-Angriffen ändert * 18 | * - n: CPU-/Speicherkostenparameter. Muss größer als 1 sein und eine Potenz von 2 und kleiner als 2^(128*r/8) Standart = 2^14 * 19 | * - r: Der Blockgröße-Parameter, mit dem die Größe und die Leistung des sequenziellen Speichers genau eingestellt werden. Muss größer 1 sein. * 20 | * - p: Parallelisierungsparameter; eine positive ganze Zahl p ≤ (232− 1). * 21 | * - outLen - Die Länge der Ausgabe in Bytes. * 22 | *********************************************************************************************************************************************************************/ 23 | 24 | 25 | 26 | public class ScryptHash 27 | { 28 | 29 | 30 | /** @param data Input Data 31 | @param salt Salt 32 | @param n CPU cost parameter 33 | @param r Memory cost parameter 34 | @param p Parallelization parameter 35 | @param outLen Length out **/ 36 | public static byte[] getHash(byte[] data, byte[] salt, int n, int r, int p, int outLen) throws Exception 37 | { 38 | if (n < 2 || (n & (n - 1)) != 0) throw new IllegalArgumentException("N is not a correct input parameter"); 39 | if (n > Integer.MAX_VALUE / 128 / r) throw new IllegalArgumentException("N is not a correct input parameter"); 40 | if (r > Integer.MAX_VALUE / 128 / p) throw new IllegalArgumentException("r is not a correct input parameter"); 41 | byte[] out = new byte[outLen]; 42 | byte[] b = new byte[128*r*p]; 43 | byte[] a = new byte[256*r]; 44 | byte[] c = new byte[128*r*n]; 45 | b = pbkdf2(data, salt, b, p*r*128); 46 | for (int i=0; i0; i-=2) 101 | { 102 | x[ 4] ^= rot(x[ 0]+x[12], 7); x[ 8] ^= rot(x[ 4]+x[ 0], 9); 103 | x[12] ^= rot(x[ 8]+x[ 4],13); x[ 0] ^= rot(x[12]+x[ 8],18); 104 | x[ 9] ^= rot(x[ 5]+x[ 1], 7); x[13] ^= rot(x[ 9]+x[ 5], 9); 105 | x[ 1] ^= rot(x[13]+x[ 9],13); x[ 5] ^= rot(x[ 1]+x[13],18); 106 | x[14] ^= rot(x[10]+x[ 6], 7); x[ 2] ^= rot(x[14]+x[10], 9); 107 | x[ 6] ^= rot(x[ 2]+x[14],13); x[10] ^= rot(x[ 6]+x[ 2],18); 108 | x[ 3] ^= rot(x[15]+x[11], 7); x[ 7] ^= rot(x[ 3]+x[15], 9); 109 | x[11] ^= rot(x[ 7]+x[ 3],13); x[15] ^= rot(x[11]+x[ 7],18); 110 | x[ 1] ^= rot(x[ 0]+x[ 3], 7); x[ 2] ^= rot(x[ 1]+x[ 0], 9); 111 | x[ 3] ^= rot(x[ 2]+x[ 1],13); x[ 0] ^= rot(x[ 3]+x[ 2],18); 112 | x[ 6] ^= rot(x[ 5]+x[ 4], 7); x[ 7] ^= rot(x[ 6]+x[ 5], 9); 113 | x[ 4] ^= rot(x[ 7]+x[ 6],13); x[ 5] ^= rot(x[ 4]+x[ 7],18); 114 | x[11] ^= rot(x[10]+x[ 9], 7); x[ 8] ^= rot(x[11]+x[10], 9); 115 | x[ 9] ^= rot(x[ 8]+x[11],13); x[10] ^= rot(x[ 9]+x[ 8],18); 116 | x[12] ^= rot(x[15]+x[14], 7); x[13] ^= rot(x[12]+x[15], 9); 117 | x[14] ^= rot(x[13]+x[12],13); x[15] ^= rot(x[14]+x[13],18); 118 | } 119 | for (int i=0; i<16; i++) a[i]=x[i]+a[i]; 120 | for (int i=0; i<16; i++) 121 | { 122 | b[i*4] = (byte) (a[i] >> 0 & 0xff); 123 | b[i*4+1] = (byte) (a[i] >> 8 & 0xff); 124 | b[i*4+2] = (byte) (a[i] >> 16 & 0xff); 125 | b[i*4+3] = (byte) (a[i] >> 24 & 0xff); 126 | } 127 | } 128 | 129 | 130 | // PBKDF2 Schlüsselableitungsfunktion: "https://en.wikipedia.org/wiki/PBKDF2" 131 | private static byte[] pbkdf2(byte[] data, byte[] salt, byte[] c, int dkLen) throws Exception 132 | { 133 | int hLen = 32; 134 | byte[] hash = new byte[hLen]; 135 | byte[] blk = new byte[salt.length+4]; 136 | int l = (int) Math.ceil((double)dkLen / hLen); 137 | int r = dkLen-(l-1)*hLen; 138 | System.arraycopy(salt, 0, blk, 0, salt.length); 139 | for (int i=1; i<=l; i++) 140 | { 141 | blk[salt.length] = (byte) (i >> 24 & 0xff); 142 | blk[salt.length+1] = (byte) (i >> 16 & 0xff); 143 | blk[salt.length+2] = (byte) (i >> 8 & 0xff); 144 | blk[salt.length+3] = (byte) (i >> 0 & 0xff); 145 | byte[] hmac = HMAC.getHMAC_SHA256(blk ,data); 146 | System.arraycopy(hmac, 0, hash, 0, hLen); 147 | System.arraycopy(hash, 0, c, (i-1)*hLen, i==l ? r:hLen); 148 | } 149 | return c; 150 | } 151 | 152 | private static void xor(byte[] b1, int a, byte[] b2, int len) 153 | { 154 | for (int i=0; i>> (32 - b)); 160 | } 161 | 162 | private static int toInt(byte[] b, int pos) 163 | { 164 | int out; 165 | int i = (2*pos-1)*64; 166 | out = (b[i] & 0xff) << 0; 167 | out |= (b[i+1] & 0xff) << 8; 168 | out |= (b[i+2] & 0xff) << 16; 169 | out |= (b[i+3] & 0xff) << 24; 170 | return out; 171 | } 172 | } -------------------------------------------------------------------------------- /src/BTClib3001/SigScript.java: -------------------------------------------------------------------------------- 1 | package BTClib3001; 2 | 3 | 4 | 5 | /**************************************************************************************************************************** 6 | * V2.0 Autor: Mr. Maxwell vom 04.10.2023 * 7 | * Letzte Änderung: getName(); hinzugefügt und Klasse komplett neu, da sie fehlerhaft uns schlecht programmiert war. * 8 | * Gehört zur BTClib3001 * 9 | * Nicht statische Klasse die eine Signature aus dem SigScript parst. * 10 | * Vorgehensweise: * 11 | * Es wird mit dem Konstruktor ein "new SigScript(sig)" Object erstellt. * 12 | * Nun können die Signature-Teile aus dem SigScript über die Methoden abgerufen werden. * 13 | * Das raw SigScript in ByteArray darf durch die Klasse nicht verändert werden! * 14 | * Derzeit sind 2 Sig.Scripte implementiert: P2PK und P2PKH. * 15 | * Möglicherweise fehlen noch weitere Sig.Scripte! * 16 | * Achtung, dieser Parser wird im Blockexplorer verwendet und durchläuft jede Transakton. Laufzeit Konstruktor bachten!* 17 | *****************************************************************************************************************************/ 18 | 19 | 20 | 21 | public class SigScript 22 | { 23 | private byte[] sig; // Original raw Signature 24 | private int lenSig; // Die Gesamtlänge dieser Signatur (erstes Byte) 25 | private int posSig; // Die Startposition dieser Signature 26 | private int lenSigRS; // Die Länge der Signature R+S 27 | private int posSigRS; // Die Startposition der Signature r+s 28 | private int posSigR; // Die Startposition der Signature r 29 | private int posSigS; // Die Startposition der Signature s 30 | private int lenSigR; // Die Länge der Signature r 31 | private int lenSigS; // Die Länge der Signature s 32 | private int lenPub; // Die Länge des Public Keys 33 | private int posPub; // Die Startposition des Public Keys 34 | private String name; // Der Name es Sig.Scriptes 35 | private int nr =-1; // Die Nummer des identifizierten Sig.Scriptes. P2PK=1, P2PKH=2 ... Wenn -1 wurde ein unbekanntes Sig.Script geladen. 36 | 37 | 38 | 39 | 40 | // ------------------------------------- Konstruktor -------------------------------------------------- 41 | 42 | 43 | 44 | /** Der Konstruktor erstellt ein SigScript Objekt 45 | @param data Das raw Sig.Script als ByteArray 46 | Es soll hier keine Excepteion ausgelöst werden, da dies in Anwendungen (z.B. Blockexplorer) Störungen auslöst die so nicht gewollt sind. 47 | Bei einem unbekanntem Sig.Script soll stattdessen nur der Name "unknown..." in das Namensfeld übergeben werden und die Anwendung soll weiter laufen. **/ 48 | public SigScript(byte[] data) 49 | { 50 | this.sig = data; 51 | try 52 | { 53 | if(calc_P2PK()) return; // P2PK Pay-to-Public-Key 54 | if(calc_P2PKH()) return; // P2PKH Pay-to-Public-Key-Hash 55 | // Weitere Scripte hier einfügen! 56 | name = "unknown sig.script"; 57 | } 58 | catch (Exception e) {name = "unknown sig.script";} 59 | } 60 | 61 | 62 | 63 | 64 | 65 | // ---------------------------------------------------------------------- Public Methoden ---------------------------------------------------------------- 66 | 67 | 68 | /** Gibt den Namen des Signature-Scriptes zurück. 69 | Derzeit sind implementiert: P2PK und P2PKH **/ 70 | public String getName() 71 | { 72 | return name; 73 | } 74 | 75 | 76 | /** Gibt den r Teil der Signature zurück. 77 | Im Fehlerfall oder wenn das Script nicht erkannt wird, wird ein Byte-Array der Länge NULL zurück gegeben. Dies führt zu keinem Fehler in Programmen.**/ 78 | public byte[] getSigR() 79 | { 80 | if(nr==1 || nr==2) 81 | { 82 | byte[] out = new byte[lenSigR]; 83 | System.arraycopy(sig, posSigR, out, 0, lenSigR); 84 | return out; 85 | } 86 | return new byte[0]; 87 | } 88 | 89 | 90 | /** Gibt den s Teil der Signature zurück. 91 | Im Fehlerfall oder wenn das Script nicht erkannt wird, wird ein Byte-Array der Länge NULL zurück gegeben. Dies führt zu keinem Fehler in Programmen.**/ 92 | public byte[] getSigS() 93 | { 94 | if(nr==1 || nr==2) // Bei P2PK oder P2PKH 95 | { 96 | byte[] out = new byte[lenSigS]; 97 | System.arraycopy(sig, posSigS, out, 0, lenSigS); 98 | return out; 99 | } 100 | return new byte[0]; 101 | } 102 | 103 | 104 | /** Gibt den Public Key (einschließlich der 0x02, oder 0x03 oder 0x04 am Anfang) zurück. 105 | Im Fehlerfall oder wenn das Script nicht erkannt wird, wird ein Byte-Array der Länge NULL zurück gegeben. Dies führt zu keinem Fehler in Programmen. 106 | In einem P2PK Sig.Script ist kein Public-Key vorhanden! In dem Fall wird auch ein Null-Längen-ByteArray zurück gegeben. **/ 107 | public byte[] getPubKey() 108 | { 109 | if(nr==2) // Nur in P2PKH ist ein Public-Key in der Signatur vorhanden. 110 | { 111 | byte[] out = new byte[lenPub]; 112 | System.arraycopy(sig, posPub, out, 0, lenPub); 113 | return out; 114 | } 115 | return new byte[0]; 116 | } 117 | 118 | 119 | /** Gibt die Signaturteile r, s, und Pub.Key in getrennter, beschrifteter Form, als String zurück. **/ 120 | public String toString() 121 | { 122 | String r = "Sig. r = " + Convert.byteArrayToHexString(getSigR()); 123 | String s = "Sig. s = " + Convert.byteArrayToHexString(getSigS()); 124 | String pub = "Pub. Key = " + Convert.byteArrayToHexString(getPubKey()); 125 | return r+"\n"+s+"\n"+pub+"\n"; 126 | } 127 | 128 | 129 | 130 | 131 | // ---------------------------------------------------------------------- private Methoden ------------------------------------------------------------- 132 | 133 | 134 | 135 | // parst ein P2PK Sig.Script. 136 | // wenn es geparst werden kann wird true zurück gegeben. 137 | // Ein P2PK (Pay to Public-Key) Script enthält nur R und S, abe der Public Key ist hier nicht enthalten! 138 | private boolean calc_P2PK() throws Exception 139 | { 140 | try 141 | { 142 | int[] cs = Calc.decodeCompactSize(sig,0); 143 | posSig = cs[0]; 144 | lenSig = cs[1]; 145 | 146 | cs = Calc.decodeCompactSize(sig,posSig+1); 147 | posSigRS = cs[0]; 148 | lenSigRS = cs[1]; 149 | 150 | cs = Calc.decodeCompactSize(sig,posSigRS+1); 151 | posSigR = cs[0]; 152 | lenSigR = cs[1]; 153 | 154 | cs = Calc.decodeCompactSize(sig,posSigR+lenSigR+1); 155 | posSigS = cs[0]; 156 | lenSigS = cs[1]; 157 | 158 | if(posSigRS+lenSigRS+1 == sig.length) // Wenn keine weiteren Daten folgen ist es wahscheinlich ein P2PK Script 159 | { 160 | name = "P2PK"; 161 | nr = 1; 162 | return true; 163 | } 164 | else return false; 165 | } 166 | catch (Exception e){return false;} 167 | } 168 | 169 | 170 | // parst ein P2PKH Sig.Script. 171 | // wenn es geparst werden kann wird true zurück gegeben. 172 | // Ein P2PKH (Pay to Public-Key Hash) Script enthält R,S und den Public Key. Es ist das häufigste und allgemeine SigScript aller Legancy BTC Transaktionen. 173 | private boolean calc_P2PKH() throws Exception 174 | { 175 | try 176 | { 177 | int[] cs = Calc.decodeCompactSize(sig,0); 178 | posSig = cs[0]; 179 | lenSig = cs[1]; 180 | 181 | cs = Calc.decodeCompactSize(sig,posSig+1); 182 | posSigRS = cs[0]; 183 | lenSigRS = cs[1]; 184 | 185 | cs = Calc.decodeCompactSize(sig,posSigRS+1); 186 | posSigR = cs[0]; 187 | lenSigR = cs[1]; 188 | 189 | cs = Calc.decodeCompactSize(sig,posSigR+lenSigR+1); 190 | posSigS = cs[0]; 191 | lenSigS = cs[1]; 192 | 193 | cs = Calc.decodeCompactSize(sig,posSigRS+lenSigRS+1); 194 | posPub = cs[0]; 195 | lenPub = cs[1]; 196 | 197 | if(lenPub + lenSig + 2 == sig.length) // Wenn keine weiteren Daten folgen ist es wahscheinlich ein P2PKH Script 198 | { 199 | name = "P2PKH"; 200 | nr = 2; 201 | return true; 202 | } 203 | else return false; 204 | } 205 | catch (Exception e){return false;} 206 | } 207 | } -------------------------------------------------------------------------------- /src/BTClib3001/TxSigniererLegancy.java: -------------------------------------------------------------------------------- 1 | package BTClib3001; 2 | import java.io.IOException; 3 | import java.math.BigInteger; 4 | import java.util.Arrays; 5 | import ECDSA.Secp256k1; 6 | 7 | 8 | 9 | 10 | /******************************************************************************************************************************************* 11 | * V1.3.0 Autor: Mr. Maxwell vom 08.01.2024 * 12 | * * 13 | * BTClib3001 Klasse * 14 | * Dieser Transaktions-Singnierer signiert unsignierte Legancy Transaktionen. (Keine Witness Tx Verwenden) * 15 | * static Klasse ohne Konstruktor * 16 | * Klasse getestet! Funktioniert für alle Legancy-Transaktionen (mit mehreren Inputs und Outputs) fehlerfrei ab Version V1.2.0! * 17 | ********************************************************************************************************************************************/ 18 | 19 | 20 | 21 | 22 | 23 | public class TxSigniererLegancy 24 | { 25 | final static byte[] MAINNET = {(byte) 0xf9,(byte) 0xbe,(byte) 0xb4,(byte) 0xD9}; // magic 26 | final static byte[] TESTNET3 = {(byte) 0x0b,(byte) 0x11,(byte) 0x09,(byte) 0x07}; // magic 27 | final static byte[] PrevPrivMAINNET = {(byte) 0x80}; 28 | final static byte[] PrevPrivTESTNET3 = {(byte) 0xef}; 29 | 30 | 31 | 32 | 33 | 34 | 35 | /** Signiert eine unsignierte Legancy Transaktion mit mehreren Tx-In und Ausgängen. Keine Witness Transaktionen verwenden! 36 | Der Hash-Code am ende der Tx "01000000" wird hier eingefügt! (Tx wird ohne Hash-Code übergeben) 37 | Da jeder Tx-Eingang separat signiert werden muss, werden die Attribute als Array angefordert. Die Länge der Arrays ergibt sich aus der Tx-In Anzahl. 38 | Beispiel: Wenn die TX 27 Eingänge enthält, müssen natürlich auch 27 Private-Keys, 27 Zufallszahlen, und 27 Compressed-Flags übergeben werden. 39 | @param usigTx Unsignierte Transaktion, die signiert werden soll. (Ohne Hash-Code am Ende) 40 | @param privKey Ein Array mit der Anzahl an Private-Keys von Tx-In Eingängen. 41 | @param k Ein Array mit der Anzahl an Zufallszahlen von Tx-In Eingängen 42 | @param compressed Boolean-Array, Für jede Tx-In muss die Angabe über das Compressed Flag erfolgen. 43 | @return Signierte Raw-Transaktion als Byte-Array in Protokoll-Format die so gesendet werde kann. **/ 44 | public static byte[] sigTx(byte[] usigTx, byte[][] privKey, byte[][] k, boolean[] compressed) 45 | { 46 | byte[] uTx = Arrays.copyOfRange(usigTx, 0, usigTx.length+4); // Hash-Code wird angehängt 47 | uTx[uTx.length-4] = 0x01; // Hash-Code wird angehängt 48 | byte[] b1 = removeSigScript(uTx, -1); // Löschte alle Daten im SigScript Feld 49 | ByteArrayList out = new ByteArrayList(b1); 50 | Transaktion tx = new Transaktion(b1,0); 51 | for(int i = tx.getTxInCount()-1; i>=0; i--) 52 | { 53 | byte[] sicScript = sigScript(uTx,privKey[i],k[i],compressed[i], i); // Erstellt die Signatur 54 | int posS = tx.getSigScript_pos()[i]; // Ermittelt die Position des SigScriptes in der Tx 55 | out.remove(posS-1, posS); // Löscht das alte Längen-Byte des SigScript Felds 56 | out.insert(sicScript, posS-1); // Schiebt die Signatur im SigScript Feld ein 57 | } 58 | out.remove(out.size()-4, out.size()); // Der Hashcode wird wieder entfernt 59 | return out.getArrayAll(); 60 | } 61 | 62 | 63 | 64 | 65 | 66 | /** Hier wird ein Signatur-Skript (sigScript) für eine einzelne Eingabe erzeugt/signiert. 67 | Dazu werden alle anderen Signatur-Scripte entfernt und nur das zu signierende gelassen und dann der Sig-Hash generiert. 68 | @param index der zu signierenden Tx-Eingabe. 0 = die erste Eingabe usw. 69 | @param usigTx Unsignierte Transaktion, aus der ein SigScript signiert werden soll 70 | @param privKey der Private-Key mit dem diese Eingabe signiert werden soll 71 | @param k Zufalls-Zahl 72 | @param compressed Wenn true, wird der Pub-Key komprimiert. 73 | @return Das SigScript für diesen Tx-Eingang **/ 74 | public static byte[] sigScript(byte[] usigTx, byte[] privKey, byte[] k, boolean compressed, int index) 75 | { 76 | byte[] b1 = removeSigScript(usigTx, index); // Löscht alle SigScripts bis auf eine (index) aus der Tx 77 | byte[] sigHash = Calc.getHashSHA256(Calc.getHashSHA256(b1)); // Signatur-Hash wird gebildet 78 | // System.out.println("TxToSig "+index+": "+Convert.byteArrayToHexString(b1)); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Zum Debuggen 79 | // System.out.println("SigHash "+index+": "+Convert.byteArrayToHexString(sigHash)); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Zum Debuggen 80 | Secp256k1 secp = new Secp256k1(); 81 | BigInteger[] sig = secp.sig(sigHash, privKey, k); 82 | if(sig[1].compareTo(new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0",16)) > 0) // Y-Koordinate auf der Elliptischen Kurve muss immer auf den positiven Wert gesetzt werden. (Bip0062) 83 | { sig[1] = (new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",16)).subtract(sig[1]);} 84 | byte[] sigR = sig[0].toByteArray(); // Die fertige Signatur R 85 | byte[] sigS = sig[1].toByteArray(); // Die fertige Signatur S 86 | byte[] pub = Calc.getPublicKey(privKey, compressed); // wird der PublicKey aus dem PrivKey berechnet. 87 | // Das SigScript wird nun von hinten in umgekehrter Reihenfolge erstellt 88 | ByteArrayList out = new ByteArrayList(pub); // Public-Key wird eingefügt 89 | out.insert((byte)pub.length, 0); // Public-Key Länge wird eingefügt 90 | out.insert((byte)0x01, 0); // Hash-Code 0x01 wird eingefügt 91 | out.insert(sigS, 0); // Sig-S wird eingefügt 92 | out.insert((byte)sigS.length, 0); // Sig-S Länge wird eingefügt 93 | out.insert((byte)0x02, 0); // 0x02 wird eingefügt 94 | out.insert(sigR, 0); // Sig-R wird eingefügt 95 | out.insert((byte)sigR.length, 0); // Sig-R Länge wird eingefügt 96 | out.insert((byte)0x02, 0); // 0x02 wird eingefügt 97 | out.insert((byte)(sigR.length + sigS.length + 4), 0); // Sig-R Länge wird eingefügt 98 | out.insert((byte)0x30, 0); // 0x30 wird eingefügt 99 | out.insert((byte)(sigR.length + sigS.length + 7), 0); // Länge der Signatur ohne Pub-Key 100 | out.insert((byte)out.size(), 0); // Länge des gesamten Scriptes wird eingefügt. 101 | return out.getArrayAll(); 102 | } 103 | 104 | 105 | 106 | 107 | // Test: Mit 3 Inputs erfolgreich getestet! Methode funktioniert gut! 108 | /** Gibt den Signature-Hash der Legancy Transaktion zurück. Es können Signiert oder Unsignierte Transaktionen verwedet werden. 109 | Jeder Transaktions-Eingang muss einzeln signiert werden und es gibt deswegen auch für jede Tx-In einen anderen Signature-Hash! 110 | Um den Signature-Hash erstellen zu können ist das PK-Script der vorherigen Transaktion notwendig. 111 | @param tx unsignierte oder signierte Transakion. 112 | @param pkScript Das Pk-Script der vorherigen Tx auf die sich dieser Signature-Hash bezieht. 113 | @param txIndex Der Transaktions-Index der Tx-In dessen Signature-Hash berechnet werden soll. 114 | @return Signature-Hash dieser Transaktion für eine bestimmte Tx-In. 115 | Ist erfolgreich getestet für Standard-Transaktionen! 116 | 1. Alle Signaturen der Transaktion werden entfernt und durch (Compact-Size) 0x00 ersetzt. 117 | 2. Das übergebene PK-Script der vorherigen Transaktion wird an die (txIndex) gewünschte Stelle der Signature eingefügt. 118 | 3. Hash-Code 0x01000000 wird hinten angehängt 119 | 4. Dies entspricht dann der ursprünglichen unsignierten Transaktion und wird dann mit SHA256² gehascht. 120 | Keine Witness-Tx hier verwenden! Für Witness-Tx gibt es eine eigene Klasse: "TxSigniererWitness". 121 | Funktioniert auch NICHT für Legancy-Inputs die in Witness-Transaktionen eingebettet sind! **/ 122 | public static byte[] getSigHash(Transaktion tx, byte[] pkScript, int txIndex) throws Exception 123 | { 124 | if(tx.isWitness) throw new Exception("Witness transaction cannot be signed in Legancy Class!"); 125 | else 126 | { 127 | ByteArrayList list = new ByteArrayList(tx.getRawTx()); 128 | for(int i=tx.getTxInCount()-1; i>=0;i--) 129 | { 130 | int pos = tx.getSigScript_pos()[i]-1; 131 | list.remove(pos, pos + tx.getSigScript_len()[i]+1); 132 | if(i==txIndex) 133 | { 134 | list.insert(pkScript, pos); 135 | list.insert((byte)pkScript.length,pos); 136 | } 137 | else list.insert((byte)0x00, pos); 138 | } 139 | byte[] b = {0x01, 0x00, 0x00, 0x00}; 140 | list.add(b); 141 | byte[] uSigTx = list.getArrayAll(); 142 | // System.out.println("TxToSig "+txIndex+": "+Convert.byteArrayToHexString(uSigTx)); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Zum Debuggen 143 | return Calc.getHashSHA256(Calc.getHashSHA256(uSigTx)); 144 | } 145 | } 146 | 147 | 148 | 149 | 150 | 151 | // --------------------------------------------------------------------------------------------- Zusätzliche Methoden -------------------------------------------------------------- 152 | 153 | 154 | 155 | //Löscht alle SigScripts aus der Transaktion, bis auf eine Einzige, die in "notRemove" markiert wird. 156 | //Sollen alle gelöscht werden, dann im "notRemove" Feld -1 eintragen. 157 | private static byte[] removeSigScript(byte[] usigTx, int notRemove) 158 | { 159 | Transaktion uTx = new Transaktion(usigTx,0); 160 | byte[] tx2 = usigTx.clone(); 161 | ByteArrayList b = new ByteArrayList(tx2); 162 | for(int i = uTx.getTxInCount()-1; i>=0; i--) 163 | { 164 | if(i!=notRemove) 165 | { 166 | b.remove(uTx.getSigScript_pos()[i]-1, uTx.getSigScript_pos()[i]+uTx.getSigScript_len()[i]); 167 | b.insert((byte)0x00, uTx.getSigScript_pos()[i]-1); 168 | } 169 | } 170 | return b.getArrayAll(); 171 | } 172 | 173 | 174 | 175 | 176 | /** Sucht die richtigen Private-Keys aus der übergebenen Liste heraus und und gibt sie in der Reihenvolge zurück, wie sie zum signieren benötigt werden. 177 | Dabei werden alle übergebenen Private-Keys in allen möglichen Formaten getestet, ob sie für sie für einen Eingang dieser Tx gültig sind. (Brute-Forcing-Methode) 178 | Es können beliebig viele Priv-Test-Keys übergeben werden. Unter Beachtung der Laufzeit dieser Methode. 179 | Funktionsbeschreibung: Aus jedem Test-Priv-Key wird eine BTC-Adresse (Hash160) erstellt und mit dem PK-Script in der Unsignierten Tx verglichen. Bei Übereinstimmung, ist der Priv.Key richtig. 180 | Vorraussetztung ist, dass in der unsignierten Tx die PK-Scripte der vorherigen Tx vorhanden sind. (Temporär im Signatur-Feld) 181 | Da die übergebene PrivKey-Liste groß sein kann, sollte hier etwas auf die Laufzeit geachtet werden! 182 | @param usigTx Unsignierte Tx, aus der die Private-Keys gesucht werden sollen. 183 | @param privTestKeys Eine Liste von Private-Keys, beliebiger Länge, aus der die richtigen Priv.Keys gesucht werden. 184 | @param pref_PrivKey Die CoinParameter müssen übergeben werden, zur Identifizierung des jeweiligen Coin. Siehe CoinParameter Class. (Bitcoin-MainNet=0x80, Bitcoin-TestNet=0xEF) 185 | @param magic, TestNet oder MainNet (MAINNET = {(byte) 0xf9,(byte) 0xbe,(byte) 0xb4,(byte) 0xD9}; TESTNET3 = {(byte) 0x0b,(byte) 0x11,(byte) 0x09,(byte) 0x07};) 186 | @return Werden alle benötigten Priv-Keys gefunden, wird ein Array mit den richtigen Priv.keys, in der richtigen Reihenvolge und Länge, zum signieren zurückgegeben. 187 | @throws Exception Wird nur ein benötiger Priv.Key nicht gefunden, wird eine Exceptin geworfen. **/ 188 | public static byte[][] calcPrivKeyList(byte[] usigTx, byte[][] privTestKeys, byte[] pref_PrivKey, byte[] magic) throws Exception 189 | { 190 | Transaktion tx = new Transaktion(usigTx,0); 191 | byte[][] b_pk = tx.getSigScript(); // Die PK-Scripte, an der Stelle der späteren Sig-Scripte 192 | byte[][] out = new byte[b_pk.length][]; // Rückgabe-Array wird initialisiert 193 | for(int i=0; i\nPriv Hex: "+prvHex+"\nPriv WIF: "+prvWIF+"
Pub Key: "+pub+"\nHash160: "+h160+"\nAddress: "+addr+"\nExplorer: "+link+value+""); 126 | else GUI.txt_ausgabe.setText("
\nPriv Hex: ***\nPriv WIF: ***
Pub Key: " +pub+"\nHash160: "+h160+"\nAddress: "+addr+"\nExplorer: "+link+value+"
"); 127 | setQRCodes(prvWIF, addr); 128 | } 129 | 130 | 131 | 132 | 133 | 134 | 135 | static int v; 136 | 137 | private static void setQRCodes(String prvWIF, String addr) throws Exception 138 | { 139 | spinner.setEditor(new JSpinner.DefaultEditor(spinner)); 140 | spinner.setPreferredSize(new Dimension(16,16)); 141 | v = Integer.valueOf(spinner.getModel().getValue().toString()); 142 | GUI.panel_QRCode .add(spinner); 143 | 144 | qrCode1 = QRCodeZXING.writeQRCode(prvWIF, GUI.color1, new Color(120,0,0), 75, 75); 145 | qrCode2 = QRCodeZXING.writeQRCode(addr, GUI.color1, new Color(0,80,0), 75, 75); 146 | 147 | spinner.addChangeListener(new ChangeListener() 148 | { 149 | public void stateChanged(ChangeEvent e) 150 | { 151 | 152 | v = Integer.valueOf(spinner.getModel().getValue().toString()); 153 | privKey.setIcon(new ImageIcon(new ImageIcon(qrCode1).getImage().getScaledInstance(v, v, 2))); 154 | address.setIcon(new ImageIcon(new ImageIcon(qrCode2).getImage().getScaledInstance(v, v, 2))); 155 | } 156 | }); 157 | 158 | try 159 | { 160 | verifyQRCode(qrCode1, prvWIF); 161 | privKey = new JLabel(new ImageIcon(qrCode1)); 162 | privKey .setBackground(GUI.color1); 163 | privKey .setText("Private Key"); 164 | privKey .setOpaque(true); 165 | privKey.setVerticalTextPosition(SwingConstants.TOP); 166 | privKey.setHorizontalTextPosition(SwingConstants.CENTER); 167 | privKey.setIcon(new ImageIcon(new ImageIcon(qrCode1).getImage().getScaledInstance(v, v, 2))); 168 | 169 | } 170 | catch(Exception e) 171 | { 172 | privKey = new JLabel(e.getMessage()); 173 | privKey.setForeground(Color.red); 174 | } 175 | try 176 | { 177 | verifyQRCode(qrCode2, addr); 178 | address = new JLabel(new ImageIcon(qrCode2)); 179 | address .setBackground(GUI.color1); 180 | address .setText("Address"); 181 | address .setOpaque(true); 182 | address.setVerticalTextPosition(SwingConstants.TOP); 183 | address.setHorizontalTextPosition(SwingConstants.CENTER); 184 | address.setIcon(new ImageIcon(new ImageIcon(qrCode2).getImage().getScaledInstance(v, v, 2))); 185 | 186 | } 187 | catch(Exception e) 188 | { 189 | address = new JLabel(e.getMessage()); 190 | address.setForeground(Color.red); 191 | } 192 | Component strut5 = Box.createHorizontalStrut(10); 193 | Component strut6 = Box.createHorizontalStrut(20); 194 | strut5.setEnabled(false); 195 | strut6.setEnabled(false); 196 | privKey.setVisible(GUI.showPrivKeys.isSelected()); 197 | GUI.panel_QRCode.add(privKey); 198 | GUI.panel_QRCode.add(strut5); 199 | GUI.panel_QRCode.add(address); 200 | GUI.panel_QRCode.add(strut6); 201 | } 202 | 203 | 204 | 205 | // prüft den erstellten QR Code, indem das Bild gescannt wird und mit dem übergebenem String verglichen wird. 206 | // Sind beide nicht gleich, wird ein Fehler ausgegeben und der QR-Code darf nicht angezeigt werden! 207 | public static void verifyQRCode(BufferedImage image, String str) throws Exception 208 | { 209 | String qr = QRCodeZXING.readQRCode(image, false); 210 | if(qr.equals(str)==false) throw new Exception("QR-Code Error!"); 211 | } 212 | } -------------------------------------------------------------------------------- /src/CoinGen/Config.java: -------------------------------------------------------------------------------- 1 | package CoinGen; 2 | import java.awt.Dimension; 3 | import java.awt.Toolkit; 4 | import java.io.BufferedReader; 5 | import java.io.BufferedWriter; 6 | import java.io.File; 7 | import java.io.FileNotFoundException; 8 | import java.io.FileReader; 9 | import java.io.FileWriter; 10 | import javax.swing.DefaultComboBoxModel; 11 | import org.json.JSONArray; 12 | import org.json.JSONObject; 13 | 14 | import GUI.GUI; 15 | 16 | 17 | 18 | 19 | /*************************************************************************************************** 20 | * Läd und speichert die ConfigAddressGen.json Datei. * 21 | * Wenn die Datei nicht vorhanden ist, wird sie beim Programmende automatisch gespeichert. * 22 | * Diese Datei beinhaltet diverse Einstellungen und Zustände im Programm, außer die Eingabe Felder!* 23 | ***************************************************************************************************/ 24 | 25 | 26 | 27 | public class Config 28 | { 29 | 30 | final static String fileName = "ConfigAddressGen.json"; // Name der Configurations Datei für diese. Programm 31 | final static String dateiID = "ecab3c3cd7470bd5c43566a59b793fdeb820e73aa426b3e86cebe56020488349"; // Die dateiID dient zur Identifizierung dieser json Datei. 32 | 33 | 34 | 35 | /** Läd die Programm Status Einstellungen aus der Datei: "ConfigAddressGen.json" und schreibt sie in die Variablen. **/ 36 | public static void load() 37 | { 38 | File f = new File(fileName); 39 | if(f.exists()) 40 | { 41 | try 42 | { 43 | BufferedReader br = new BufferedReader(new FileReader(f)); 44 | String str = ""; 45 | while(br.ready()) str = str +br.readLine(); 46 | br.close(); 47 | JSONObject jo = new JSONObject(str); 48 | String dateiID = jo.getString("dateiID"); 49 | String version = jo.getString("version"); 50 | if(dateiID.equals(Config.dateiID)==false) throw new FileNotFoundException("The file "+fileName+" is an incorrect file!"); 51 | if(version.equals(GUI.version)==false) throw new FileNotFoundException("The file "+fileName+" has the wrong version!"); 52 | JSONArray array = jo.getJSONArray("coinList"); 53 | String[] list = new String[array.length()]; 54 | for(int i=0;i screenSize.width-855 || GUI.posX < 0) GUI.posX=0; 67 | if(GUI.posY > screenSize.height-340|| GUI.posY < 0) GUI.posY=0; 68 | GUI.frame.setLocation(GUI.posX, GUI.posY); 69 | } 70 | catch (Exception e) {e.printStackTrace();} 71 | } 72 | else {} 73 | } 74 | 75 | 76 | 77 | public static void save() 78 | { 79 | try 80 | { 81 | JSONObject jo = new JSONObject(); 82 | jo.put("dateiID", dateiID); 83 | jo.put("progName", GUI.progName); 84 | jo.put("version", GUI.version); 85 | jo.put("autor", GUI.autor); 86 | jo.put("posX", GUI.frame.getX()); 87 | jo.put("posY", GUI.frame.getY()); 88 | int size = GUI.comboBox_coin.getItemCount(); 89 | String[] list = new String[size]; 90 | for (int i=0;i 0) return NO_SUCH_PAGE; 56 | try 57 | { 58 | BufferedImage img = paintComponent(Print.print); 59 | Graphics2D g2 = (Graphics2D) graphics; 60 | g2.drawImage(img, Print.x, Print.y,Print.width,Print.height, null); 61 | //g2.drawImage(img, 120, 160,Print.gui.getWidth()-400,Print.gui.getHeight()-470, null); 62 | } 63 | catch (Exception e) {return NO_SUCH_PAGE;} 64 | return PAGE_EXISTS; 65 | } 66 | 67 | private static BufferedImage paintComponent(Component c) 68 | { 69 | BufferedImage img = new BufferedImage(c.getWidth(), c.getHeight(), BufferedImage.TYPE_INT_RGB); 70 | Graphics2D g = img.createGraphics(); 71 | c.paintAll(g); 72 | g.dispose(); 73 | return img; 74 | } 75 | } -------------------------------------------------------------------------------- /src/CoinGen/QRCodeZXING.java: -------------------------------------------------------------------------------- 1 | package CoinGen; 2 | import java.awt.Color; 3 | import java.awt.Graphics2D; 4 | import java.awt.Image; 5 | import java.awt.event.ComponentAdapter; 6 | import java.awt.event.ComponentEvent; 7 | import java.awt.image.BufferedImage; 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.IOException; 10 | import java.util.Base64; 11 | import javax.imageio.ImageIO; 12 | import javax.swing.ImageIcon; 13 | import javax.swing.JFrame; 14 | import javax.swing.JLabel; 15 | import com.google.zxing.BarcodeFormat; 16 | import com.google.zxing.BinaryBitmap; 17 | import com.google.zxing.ChecksumException; 18 | import com.google.zxing.FormatException; 19 | import com.google.zxing.NotFoundException; 20 | import com.google.zxing.RGBLuminanceSource; 21 | import com.google.zxing.Result; 22 | import com.google.zxing.WriterException; 23 | import com.google.zxing.common.BitMatrix; 24 | import com.google.zxing.common.HybridBinarizer; 25 | import com.google.zxing.qrcode.QRCodeReader; 26 | import com.google.zxing.qrcode.QRCodeWriter; 27 | 28 | 29 | 30 | /*************************************************************************** 31 | * V1.3 Mr. Maxwell 24.01.2024 * 32 | * Erzeugt einen QR-Code oder liest ein CR-Code von einem Image ein. * 33 | * Benötigt zxing.rar Bibliothek * 34 | ****************************************************************************/ 35 | 36 | 37 | 38 | public class QRCodeZXING 39 | { 40 | 41 | 42 | 43 | /** Öffnet ein JFrame (Ein neues Fenster) und zeigt den QR-Code an. 44 | Automatische Größenänderung des QR-Codes beim Verändern der Größe des Fensters 45 | @param data String aus dem der QR-Code erzeugt werden soll. 46 | @param title Tittel des JFrame Fensters (Kann null sein) 47 | @param color1 Hintergrundfarbe 48 | @param color2 Schriftfarbe 49 | @param x postion X 50 | @param y position Y **/ 51 | public static void printQR(String data, String title, Color color1, Color color2, int x, int y) throws Exception 52 | { 53 | BufferedImage bi = QRCodeZXING.writeQRCode(data, color1, color2, 1024, 1024); 54 | JLabel lbl = new JLabel(new ImageIcon(bi)); 55 | lbl.setBounds(0, 0, 1200, 1200); 56 | JFrame d = new JFrame(); 57 | d.setTitle(title); 58 | d.setBounds(x, y, 500, 520); 59 | d.add(lbl); 60 | d.setVisible(true); 61 | d.addComponentListener(new ComponentAdapter() 62 | { 63 | public void componentResized(ComponentEvent e) 64 | { 65 | int minSize = Integer.min(d.getWidth(), d.getHeight()); 66 | lbl.setIcon(new ImageIcon(new ImageIcon(bi).getImage().getScaledInstance(minSize, minSize, 2))); 67 | } 68 | }); 69 | } 70 | 71 | 72 | 73 | 74 | /** Erzeugt einen QR-Code aus einem Text-String. 75 | @param data String aus dem der QR-Code erzeugt werden soll. 76 | @param color1 Hintergrundfarbe 77 | @param color2 Schriftfarbe 78 | @param x Breite 79 | @param y Höhe 80 | @return base64-Code des PNG-QR Codes **/ 81 | public static String writeQRCodetoBase64(String data, Color color1, Color color2, int x, int y) throws IOException 82 | { 83 | BufferedImage bi = QRCodeZXING.writeQRCode(data, color1, color2, x, y); 84 | ByteArrayOutputStream bo = new ByteArrayOutputStream (); 85 | ImageIO.write(bi, "png", bo); 86 | byte[] b = bo.toByteArray(); 87 | bo.close(); 88 | return new String(Base64.getEncoder().encode(b)); 89 | } 90 | 91 | 92 | 93 | /** Erzeugt einen QR-Code aus einem Text-String. 94 | @param data String aus dem der QR-Code erzeugt werden soll. 95 | @param color1 Hintergrundfarbe 96 | @param color2 Schriftfarbe 97 | @param x Breite 98 | @param y Höhe 99 | @return Byte-Array des PNG-QR Codes **/ 100 | public static byte[] writeQRCodetoByteArray(String data, Color color1, Color color2, int x, int y) throws IOException 101 | { 102 | BufferedImage bi = QRCodeZXING.writeQRCode(data, color1, color2, x, y); 103 | ByteArrayOutputStream bo = new ByteArrayOutputStream (); 104 | ImageIO.write(bi, "png", bo); 105 | byte[] b = bo.toByteArray(); 106 | bo.close(); 107 | return b; 108 | } 109 | 110 | 111 | 112 | /** Erzeugt einen QR-Code aus einem Text-String. 113 | @param data String aus dem der QR-Code erzeugt werden soll. 114 | @param color1 Hintergrundfarbe 115 | @param color2 Schriftfarbe 116 | @param x Breite 117 | @param y Höhe 118 | @return Das Bild mit dem QR-Code **/ 119 | public static BufferedImage writeQRCode(String data, Color color1, Color color2, int x, int y) 120 | { 121 | QRCodeWriter writer = new QRCodeWriter(); 122 | BufferedImage image = new BufferedImage(x, y, BufferedImage.TYPE_INT_RGB); 123 | int white = color1.getRed() << 16 | color1.getGreen() << 8 | color1.getBlue(); 124 | int black = color2.getRed() << 16 | color2.getGreen() << 8 | color2.getBlue(); 125 | try 126 | { 127 | BitMatrix bitMatrix = writer.encode(data, BarcodeFormat.QR_CODE, x+20, y+20); 128 | for (int i = 0; i < x; i++) 129 | { 130 | for (int j = 0; j < y; j++) image.setRGB(i, j, bitMatrix.get(i+10, j+10) ? black : white); 131 | } 132 | } 133 | catch (WriterException e) {e.printStackTrace();} 134 | return image; 135 | } 136 | 137 | 138 | 139 | /** Scannt einen QR-Code von einem Image Bild. 140 | @param image Übergeben wird ein Image mit dem QR-Code 141 | @param rotate muss "false" sein, wird von der Methode selbst verwendet. 142 | @return Der String, der die Daten aus dem QR-Code enthällt **/ 143 | public static String readQRCode(BufferedImage image, boolean rotate) 144 | { 145 | Image tmp = image.getScaledInstance(100, 100, Image.SCALE_SMOOTH); 146 | BufferedImage dimg = new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB); 147 | Graphics2D g2d = dimg.createGraphics(); 148 | g2d.drawImage(tmp, 0, 0, null); 149 | g2d.dispose(); 150 | image = dimg; 151 | int width = image.getWidth(); 152 | int heigth = image.getHeight(); 153 | int[] pixels = image.getRGB(0,0, width, heigth, null, 0, width); 154 | RGBLuminanceSource source = new RGBLuminanceSource(width, heigth, pixels); 155 | BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); 156 | QRCodeReader reader = new QRCodeReader(); 157 | if(rotate) 158 | { 159 | try {bitmap.getBlackMatrix().rotate180();} 160 | catch (NotFoundException e1) {e1.printStackTrace();} 161 | } 162 | try 163 | { 164 | Result result = reader.decode(bitmap); 165 | return result.getText(); 166 | } 167 | catch (NotFoundException e) 168 | { 169 | if(rotate==false) return readQRCode(image,true); 170 | else e.printStackTrace(); return "QR code could not be read"; 171 | } 172 | catch (ChecksumException e) 173 | { 174 | if(rotate==false) return readQRCode(image,true); 175 | else e.printStackTrace(); return "Checksum Error"; 176 | } 177 | catch (FormatException e) 178 | { 179 | if(rotate==false) return readQRCode(image,true); 180 | else e.printStackTrace(); return "Format Error"; 181 | } 182 | } 183 | } -------------------------------------------------------------------------------- /src/CoinGen/QrCapture.java: -------------------------------------------------------------------------------- 1 | package CoinGen; 2 | import java.awt.FlowLayout; 3 | import java.awt.Rectangle; 4 | import java.awt.event.WindowAdapter; 5 | import java.awt.event.WindowEvent; 6 | import java.awt.image.BufferedImage; 7 | import java.io.Closeable; 8 | import java.io.IOException; 9 | import java.util.concurrent.Exchanger; 10 | import java.util.concurrent.TimeUnit; 11 | import java.util.concurrent.TimeoutException; 12 | import javax.swing.JDialog; 13 | import javax.swing.JFrame; 14 | import com.github.sarxos.webcam.Webcam; 15 | import com.github.sarxos.webcam.WebcamPanel; 16 | import com.github.sarxos.webcam.WebcamResolution; 17 | import com.google.zxing.BinaryBitmap; 18 | import com.google.zxing.MultiFormatReader; 19 | import com.google.zxing.NotFoundException; 20 | import com.google.zxing.Result; 21 | import com.google.zxing.client.j2se.BufferedImageLuminanceSource; 22 | import com.google.zxing.common.HybridBinarizer; 23 | 24 | 25 | 26 | /*************************************************************************************************************** 27 | * V1.2 optimiert Mr. Maxwell 23.01.2024 * 28 | * Angepasste Verion die von JDialog aufgerufen werden kann. JDialog muss dem Konstruktor übergeben werden. * 29 | * Diese Klasse ist von https://github.com/sarxos/webcam-capture * 30 | * Mit dieser Klasse kann ein QR-Code mit der Kamera gescannt und ausgegeben werden. * 31 | * Es wird automatisch die Kamera gestartet und ein Fenster geöffnet, welches das live Kamera-Bild anzeigt. * 32 | * Nun muss mit der Kamera ein QR-Code gescannt werden. * 33 | * Nach erfolgreichem Scan, schleißt die Kamera und der QR-Code wird als String zurück gegeben. * 34 | * Achtung: die Methode getResult() blockiert den Thread bis ein Ergebnis kommt! * 35 | * Diese Klasse kann auch als JDialog implementiert werden. (Mit Einschränkungen) * 36 | * Einschränkung: Die Methode setModal(true); Darf hier nicht aufgerufen werden! Führt zum Chrash der Kamera! * 37 | * Die Blockierung der Übergeordenten Fenster muss daher manuell erfolgen! * 38 | * Beispiel zur Anwendung: * 39 | * QrCapture qr = new QrCapture(); * 40 | * String str = qr.getResult(); * 41 | * qr.close(); * 42 | * in "str" ist dann der String des QR-Codes enthalten. * 43 | * * 44 | * Benötiget Bibliotheken: * 45 | * - webcam-capture-smal-0.3.12.jar Kamera-API´s (wurde von mir angepasst: weitere libs, daraus entfernt) * 46 | * - bridj-0.7.0.jar Wird von webcam-Capture selbst benötigt * 47 | * - zxing.jar QR-Code API * 48 | ****************************************************************************************************************/ 49 | 50 | 51 | 52 | public class QrCapture extends JDialog implements Closeable 53 | { 54 | 55 | private Webcam webcam = null; 56 | private BufferedImage image = null; 57 | private Result result = null; 58 | private Exchanger exchanger = new Exchanger(); 59 | 60 | 61 | /** Konstruktor starte die Kamera und scannt den QR-Code 62 | @param x Position des Frames 63 | @param y Position des Frames */ 64 | public QrCapture(JDialog owner, int x, int y) throws IOException 65 | { 66 | super(owner); 67 | setBounds(new Rectangle(x, y, 250, 250)); 68 | getContentPane().setLayout(new FlowLayout()); 69 | setTitle("Capture"); 70 | setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 71 | webcam = Webcam.getDefault(); 72 | if(webcam==null) throw new IOException("no camera found"); 73 | webcam.setViewSize(WebcamResolution.QVGA.getSize()); 74 | try {webcam.open();} 75 | catch(Exception e){throw new IOException("Camera Error! Check permission");} 76 | getContentPane().add(new WebcamPanel(webcam)); 77 | pack(); 78 | setVisible(true); 79 | 80 | // Wenn das Kamera Fenster durch Abbruch geschlossen wird 81 | addWindowListener(new WindowAdapter() 82 | { 83 | @Override 84 | public void windowClosed(WindowEvent e) 85 | { 86 | try {exchanger.exchange("",1,TimeUnit.MICROSECONDS);} // Hinzugefügt, da es sonnst zu einem Thread-Blocking kommt 87 | catch (InterruptedException e1) {e1.printStackTrace();} 88 | catch (TimeoutException e1) {} 89 | close(); 90 | } 91 | }); 92 | 93 | // Aufnahme Endlos-Schleife 94 | final Thread daemon = new Thread(new Runnable() 95 | { 96 | @Override 97 | public void run() 98 | { 99 | while (isVisible()) read(); 100 | } 101 | }); 102 | daemon.setDaemon(true); 103 | daemon.start(); 104 | } 105 | 106 | 107 | // Wartet hier auf ein Ergebnis 108 | // Achtung, blockiert den Thread, bis ein Ergebnis kommt. !!! 109 | public String getResult() throws InterruptedException 110 | { 111 | return exchanger.exchange(null); 112 | } 113 | 114 | 115 | @Override 116 | public void close() 117 | { 118 | webcam.close(); 119 | } 120 | 121 | 122 | 123 | //------------------------------------------ Private Methoden -------------------------------------------- 124 | 125 | // Diese Methode wird in einer Endlosschleife solange wiederholt bis ein Scan erfolgreich ist oder abgebrochen wird. 126 | private void read() 127 | { 128 | if (!webcam.isOpen()) return; 129 | if ((image = webcam.getImage()) == null) return; 130 | try 131 | { 132 | BinaryBitmap bb = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(image))); 133 | result = new MultiFormatReader().decode(bb); 134 | } 135 | catch (NotFoundException e) {return; } 136 | if (result != null) 137 | { 138 | try {exchanger.exchange(result.getText());} 139 | catch (InterruptedException e) {return;} 140 | finally {dispose();} 141 | } 142 | } 143 | } -------------------------------------------------------------------------------- /src/CoinGen/Web.java: -------------------------------------------------------------------------------- 1 | package CoinGen; 2 | import java.io.BufferedReader; 3 | import java.io.InputStream; 4 | import java.io.InputStreamReader; 5 | import java.net.URL; 6 | import java.nio.charset.Charset; 7 | import org.json.JSONObject; 8 | 9 | 10 | 11 | /*********************************************************************************************************** 12 | * Version 0.2 Autor: Mr. Maxwell vom 29.11.2022 * 13 | * Kommunikation mit Webseiten * 14 | * Statische Klasse mit verschiedenen Methoden * 15 | * Info: Die Verbindung mit dem Web dient dazu den BTC-Betrag von generieren Adressen schnell anzuzeigen. * 16 | * Und per Link schnell die Adresse in einem Blockexplorer aufzurufen. * 17 | * Dies erleichtert das finden von Beträgen und wird zu u.A. zu testzwecken verwendet. * 18 | * Der CoinAdressGenerator sollte im regulärem Betrieb nur auf Offline-PC´s verwendet werden! * 19 | *************************************************************************************************************/ 20 | 21 | 22 | 23 | public class Web 24 | { 25 | 26 | 27 | 28 | /** @param coinAddress Die Coin-Adresse als String 29 | @param coinParameterSymbol Es wird das Symbol als String aus der CoinParameter-Klasse übergeben, um den jeweiligen Coin auszuwählen 30 | @return Gibt den Link als String zu einer Adresse in einem Blockexprorer des gewünschten Coins zurück. **/ 31 | public static String getLink(String coinAddress, String coinParameterSymbol) 32 | { 33 | switch(coinParameterSymbol) 34 | { 35 | case "BTC" : return "https://www.blockchain.com/btc/address/" + coinAddress; 36 | case "BTC-T" : return "https://live.blockcypher.com/btc-testnet/address/"+ coinAddress; 37 | case "BCH" : return "https://blockchair.com/bitcoin-cash/address/" + coinAddress; 38 | case "BCH-T" : return "https://explorer.bitcoin.com/tbch/address/" + coinAddress; 39 | case "LTC" : return "https://blockchair.com/litecoin/address/" + coinAddress; 40 | case "LTC-T" : return "https://blockexplorer.one/ltc/testnet/address/" + coinAddress; 41 | case "BSV" : return "https://blockchair.com/bitcoin-sv/address/" + coinAddress; 42 | case "BSV-T" : return "https://testnet.bitcoincloud.net/address/" + coinAddress; 43 | case "DOGE" : return "https://live.blockcypher.com/doge/address/" + coinAddress; 44 | case "DASH" : return "https://live.blockcypher.com/dash/address/" + coinAddress; 45 | 46 | } 47 | return ""; 48 | } 49 | 50 | 51 | 52 | 53 | 54 | /**Gibt das Guthaben einer Coin-Adresse online aus der blockcypher.com API zurück 55 | //@param coinAddress Die Coin-Adresse als String 56 | //@param coinParameterSymbol Es wird das Symbol aus der CoinParameter-Klasse übergeben um den jeweiligen Coin auszuwählen (z.B. "BTC" für Bitcoin) 57 | //@return Gibt den aktuellen Betrag des entsprechenden Coin als double zurück. **/ 58 | public static double getValue(String coinAddress, String coinParameterSymbol) 59 | { 60 | try 61 | { 62 | switch(coinParameterSymbol) 63 | { 64 | case "BTC" : {return getJSON("https://api.blockcypher.com/v1/btc/main/addrs/" +coinAddress+"/balance").getDouble("balance");} 65 | case "BTC-T": {return getJSON("https://api.blockcypher.com/v1/btc/test3/addrs/"+coinAddress+"/balance").getDouble("balance");} 66 | case "LTC" : {return getJSON("https://api.blockcypher.com/v1/ltc/main/addrs/" +coinAddress+"/balance").getDouble("balance");} 67 | case "DOGE" : {return getJSON("https://api.blockcypher.com/v1/doge/main/addrs/"+coinAddress+"/balance").getDouble("balance");} 68 | case "DASH" : {return getJSON("https://api.blockcypher.com/v1/dash/main/addrs/"+coinAddress+"/balance").getDouble("balance");} 69 | } 70 | } 71 | catch(Exception e) {} 72 | return -1; 73 | } 74 | 75 | 76 | 77 | // Blockchain.info geht nicht mehr 78 | ///**Gibt das Guthaben einer Coin-Adresse online aus der Blockchain.info API zurück 79 | //@param coinAddress Die Coin-Adresse als String 80 | //@param coinParameterSymbol Es wird das Symbol aus der CoinParameter-Klasse übergeben um den jeweiligen Coin auszuwählen (z.B. "BTC" für Bitcoin) 81 | //@return Gibt den aktuellen Betrag des entsprechenden Coin als double zurück. **/ 82 | //public static double getValue(String coinAddress, String coinParameterSymbol) 83 | //{ 84 | // try 85 | // { 86 | // switch(coinParameterSymbol) 87 | // { 88 | // case "BTC" : {return getJSON("https://blockchain.info/balance?active="+coinAddress).getJSONObject(coinAddress).getDouble("final_balance");} 89 | // } 90 | // } 91 | // catch(Exception e) {} 92 | // return -1; 93 | //} 94 | 95 | 96 | 97 | 98 | 99 | 100 | //** Blockchair geht nicht mehr! 101 | //Gibt das Guthaben einer Coin-Adresse aus dem Web zurück 102 | //@param coinAddress Die Coin-Adresse als String 103 | //@param coinParameterSymbol Es wird das Symbol aus der CoinParameter-Klasse übergeben um den jeweiligen Coin auszuwählen (z.B. "BTC" für Bitcoin) 104 | //@return Gibt den aktuellen Betrag des entsprechenden Coin als double zurück. **/ 105 | //public static double getValue(String coinAddress, String coinParameterSymbol) 106 | //{ 107 | // try 108 | // { 109 | // switch(coinParameterSymbol) 110 | // { 111 | // case "BTC" : {return getJSON("https://api.blockchair.com/bitcoin/dashboards/address/"+ coinAddress) .getJSONObject("data").getJSONObject(coinAddress).getJSONObject("address").getDouble("balance");} 112 | // case "BTC-T": {return getJSON("https://api.blockchair.com/bitcoin/testnet/dashboards/address/"+coinAddress).getJSONObject("data").getJSONObject(coinAddress).getJSONObject("address").getDouble("balance");} 113 | // case "BCH" : {return getJSON("https://api.blockchair.com/bitcoin-cash/dashboards/address/"+coinAddress) .getJSONObject("data").getJSONObject(coinAddress).getJSONObject("address").getDouble("balance");} 114 | // case "LTC" : {return getJSON("https://api.blockchair.com/litecoin/dashboards/address/"+coinAddress) .getJSONObject("data").getJSONObject(coinAddress).getJSONObject("address").getDouble("balance");} 115 | // case "BSV" : {return getJSON("https://api.blockchair.com/bitcoin-sv/dashboards/address/"+coinAddress) .getJSONObject("data").getJSONObject(coinAddress).getJSONObject("address").getDouble("balance");} 116 | // } 117 | // } 118 | // catch(Exception e) {} 119 | // return -1; 120 | //} 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | /** @param coinAddress Die Coin-Adresse als String 129 | @param coinParameterSymbol Es wird das Symbol als String aus der CoinParameter-Klasse übergeben, um den jeweiligen Coin auszuwählen 130 | @return Gibt den Link in (konvertiert in HTML-Form) als String zu einer Adresse in einem Blockexprorer des gewünschten Coins zurück. **/ 131 | public static String getLinkHTML(String coinAddress, String coinParameterSymbol) 132 | { 133 | String str = getLink(coinAddress,coinParameterSymbol); 134 | if(str.equals("")) return ""; 135 | else return ""+coinAddress+""; 136 | } 137 | 138 | 139 | 140 | /** Gibt eine JSON Webseite als JSONObject zurück. 141 | @param url Die URL der Webseite als String 142 | @return JSON-Object des gesamten Inhaltes **/ 143 | public static JSONObject getJSON(String url) throws Exception 144 | { 145 | InputStream is = new URL(url).openStream(); 146 | BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8"))); 147 | StringBuilder sb = new StringBuilder(); 148 | while(rd.ready()) sb.append(rd.readLine()); 149 | rd.close(); 150 | JSONObject json = new JSONObject(sb.toString()); 151 | return json; 152 | } 153 | } -------------------------------------------------------------------------------- /src/ECDSA/Math_Modulo.java: -------------------------------------------------------------------------------- 1 | package ECDSA; 2 | import java.math.*; 3 | 4 | 5 | 6 | /*************************************************************************************** 7 | * Math_Modulo Class V1.1 Autor: Mr. Maxwell 05.11.2019 * 8 | * Hier werden mathematische Berechnungen definiert über den Zahlenraum Modulo "p" * 9 | ****************************************************************************************/ 10 | 11 | 12 | 13 | public class Math_Modulo 14 | { 15 | 16 | final static BigInteger p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F",16); // Bitcoin Modulo: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F 17 | final static BigInteger GENERATOR = new BigInteger("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",16); // Bitcoin Generator Punkt G = 02 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798 18 | final static BigInteger GENERATORY = new BigInteger("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8",16); // Generator Y-Koordinate 19 | final static BigInteger ORDNUNG = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",16); // Ordnung n von G: (Modulo) 20 | //final static BigInteger HALB = new BigInteger("7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1",16); // Zahl 1/2, mit dem durch 2 dividiert werden kann. 21 | 22 | static BigInteger ZERO = new BigInteger("0"); 23 | static BigInteger ONE = new BigInteger("1"); 24 | static BigInteger TWO = new BigInteger("2"); 25 | static BigInteger THREE= new BigInteger("3"); 26 | static BigInteger FOUR = new BigInteger("4"); 27 | static BigInteger FIVE = new BigInteger("5"); 28 | static BigInteger SIX = new BigInteger("6"); 29 | static BigInteger SEVEN= new BigInteger("7"); 30 | 31 | 32 | 33 | 34 | /** Addiert a + b 2,3µs */ 35 | public static BigInteger add(BigInteger a, BigInteger b) 36 | { 37 | return a.add(b).mod(p); 38 | } 39 | public static BigInteger add(BigInteger a, BigInteger b, BigInteger c) 40 | { 41 | return (a.add(b).add(c)).mod(p); 42 | } 43 | public static BigInteger add(BigInteger a, BigInteger b, BigInteger c, BigInteger d) 44 | { 45 | return add(add(a,b),add(c,d)); 46 | } 47 | 48 | 49 | 50 | /** negiert -a 0,9µs */ 51 | public static BigInteger neg(BigInteger a) 52 | { 53 | return p.subtract(a); 54 | } 55 | 56 | /** Subtrahiert a-b */ 57 | public static BigInteger sub(BigInteger a, BigInteger b) 58 | { 59 | return add(a,neg(b)); 60 | } 61 | 62 | 63 | 64 | /** Multipliziert a * b 7µs */ 65 | public static BigInteger mul(BigInteger a, BigInteger b) 66 | { 67 | return a.multiply(b).mod(p); 68 | } 69 | public static BigInteger mul(BigInteger a, BigInteger b, BigInteger c) 70 | { 71 | return a.multiply(b).multiply(c).mod(p); 72 | } 73 | public static BigInteger mul(BigInteger a, BigInteger b, BigInteger c, BigInteger d) 74 | { 75 | return mul(mul(a,b),mul(c,d).mod(p)); 76 | } 77 | 78 | 79 | 80 | /** dividiert a/b */ 81 | public static BigInteger div(BigInteger a, BigInteger b) 82 | { 83 | return mul(a,inv(b)); 84 | } 85 | 86 | 87 | 88 | /** Liefert 1/a 45µs */ 89 | static BigInteger inv(BigInteger a) 90 | { 91 | return a.modInverse(p); 92 | } 93 | 94 | 95 | 96 | /** Diese Funktion berechnet die Zahl 1/2 mit der auf der elliptischen Kurve durch zwei geteilt werden kann. */ 97 | static BigInteger calcHalb(BigInteger a) 98 | { 99 | return a.modInverse(ORDNUNG); 100 | } 101 | 102 | 103 | 104 | /** Potenz x^n (sehr langsam!) */ 105 | static BigInteger pow(BigInteger x, BigInteger n) 106 | { 107 | return x.modPow(n,p); 108 | } 109 | 110 | 111 | 112 | /** Wurzel sqrt(a) Tonelli–Shanks Algorithmus */ 113 | public static BigInteger sqrt(BigInteger n) 114 | { 115 | BigInteger s = ZERO; 116 | BigInteger q = p.subtract(ONE); 117 | while (q.and(ONE).equals(ZERO)) { q=q.divide(TWO); s=s.add(ONE); } 118 | if (s.equals(ONE)) 119 | { 120 | BigInteger r = pow(n, (p.add(ONE)).divide(FOUR)); 121 | if (r.multiply(r).mod(p).equals(n)) return abs(r); 122 | {System.out.println("\nFehler! sqrt("+n+") existiert nicht! (Math_Modulo.sqrt) \n"); return ZERO;} 123 | } 124 | 125 | // Find the first quadratic non-residue z by brute-force search 126 | BigInteger z = ZERO; 127 | while (pow(z=z.add(ONE), (p.subtract(ONE)).divide(TWO)).equals(p.subtract(ONE))== false); 128 | BigInteger c = pow(z, q); 129 | BigInteger r = pow(n, q.add(ONE).divide(TWO)); 130 | BigInteger t = pow(n, q); 131 | BigInteger m = s; 132 | while (t.equals(ONE)==false) 133 | { 134 | BigInteger tt = t; 135 | BigInteger i = ZERO; 136 | while (tt.equals(ONE)==false) 137 | { 138 | tt = (tt.multiply(tt).mod(p)); 139 | i=i.add(ONE); 140 | if (i.equals(m)) 141 | { 142 | if(n.equals(ZERO)==false) System.out.println("\nFehler! sqrt("+n+") existiert nicht! (Math_Modulo.sqrt) \n"); 143 | return ZERO; 144 | } 145 | } 146 | BigInteger b = pow(c, pow(TWO, m.subtract(i).subtract(ONE))); 147 | BigInteger b2 = b.multiply(b).mod(p); 148 | r = r.multiply(b).mod(p); 149 | t = t.multiply(b2).mod(p); 150 | c = b2; 151 | m = i; 152 | } 153 | if (r.multiply(r).mod(p).equals(n)) return abs(r); 154 | return ZERO; 155 | } 156 | 157 | 158 | // liefer den Betrag |a| 159 | private static BigInteger abs(BigInteger a) 160 | { 161 | if(a.compareTo(p.subtract(ONE).divide(TWO)) == 1) return neg(a); 162 | return a; 163 | } 164 | } -------------------------------------------------------------------------------- /src/ECDSA/Secp256k1.java: -------------------------------------------------------------------------------- 1 | package ECDSA; 2 | import java.math.BigInteger; 3 | import java.util.Arrays; 4 | 5 | import BTClib3001.Convert; 6 | 7 | 8 | 9 | 10 | /******************************************************************************************** 11 | * Secp256k1 V2.5 Autor: Mr. Maxwell 31.01.2023 * 12 | * - Multipliziert einen Faktor mit einem Punkt auf der elliptischen Kurve. * 13 | * - Generiert den Pub.Key durch die Multiplikation von "G" mit dem Priv.Key. * 14 | * - Erzeugt ECDSA Signatur * 15 | * - Verifiziert ECDSA Signatur * 16 | ********************************************************************************************/ 17 | 18 | 19 | 20 | public class Secp256k1 21 | { 22 | public final static BigInteger ModuloHalb = new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFE17",16); 23 | public final static BigInteger GENERATOR = new BigInteger("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",16); 24 | public final static BigInteger GENERATORY = new BigInteger("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8",16); 25 | public final static BigInteger ORDNUNG = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",16); 26 | public final static BigInteger HALB = new BigInteger("7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1",16); 27 | 28 | public final static BigInteger ZERO = new BigInteger("0"); 29 | public final static BigInteger ONE = new BigInteger("1"); 30 | public final static BigInteger TWO = new BigInteger("2"); 31 | public final static BigInteger THREE= new BigInteger("3"); 32 | public final static BigInteger FOUR = new BigInteger("4"); 33 | public final static BigInteger FIVE = new BigInteger("5"); 34 | public final static BigInteger SIX = new BigInteger("6"); 35 | public final static BigInteger SEVEN= new BigInteger("7"); 36 | 37 | 38 | 39 | 40 | /** Mit dem Konstruktor wird die EXP-List initialisiert. 41 | Dies ist für die Multiplikation mit "G" notwendig! **/ 42 | public Secp256k1() 43 | { 44 | EXPList.set_EXP_List(); 45 | } 46 | 47 | 48 | 49 | /** Es wird eine Signatur erstellt bestehend aus den Teilen "r" und "s". 50 | Übergeben wird der 32byte lange Hash, der signiert werden soll, 51 | - der Priv.Key 32Byte, 52 | - die "rand" Zufallszahl "k" als ByteArray. 53 | Rückgabe ist ein BigInteger-Array bestehend aus 2 Elementen: [0] = r und [1] = s. 54 | Achtung: Die "rand" Zufallszahl "k" muss aus einer kryptographisch starken Entropie stammen! 55 | Falls "k" vorhersehbar ist, kann der Priv.Key leicht aufgedeckt werden!!! */ 56 | public BigInteger[] sig(byte[] hash, byte[] privKey, byte[] k) 57 | { 58 | byte[] ran = to_fixLength(k,32); 59 | if(ran[0]<0) 60 | { 61 | ran = Arrays.copyOf(ran, 31); 62 | ran = to_fixLength(ran,32); 63 | } 64 | BigInteger rand = new BigInteger(1,ran); 65 | BigInteger[] out= new BigInteger[2]; 66 | BigInteger r = multiply_G(rand)[0]; 67 | BigInteger r_x_priv = r.multiply(new BigInteger(1,privKey)).mod(ORDNUNG); 68 | BigInteger zähler = (new BigInteger(1,hash).add(r_x_priv)).mod(ORDNUNG); 69 | BigInteger k_inverse= rand.modInverse(ORDNUNG); 70 | out[0] = r; 71 | out[1] = k_inverse.multiply(zähler).mod(ORDNUNG); 72 | return out; 73 | } 74 | 75 | 76 | 77 | /** Die Signatur "r" und "s" wird geprüft. 78 | - Übergeben wird der 32byte lange Hash, dessen Signatur geprüft werden soll, 79 | - die Signatur selbst "sig" als BigInteger-Array bestehend aus 2 Elementen: [0] = r und [1] = s. 80 | - und der Pub.Key als BigInteger Array mit 2 Elementen.*/ 81 | public boolean verify(byte[] hash, BigInteger[] sig, BigInteger[] pub) 82 | { 83 | BigInteger h = new BigInteger(1,hash).mod(ORDNUNG); 84 | BigInteger s_invers = sig[1].modInverse(ORDNUNG); 85 | BigInteger[] arg1 = multiply_G(h.multiply(s_invers).mod(ORDNUNG)); 86 | BigInteger[] arg2 = multiply_Point(pub,sig[0].multiply(s_invers).mod(ORDNUNG)); 87 | BigInteger[] arg3 = addition(arg1,arg2); 88 | if(arg3[0].equals(sig[0])) return true; 89 | else return false; 90 | } 91 | 92 | 93 | 94 | /** Multipliziert den Generator mit dem "factor" auf der elliptischen Kurve. 95 | Schnelle Berechnung mit Hilfe der EXP_List. ca. 3ms */ 96 | public BigInteger[] multiply_G(BigInteger factor) 97 | { 98 | BigInteger[] voher = EXPList.nullVektor; 99 | BigInteger[] erg = new BigInteger[2]; 100 | for(int i=0;i<=255;i++) 101 | { 102 | if(factor.testBit(i)) 103 | { 104 | erg = addition(voher,EXPList.list[i]); 105 | voher = erg; 106 | } 107 | } 108 | return erg; 109 | } 110 | 111 | 112 | 113 | /** Multipliziert einen eigenen Punkt "point" mit "factor" auf der elliptischen Kurve. 114 | Rekursive Funktion, sehr rechenintensiv und daher sehr langsam! */ 115 | public static BigInteger[] multiply_Point(BigInteger[] point, BigInteger factor) 116 | { 117 | BigInteger[] erg = point; 118 | BigInteger[] NULL= new BigInteger[2]; 119 | NULL[0] = ZERO; 120 | NULL[1] = ZERO; 121 | if(factor.equals(ZERO)) return NULL; 122 | if(factor.equals(ONE)) return erg; 123 | if(factor.equals(TWO)) return multiply_2(erg); 124 | if(factor.equals(THREE)) return addition(multiply_2(erg),erg); 125 | if(factor.equals(FOUR)) return multiply_2(multiply_2(erg)); 126 | if(factor.compareTo(FOUR)==1); 127 | { 128 | int exp = factor.bitLength()-1; 129 | for(;exp >0;exp--)erg = multiply_2(erg); 130 | factor = factor.clearBit(factor.bitLength()-1); 131 | erg = addition(multiply_Point(point,factor),erg); 132 | } 133 | return erg; 134 | } 135 | 136 | 137 | 138 | 139 | 140 | // Multiplikation auf der elliptischen Kurve mit 2 (Nur zur Vorberechnung, nicht zur Laufzeit anwenden!) m = (3*P[0]²)/(2*sqrt(P[0]²+7)) 141 | // n = P[1] - m*P[0]; 142 | // erg[0] = m² - 2*P[0] 143 | // erg[1] = -(m*erg[0] + n) 144 | private static BigInteger[] multiply_2(BigInteger[] P) 145 | { 146 | BigInteger[] erg = new BigInteger[2]; 147 | BigInteger m = Math_Modulo.div(Math_Modulo.mul(THREE,Math_Modulo.pow(P[0],TWO)) , Math_Modulo.mul(TWO , Math_Modulo.sqrt(Math_Modulo.add(Math_Modulo.pow(P[0],THREE),SEVEN)))); 148 | if(P[1].compareTo(ModuloHalb)==1) m = Math_Modulo.neg(m); 149 | BigInteger n = Math_Modulo.sub(P[1] , Math_Modulo.mul(m,P[0])); 150 | erg[0] = Math_Modulo.sub(Math_Modulo.pow(m,TWO) , Math_Modulo.mul(TWO,P[0])); 151 | erg[1] = Math_Modulo.neg(Math_Modulo.add(Math_Modulo.mul(m,erg[0]) , n)); 152 | return erg; 153 | } 154 | 155 | 156 | 157 | /** Addiert ein Punkt P mit dem Punkt Q auf der elliptischen Kurve. 158 | m = (Q[1]-P[1])/(Q[0]-P[0]) 159 | n = P[1] - m*P[0]; 160 | x = m² - x1 -x2 161 | y = -(m*x + n) */ 162 | public static BigInteger[] addition(BigInteger[] po1, BigInteger[] po2) 163 | { 164 | BigInteger[] nullVektor = new BigInteger[2]; 165 | nullVektor[0] = new BigInteger("0",16); nullVektor[1] = new BigInteger("0",16); 166 | if(po1[0].equals(ZERO) && po1[1].equals(ZERO)) return po2; 167 | if(po2[0].equals(ZERO) && po2[1].equals(ZERO)) return po1; 168 | if(po2[1].equals(po1[1])) return multiply_2(po1); 169 | else if(po2[0].equals(po1[0])) return nullVektor; 170 | BigInteger[] erg = new BigInteger[2]; 171 | BigInteger m = Math_Modulo.div(Math_Modulo.sub(po2[1],po1[1]) , Math_Modulo.sub(po2[0],po1[0])); 172 | BigInteger n = Math_Modulo.sub(po1[1] , Math_Modulo.mul(m,po1[0])); 173 | erg[0] = Math_Modulo.sub(Math_Modulo.sub(Math_Modulo.mul(m,m) ,(po1[0])) , (po2[0])); 174 | erg[1] = Math_Modulo.neg(Math_Modulo.add(Math_Modulo.mul(m,erg[0]) , n)); 175 | return erg; 176 | } 177 | 178 | 179 | 180 | /** Subtrahiert ein Punkt P mit dem Punkt Q auf der elliptischen Kurve. 181 | Wird nur zu Testzwecken benötigt. */ 182 | public static BigInteger[]subtraktion(BigInteger[] p1, BigInteger[] p2) 183 | { 184 | BigInteger[] y = new BigInteger[2]; 185 | y[0] = p2[0]; 186 | y[1] = Math_Modulo.neg(p2[1]); 187 | return addition(p1,y); 188 | } 189 | 190 | 191 | 192 | /** Dividiert P/Q auf der elliptischen Kurve 193 | Wird nur zu Testzwecken benötigt. */ 194 | public static BigInteger[] div(BigInteger[] P, BigInteger Q) 195 | { 196 | BigInteger teiler = Math_Modulo.calcHalb(Q); 197 | return multiply_Point(P,teiler); 198 | } 199 | 200 | 201 | 202 | /** Beschneidet ein ByteArray beliebiger Länge auf eine fest definierte Länge "len". 203 | - Wenn "data" kleiner als "len" ist wird es vorne mit Nullen aufgefüllt. 204 | - Wenn "data" länger als "len" ist, wird es hinten abgeschnitten. */ 205 | public static byte[] to_fixLength(byte[] data, int len) 206 | { 207 | if(data.length < len) 208 | { 209 | byte[] out = new byte[len]; 210 | System.arraycopy(data, 0, out, len-data.length, data.length); 211 | return out; 212 | } 213 | if(data.length > len) return Arrays.copyOf(data, len); 214 | return data; 215 | } 216 | 217 | 218 | 219 | // -------------------------------------------- Sonnstiges ---------------------------------- // 220 | 221 | 222 | /** Gibt den Punkt Y von X zurück. 223 | Da es immer zwei gültige Y-Werte zu einem X-Wert gibt ist die Berechnung nur zu 50% richtig. 224 | Die Information um welchen Punkt es sich handelt kann die Methode nicht "erraten". Daher werden beide möglichen Y-Werte in einem Array zurück gegeben. 225 | Y1 = sqrt(x³+7) , Y2 = -sqrt(x³+7) */ 226 | public static BigInteger[] y_von_x(BigInteger x) 227 | { 228 | BigInteger[] erg = new BigInteger[2]; 229 | try 230 | { 231 | erg[0] = Math_Modulo.sqrt(Math_Modulo.add(Math_Modulo.pow(x,THREE) , SEVEN)); 232 | erg[1] = Math_Modulo.neg(Math_Modulo.sqrt(Math_Modulo.add(Math_Modulo.pow(x,THREE) , SEVEN))); 233 | return erg; 234 | } 235 | catch(Exception e) 236 | { 237 | System.out.println("X-Koordinate trifft nicht auf die elliptische Kurve!"); 238 | return erg; 239 | } 240 | } 241 | 242 | 243 | 244 | /** Decompremiert einen Punkt auf der elliptischen Kurve secp256k1. 245 | Es Wird ein kompriemierter Punkt (PubKey) mit 02 oder 03 vorne als Hex-String übergeben. 246 | Der Übergebene Hex-String muss genau 33Byte lang sein und darf nur Hexadezimale Zeichen enthalten. 247 | Das erste Byte muss 02 oder 03 Sein! Dies gibt an ob der Y-Wert gerade oder ungerade ist. "02"= Gerade, "03"= unerade 248 | Zurück gegeben wird der unkomprimierte Punkt als BigInteger-Array. **/ 249 | public static BigInteger[] deComp(String pub) throws Exception 250 | { 251 | if(pub.length()!=66) throw new Exception("Format Error: String not 33Byte long!"); 252 | String s = pub.substring(0,2); 253 | if(!s.equals("02") && !s.equals("03")) throw new Exception("Format Error: First byte is not 02 or 03!"); 254 | String str = pub.substring(2,66); 255 | BigInteger[] erg = new BigInteger[2]; 256 | erg[0] = new BigInteger(str,16); 257 | BigInteger[] y = y_von_x(erg[0]); 258 | BigInteger yGerade; 259 | BigInteger yUngerade; 260 | if(y[0].mod(TWO).equals(BigInteger.ZERO)) 261 | { 262 | yGerade = y[0]; 263 | yUngerade = y[1]; 264 | } 265 | else 266 | { 267 | yGerade = y[1]; 268 | yUngerade = y[0]; 269 | } 270 | if(s.equals("02")) erg[1] = yGerade; 271 | if(s.equals("03")) erg[1] = yUngerade; 272 | return erg; 273 | } 274 | 275 | 276 | 277 | /** Komprimiert einen Punkt auf der elliptischen Kurve secp256k1. 278 | @param pub Es wird ein umkomprimierter EC-Punkt als BigInteger[] übergeben. X-Koordinate ist [0] und X-Koordinate ist[1] 279 | @compressed wenn true 280 | @return Der Komprimierte Public-Key wird als Byte-Array zurückgegeben und ist genau 33 Byte oder genau 65 Byte lann (wenn uncompressed). 281 | Das erste Byte ist dabei immer "02", "03", oder "04". Dies gibt an ob der Y-Wert gerade oder ungerade ist. "02"= Gerade, "03"= ungerade 282 | oder "04" Uncompressed X und Y werden hintereinander ausgegeben.**/ 283 | public static byte[] comp(BigInteger[] pub, boolean compressed) 284 | { 285 | byte[] x = pub[0].toByteArray(); 286 | byte[] y = pub[1].toByteArray(); 287 | x = Convert.removeLeadingZeros(x); 288 | y = Convert.removeLeadingZeros(y); 289 | x = Convert.to_fixLength(x, 32); 290 | y = Convert.to_fixLength(y, 32); 291 | if(compressed) 292 | { 293 | byte[] out = new byte[33]; 294 | System.arraycopy(x, 0, out, 1, 32); 295 | if(pub[1].mod(TWO).equals(BigInteger.ZERO)) 296 | { 297 | out[0] = 0x02; 298 | return out; 299 | } 300 | else 301 | { 302 | out[0] = 0x03; 303 | return out; 304 | } 305 | } 306 | else 307 | { 308 | byte[] out = new byte[65]; 309 | System.arraycopy(x, 0, out, 1, 32); 310 | System.arraycopy(y, 0, out, 33, 32); 311 | out[0] = 0x04; 312 | return out; 313 | } 314 | } 315 | 316 | 317 | 318 | } -------------------------------------------------------------------------------- /src/GUI/GUI_CoinList.java: -------------------------------------------------------------------------------- 1 | package GUI; 2 | import java.awt.BorderLayout; 3 | import java.awt.Font; 4 | import java.awt.event.KeyAdapter; 5 | import java.awt.event.KeyEvent; 6 | import java.awt.event.MouseAdapter; 7 | import java.awt.event.MouseEvent; 8 | 9 | import javax.swing.JFrame; 10 | import javax.swing.JList; 11 | import javax.swing.JPanel; 12 | import javax.swing.JScrollPane; 13 | 14 | import BTClib3001.CoinParameter; 15 | 16 | 17 | 18 | /*********************************************************************************** 19 | * Diese Klasse ist Teil der GUI des CoinAddressGenerators * 20 | * Erzeugt das List-Fenster, in dem Coins hinzugefügt oder entfernt werden können. * 21 | ***********************************************************************************/ 22 | 23 | 24 | 25 | class GUI_CoinList extends JFrame 26 | { 27 | CoinParameter[] cp = null; 28 | GUI_CoinList(int x, int y, boolean remove) 29 | { 30 | setTitle("Coin List"); 31 | setBounds(x, y, 300, 400); 32 | JPanel contentPane = new JPanel(); 33 | contentPane.setLayout(new BorderLayout(0, 0)); 34 | setContentPane(contentPane); 35 | JScrollPane scrollPane = new JScrollPane(); 36 | contentPane.add(scrollPane, BorderLayout.CENTER); 37 | String[] strList; 38 | if(remove) 39 | { 40 | int size = GUI.comboBox_coin.getItemCount(); 41 | strList = new String[size]; 42 | for (int i = 0; i < size; i++) strList[i] = (String) GUI.comboBox_coin.getItemAt(i); 43 | } 44 | else 45 | { 46 | cp = CoinParameter.getList(); 47 | strList = new String[cp.length]; 48 | for(int i=0;i= 2) btn_open.doClick(); 117 | } 118 | }); 119 | } 120 | if(profil.equals("save")) 121 | { 122 | btn_open.setVisible(false); 123 | btn_delete.setVisible(false); 124 | setBounds(x, y, 875, 210); 125 | for(int i=0;i=0) 148 | { 149 | if(checkComboBoxItem(GUI.comboBox_coin,(String) dtm.getValueAt(row, 1))==false) throw new Exception("Unknown Coin, first import this coin!"); 150 | GUI.comboBox_coin.setSelectedItem(dtm.getValueAt(row, 1)); 151 | GUI.setSelectedAddressFormat((String) dtm.getValueAt(row, 4)); 152 | GUI.tabbedPane.setSelectedIndex(2); 153 | GUI.txt_privKey.setText((String) dtm.getValueAt(row, 3)); 154 | Action.go(); 155 | GUI.frame.setEnabled(true); 156 | dispose(); 157 | 158 | } 159 | } 160 | catch(Exception ex) {lbl_error.setText(ex.getMessage());} 161 | } 162 | }); 163 | 164 | 165 | btn_save.addActionListener(new ActionListener() 166 | { 167 | public void actionPerformed(ActionEvent e) 168 | { 169 | if(isDuplicateAddress()) {lbl_error.setText("Address already exists!");} 170 | else 171 | { 172 | int count = dtm.getRowCount(); 173 | JSONArray ja = new JSONArray(); 174 | try 175 | { 176 | for(int i=0;i=0) dtm.removeRow(pos); 208 | } 209 | }); 210 | 211 | 212 | 213 | btn_close.addActionListener(new ActionListener() 214 | { 215 | public void actionPerformed(ActionEvent e) 216 | { 217 | GUI.frame.setEnabled(true); 218 | dispose(); 219 | } 220 | }); 221 | 222 | 223 | 224 | // Close Button wird abgefangen und hier selbst verarbeitet. 225 | addWindowListener(new java.awt.event.WindowAdapter() 226 | { 227 | @Override 228 | public void windowClosing(java.awt.event.WindowEvent windowEvent) 229 | { 230 | GUI.frame.setEnabled(true); 231 | } 232 | }); 233 | } 234 | 235 | 236 | 237 | // Läd die Wallet in die Tabelle der GUI 238 | private void loadWallet() throws Exception 239 | { 240 | dtm.setRowCount(0); 241 | wallet = Wallet.open(); 242 | JSONArray ja = wallet.getJSONArray("list"); 243 | for(int i=0;i= 1 sein. Hier wird 16 verwendet; 172 | final int outLen = 32; // Die Länge des Ausgabe Hash. 173 | return ScryptHash.getHash(data, salt, memory, r, p, outLen); 174 | } 175 | 176 | 177 | 178 | /** Hängt einen SHA256 Hash von "data" vorne an "data" an. 179 | "Vorne" weil die hinten angehängten Leerzeichen des Paddings das hintere Parsen erschwerden würden. 180 | Wird in der Verschlüsselung verwendet. Dieser Hash, wird dem Klartext vorne angehängt, 181 | um später bei der entschlüsselung festzustellen ob korrekt entschlüsselt wurde. **/ 182 | private static byte[] addSHA256Checksum(byte[] data) 183 | { 184 | byte[] hash = Calc.getHashSHA256(data); 185 | byte[] out = Arrays.copyOf(hash, data.length+32); 186 | System.arraycopy(data, 0, out, 32, data.length); 187 | return out; 188 | } 189 | 190 | 191 | 192 | // Das übergebene ByteArray wird hinten mit Nullen aufgefüllt wenn die Länge nicht durch 16 ohne Rest Teilbar ist. 193 | private static byte[] padding16(byte[] input) 194 | { 195 | int m = 16 - input.length%16; 196 | if(m==16) m=0; 197 | return Arrays.copyOf(input, input.length+m); 198 | } 199 | } -------------------------------------------------------------------------------- /src/Wallet/CryptDialog.java: -------------------------------------------------------------------------------- 1 | package Wallet; 2 | import java.awt.BorderLayout; 3 | import java.awt.FlowLayout; 4 | import javax.swing.JDialog; 5 | import javax.swing.JLabel; 6 | import javax.swing.JPanel; 7 | import java.awt.Font; 8 | import javax.swing.border.LineBorder; 9 | 10 | import org.json.JSONException; 11 | import org.json.JSONObject; 12 | 13 | import BTClib3001.Calc; 14 | import BTClib3001.Convert; 15 | import javax.swing.JButton; 16 | import javax.swing.JCheckBox; 17 | import java.awt.Dimension; 18 | import javax.swing.JPasswordField; 19 | import java.awt.Color; 20 | import java.awt.Component; 21 | import javax.swing.Box; 22 | import java.awt.Insets; 23 | import javax.swing.JTextArea; 24 | import javax.swing.JProgressBar; 25 | import java.awt.event.ItemListener; 26 | import java.nio.charset.StandardCharsets; 27 | import java.util.Arrays; 28 | import java.awt.event.ItemEvent; 29 | import java.awt.event.ActionListener; 30 | import java.awt.event.ActionEvent; 31 | import java.awt.event.KeyAdapter; 32 | import java.awt.event.KeyEvent; 33 | 34 | 35 | 36 | /*************************************************************************************************************************** 37 | * Ver/End-Schlüsselung mit Dialogfeld (GUI) zu Eingabe von Passwörtern. * 38 | * Die beiden Hauptmethoden paranoidEncrypt() und paranoidDecrypt() * 39 | * integrieren den kompletten Verschlüsselungsprozess Siehe Blockdiagramm "ParanoidCrypt.png". * 40 | * Beim Aufruf öffnet sich dieses Dialogfeld welches zur Eingabe des Verschlüsselungs-Passwortes auffordert. * 41 | * Dieser Konstruktor CryptDialog() erstellt das Dialogfeld zur eingabe des Passwortes. * 42 | * Diese Klasse ist zwar für den CoinAddressGenerator programmiert, ist aber allgemein kompatibel mit folgener Ausnahme: * 43 | * Die Klartext-Daten müssen ein JSONObject sein welches den Datensetzt "pwHash" enthällt! * 44 | * Dieser PwHash ist ein 32Byte SHA256² Hex-String der den doppelten PasswortHash, des voherigen Paswortes enthällt. * 45 | * (Doppel, weil dadurch der einfache SHA256 Hash der zum verschlüsseln benutz wird nicht aufgedeckt werden kann.) * 46 | * Dies ist notwendig um zu verhindern, das versehentlich mit einem falschen Passort neu verschlüsselt wird. * 47 | * Bei der Verschlüsselung kann so, das alte Passwort geprüft werden bevor neu verschlüsselt wird. * 48 | * Bei der erstmaligen Verschlüsselung ist pwHash = "" (Leerstring); * 49 | * Die Methode paranoidEncrypt() prüft so das alte Passort! * 50 | ****************************************************************************************************************************/ 51 | 52 | 53 | 54 | public class CryptDialog extends JDialog 55 | { 56 | 57 | 58 | 59 | private static CryptDialog dialog; // Das Dialogfeld von Typ: JDialog 60 | private byte[] dataOut; // Rückgabe Daten, Chiffre oder Klartext. 61 | private JPasswordField txt_pw1 = new JPasswordField(); 62 | private JPasswordField txt_pw2 = new JPasswordField(); 63 | private JPasswordField txt_pw3 = new JPasswordField(); 64 | private boolean threadRun = false; 65 | private JProgressBar spinner= new JProgressBar(); 66 | 67 | 68 | 69 | 70 | /** Hauptmethode zur Verschlüsselung. 71 | - Öffnet das Dialogfeld zur Eingabe des Passwortes zur Verschlüsselung 72 | - Es kann entweder das alte Passwort verwendet werden oder es kann ein neues Passwort erstellt werden. 73 | - Das Passwort wird geprüft mit einem im Klartext enthaltenem JSON-Datensatz "pwHash" der den doppelten SHA256² Hash des voherigen Passwortes enthällt. 74 | Bei der erstmaligen Verschlüsselung muss dieser Datensatz auch vorhanden und ein Leerstring sein! 75 | - Verschlüsselt die Daten mit dem eingegebenem Passwort 76 | - Vor der Verschlüsselung wird an die Daten ein checksum SAH256 Hash vorrangestellt 77 | - Gibt Meldungen oder Fehler im Dialogfeld aus 78 | @param x & y Position des Dialogfensters 79 | @param klarText Die Daten die mit ParanoidCrypt verschlüsselt werden sollen als JSONObject. 80 | @return Nach erfolgreicher verschlüsselung wird die Chiffre als ByteArray zurück gegeben und das Dialogfeld geschlossen. 81 | Bei Fehlern oder durch Abbruch des Benutzers wird "null" zurück gegeben. **/ 82 | public static byte[] paranoidEncrypt(int x, int y, JSONObject klarText) throws Exception 83 | { 84 | dialog = new CryptDialog(x,y,"encrypt", klarText,null); 85 | dialog.setVisible(true); 86 | dialog.dispose(); 87 | return dialog.dataOut; 88 | } 89 | 90 | 91 | 92 | /** Hauptmethode zur Endschlüsselung. 93 | - Öffnet das Dialogfeld zur Eingabe des Passwortes zur Endschlüsselung 94 | - Entschlüsselt die Daten mit dem eingegebenem Passwort 95 | - Mit dem an den Klartext vorrangestellten SHA256 wird geprüft, ob die entschlüsselung erfolgreich war. 96 | - Gibt Meldungen oder Fehler im Dialogfeld aus 97 | @param x & y Position des Dialogfensters 98 | @param chiffre Die Daten die mit ParanoidCrypt entschlüsselt werden sollen 99 | @return Nach erfolgreicher entschlüsselung werden die Daten im Klartext zurück gegeben und das Dialogfeld geschlossen. 100 | Bei Fehlern oder durch Abbruch des Benutzers wird "null" zurück gegeben. **/ 101 | public static String paranoidDecrypt(int x, int y, byte[] chiffre) throws Exception 102 | { 103 | dialog = new CryptDialog(x,y,"decrypt",null,chiffre); 104 | dialog.setVisible(true); 105 | dialog.dispose(); 106 | if(dialog.dataOut==null) return null; 107 | return new String(dialog.dataOut, StandardCharsets.UTF_8); 108 | } 109 | 110 | 111 | 112 | /** Konstruktor der GUI des Passowrt-Dialog-Feldes 113 | @param profil Das Dialog-Profil. Es gibt: "encrypt" oder "decrypt" 114 | @param pwHash SHA256² das alte Password wird nur zur Überprüfung bei der Verschlüsselung genutzt. 115 | * @throws JSONException **/ 116 | private CryptDialog(int x, int y, String profil, JSONObject klarText, byte[] chiffre) throws JSONException 117 | { 118 | setModal(true); 119 | setBounds(x, y, 500, 340); 120 | setTitle("Password"); 121 | 122 | JPanel contentPanel = new JPanel(); 123 | JPanel panel_haupt = new JPanel(); 124 | JPanel panel_unten = new JPanel(); 125 | JTextArea info = new JTextArea(" Attention! Password cannot be recovered! \n If the password is forgotten, coins will be lost forever!"); 126 | JLabel lbl_pw1 = new JLabel("Enter Password"); 127 | JLabel lbl_pw2 = new JLabel("New Password"); 128 | JLabel lbl_pw3 = new JLabel("Confirm Password"); 129 | JCheckBox checkbox_1= new JCheckBox("Show Password"); 130 | JCheckBox checkbox_2= new JCheckBox("Change Password"); 131 | JTextArea txt_error = new JTextArea(); 132 | JButton btn_ok = new JButton("OK"); 133 | JButton btn_abbruch = new JButton("Cancel"); 134 | 135 | Component strut1 = Box.createHorizontalStrut(20); 136 | Component strut2 = Box.createHorizontalStrut(20); 137 | Component strut3 = Box.createHorizontalStrut(2000); 138 | Component strut4 = Box.createHorizontalStrut(2000); 139 | Component strut5 = Box.createHorizontalStrut(2000); 140 | Component strut6 = Box.createHorizontalStrut(2000); 141 | 142 | strut3. setPreferredSize(new Dimension(2000, 10)); 143 | strut4. setPreferredSize(new Dimension(2000, 10)); 144 | strut5. setPreferredSize(new Dimension(2000, 10)); 145 | strut6. setPreferredSize(new Dimension(2000, 6)); 146 | txt_pw1. setPreferredSize(new Dimension(2000, 21)); 147 | txt_pw2. setPreferredSize(new Dimension(2000, 21)); 148 | txt_pw3. setPreferredSize(new Dimension(2000, 21)); 149 | checkbox_1. setPreferredSize(new Dimension(2000, 18)); 150 | checkbox_2. setPreferredSize(new Dimension(2000, 18)); 151 | btn_ok. setPreferredSize(new Dimension(70, 23)); 152 | btn_abbruch.setPreferredSize(new Dimension(70, 23)); 153 | spinner. setPreferredSize(new Dimension(440, 5)); 154 | 155 | contentPanel.setLayout(new BorderLayout()); 156 | panel_haupt .setLayout(new FlowLayout( 0, 1, 1)); 157 | panel_unten .setLayout(new FlowLayout(FlowLayout.RIGHT)); 158 | info. setFont(new Font("Arial", Font.PLAIN, 13)); 159 | txt_error. setFont(new Font("Tahoma", Font.PLAIN, 13)); 160 | checkbox_2. setFont(new Font("Tahoma", Font.PLAIN, 10)); 161 | checkbox_1. setFont(new Font("Tahoma", Font.PLAIN, 10)); 162 | info. setBorder(new LineBorder(new Color(255, 180, 180), 4)); 163 | info .setEditable(false); 164 | info.setVisible(false); 165 | txt_error. setSize(new Dimension(440, 30)); 166 | txt_error. setWrapStyleWord(true); 167 | txt_error. setLineWrap(true); 168 | info .setBackground(new Color(255, 180, 180)); 169 | txt_error. setBackground(new Color(240, 240, 240)); 170 | txt_error. setForeground(new Color(165, 42, 42)); 171 | txt_error. setEditable(false); 172 | btn_abbruch.setMargin(new Insets(0, 0, 0, 0)); 173 | spinner. setIndeterminate(true); 174 | spinner. setBorderPainted(false); 175 | spinner. setVisible(false); 176 | 177 | setContentPane(contentPanel); 178 | contentPanel.add(info, BorderLayout.NORTH); 179 | contentPanel.add(panel_haupt, BorderLayout.CENTER); 180 | 181 | Component strut0 = Box.createHorizontalStrut(2000); 182 | strut0.setPreferredSize(new Dimension(2000, 10)); 183 | panel_haupt.add(strut0); 184 | panel_haupt.add(lbl_pw1); 185 | panel_haupt.add(txt_pw1); 186 | panel_haupt.add(strut3); 187 | panel_haupt.add(lbl_pw2); 188 | panel_haupt.add(txt_pw2); 189 | panel_haupt.add(strut4); 190 | panel_haupt.add(lbl_pw3); 191 | panel_haupt.add(txt_pw3); 192 | panel_haupt.add(strut5); 193 | panel_haupt.add(checkbox_1); 194 | panel_haupt.add(checkbox_2); 195 | panel_haupt.add(strut6); 196 | panel_haupt.add(spinner); 197 | panel_haupt.add(txt_error); 198 | contentPanel.add(panel_unten, BorderLayout.SOUTH); 199 | panel_unten.add(btn_ok); 200 | panel_unten.add(btn_abbruch); 201 | contentPanel.add(strut1, BorderLayout.WEST); 202 | contentPanel.add(strut2, BorderLayout.EAST); 203 | 204 | if(profil.equals("decrypt")) 205 | { 206 | lbl_pw2.setVisible(false); 207 | lbl_pw3.setVisible(false); 208 | txt_pw2.setVisible(false); 209 | txt_pw3.setVisible(false); 210 | checkbox_2.setVisible(false); 211 | } 212 | if(profil.equals("encrypt")) 213 | { 214 | if(klarText.getString("pwHash").equals("")) 215 | { 216 | checkbox_2.setSelected(true); 217 | checkbox_2.setVisible(false); 218 | lbl_pw1.setVisible(false); 219 | txt_pw1.setVisible(false); 220 | lbl_pw2.setVisible(true); 221 | lbl_pw3.setVisible(true); 222 | txt_pw2.setVisible(true); 223 | txt_pw3.setVisible(true); 224 | info.setVisible(true); 225 | } 226 | else 227 | { 228 | lbl_pw2.setVisible(false); 229 | lbl_pw3.setVisible(false); 230 | txt_pw2.setVisible(false); 231 | txt_pw3.setVisible(false); 232 | } 233 | } 234 | 235 | 236 | 237 | 238 | // ---------------------------------------- Actoin Listeners ------------------------------------// 239 | 240 | 241 | btn_ok.addActionListener(new ActionListener() 242 | { 243 | public void actionPerformed(ActionEvent e) 244 | { 245 | if(profil.equals("encrypt")) 246 | { 247 | Thread tr = new Thread(new Runnable() 248 | { 249 | public void run() 250 | { 251 | threadRun = true; 252 | dialog.setEnabled(false); 253 | spinner.setVisible(true); 254 | try 255 | { 256 | txt_error.setText(""); 257 | String pw = new String(txt_pw1.getPassword()); 258 | String pwh = Calc.getHashSHA256_from_HexString(Calc.getHashSHA256(pw)); 259 | if(pwh.equals(klarText.getString("pwHash"))) // Wenn das eingegebene PW dem voherigem PW entspricht. 260 | { 261 | if(checkbox_2.isSelected()) // Wenn das PW geändert wird. 262 | { 263 | char[] pw2 = txt_pw2.getPassword(); 264 | char[] pw3 = txt_pw3.getPassword(); 265 | String newPW = new String(pw3); 266 | if("".equals(new String(pw3))) throw new Exception("New Password cannot be empty!"); 267 | if(Arrays.equals(pw2, pw3)) 268 | { 269 | klarText.put("pwHash", Calc.getHashSHA256_from_HexString(Calc.getHashSHA256(newPW))); 270 | dataOut = Crypt.paranoidEncrypt(klarText.toString().getBytes(), Convert.hexStringToByteArray(Calc.getHashSHA256(newPW))); 271 | dispose(); 272 | } 273 | else throw new Exception("new Passwords are not the same!"); 274 | } 275 | else // Verschlüsselung mit altem Passort 276 | { 277 | dataOut = Crypt.paranoidEncrypt(klarText.toString().getBytes(), Convert.hexStringToByteArray(Calc.getHashSHA256(pw))); 278 | dispose(); 279 | } 280 | } 281 | else 282 | { 283 | if("".equals(klarText.getString("pwHash"))) // Wenn PasswortHash leer ist, wird erstmalig ein neues Passwort angelegt 284 | { 285 | char[] pw2 = txt_pw2.getPassword(); 286 | char[] pw3 = txt_pw3.getPassword(); 287 | String newPW = new String(pw3); 288 | if("".equals(new String(pw3))) throw new Exception("New Password cannot be empty!"); 289 | if(Arrays.equals(pw2, pw3)) 290 | { 291 | klarText.put("pwHash", Calc.getHashSHA256_from_HexString(Calc.getHashSHA256(newPW))); 292 | dataOut = Crypt.paranoidEncrypt(klarText.toString().getBytes(), Convert.hexStringToByteArray(Calc.getHashSHA256(newPW))); 293 | dispose(); 294 | } 295 | else throw new Exception("new Passwords are not the same!"); 296 | } 297 | else throw new Exception("Password incorrect!"); 298 | } 299 | } 300 | catch(Exception ex) {txt_error.setText(ex.getMessage());} 301 | spinner.setVisible(false); 302 | threadRun = false; 303 | dialog.setEnabled(true); 304 | } 305 | }); 306 | if(threadRun == false) tr.start(); 307 | } 308 | if(profil.equals("decrypt")) 309 | { 310 | Thread tr = new Thread(new Runnable() 311 | { 312 | public void run() 313 | { 314 | threadRun = true; 315 | dialog.setEnabled(false); 316 | spinner.setVisible(true); 317 | try 318 | { 319 | txt_error.setText(""); 320 | String pw = new String(txt_pw1.getPassword()); 321 | byte[] b = Crypt.paranoidDecrypt(chiffre, Convert.hexStringToByteArray(Calc.getHashSHA256(pw))); 322 | byte[][] dec = Crypt.removeAndCheckSHA256Checksum(b); 323 | if(dec[2][0]==1) {dataOut = dec[1]; dispose();} 324 | else throw new Exception("Error decrypt, Password incorrect!"); 325 | } 326 | catch(Exception ex) {txt_error.setText(ex.getMessage());} 327 | spinner.setVisible(false); 328 | threadRun = false; 329 | dialog.setEnabled(true); 330 | } 331 | }); 332 | if(threadRun == false) tr.start(); 333 | } 334 | } 335 | }); 336 | 337 | 338 | 339 | txt_pw1.addKeyListener(new KeyAdapter() 340 | { 341 | @Override 342 | public void keyPressed(KeyEvent e) 343 | { 344 | if(e.getKeyCode()==10) btn_ok.doClick(); 345 | } 346 | }); 347 | 348 | txt_pw2.addKeyListener(new KeyAdapter() 349 | { 350 | @Override 351 | public void keyPressed(KeyEvent e) 352 | { 353 | if(e.getKeyCode()==10) btn_ok.doClick(); 354 | } 355 | }); 356 | 357 | txt_pw3.addKeyListener(new KeyAdapter() 358 | { 359 | @Override 360 | public void keyPressed(KeyEvent e) 361 | { 362 | if(e.getKeyCode()==10) btn_ok.doClick(); 363 | } 364 | }); 365 | 366 | 367 | 368 | checkbox_1.addItemListener(new ItemListener() 369 | { 370 | public void itemStateChanged(ItemEvent e) 371 | { 372 | if (checkbox_1.isSelected()) 373 | { 374 | txt_pw1.setEchoChar((char) 0); 375 | txt_pw2.setEchoChar((char) 0); 376 | txt_pw3.setEchoChar((char) 0); 377 | } 378 | else 379 | { 380 | txt_pw1.setEchoChar('•'); 381 | txt_pw2.setEchoChar('•'); 382 | txt_pw3.setEchoChar('•'); 383 | } 384 | } 385 | }); 386 | 387 | 388 | 389 | checkbox_2.addItemListener(new ItemListener() 390 | { 391 | public void itemStateChanged(ItemEvent e) 392 | { 393 | if (checkbox_2.isSelected()) 394 | { 395 | lbl_pw2.setVisible(true); 396 | lbl_pw3.setVisible(true); 397 | txt_pw2.setVisible(true); 398 | txt_pw3.setVisible(true); 399 | info.setVisible(true); 400 | } 401 | else 402 | { 403 | lbl_pw2.setVisible(false); 404 | lbl_pw3.setVisible(false); 405 | txt_pw2.setVisible(false); 406 | txt_pw3.setVisible(false); 407 | lbl_pw1.setText("Enter Password"); 408 | info.setVisible(false); 409 | 410 | } 411 | } 412 | }); 413 | 414 | 415 | 416 | btn_abbruch.addActionListener(new ActionListener() 417 | { 418 | public void actionPerformed(ActionEvent e) 419 | { 420 | dataOut = null; 421 | dispose(); 422 | } 423 | }); 424 | 425 | 426 | 427 | // Close Button wird abgefangen und hier selbst verarbeitet. 428 | addWindowListener(new java.awt.event.WindowAdapter() 429 | { 430 | @Override 431 | public void windowClosing(java.awt.event.WindowEvent windowEvent) 432 | { 433 | dataOut = null; 434 | dispose(); 435 | } 436 | }); 437 | 438 | } 439 | } -------------------------------------------------------------------------------- /src/Wallet/Wallet.java: -------------------------------------------------------------------------------- 1 | package Wallet; 2 | import java.io.BufferedInputStream; 3 | import java.io.BufferedOutputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.nio.file.Files; 9 | import java.nio.file.StandardCopyOption; 10 | import org.json.JSONArray; 11 | import org.json.JSONObject; 12 | import GUI.GUI; 13 | 14 | 15 | 16 | /*********************************************************************************** 17 | * Autor: Mr. Maxwell * 18 | * Speichert und öffnet die CAGWallet.dat für den Coin-Address-Generator * 19 | * * 20 | * Aufbau der CAGWallet.dat: entschlüsselt im JSON-Format 21 | * { 22 | * "dateiID" : "c6fcf3b9bc7c855f46a43e70450d2dca444b527ec5fb30ada91d9bbd90fcef7a", 23 | * "progName": "Coin Address Generator", 24 | * "version" : "V3.0.0", 25 | * "pwHash" : "92b165232fbd011da355eca0b033db22b934ba9af0145a437a832d27310b89f9", 26 | * "list": 27 | * [{ 28 | * "Coin": "BTC", 29 | * "Description": "", 30 | * "Format" : "P2SH", 31 | * "Address" : "bc1qdjljwt79a7wsf0zmmfzcqk5xnzcms6mt6zcrvd", 32 | * "Priv.Key" : "L4pvfk8nAkYeoRSwCvTG2PRwVfY8kHA6nffBHxqPpjHSEUfYgKcE", 33 | * "Date" : "2020-02-26" 34 | * }] 35 | * } 36 | ************************************************************************************/ 37 | 38 | 39 | 40 | public class Wallet 41 | { 42 | 43 | 44 | 45 | final static String dateiID = "c6fcf3b9bc7c855f46a43e70450d2dca444b527ec5fb30ada91d9bbd90fcef7a"; 46 | final static String fileName = "CAGWallet.dat"; // Name der Wallet Datei 47 | 48 | 49 | 50 | /** Wird von der GUI_Wallet aufgerufen. 51 | - Die CAGWallet.dat wird gelesen, oder ggf. neu erstellt 52 | - ParanoidDecrypt wird gestartet und öffnet den Passwort-Dialog 53 | @return CAGWallet.dat wird entschlüsselt und als JSONObject zurück gegeben. **/ 54 | public static JSONObject open() throws Exception 55 | { 56 | File file = new File(fileName); 57 | if(file.exists()==false) 58 | { 59 | JSONObject jo = new JSONObject(); 60 | jo.put("pwHash", ""); 61 | jo.put("list", new JSONArray()); 62 | save(jo); 63 | } 64 | BufferedInputStream bi = new BufferedInputStream(new FileInputStream(file)); 65 | byte[] in = new byte[(int) file.length()]; 66 | bi.read(in); 67 | bi.close(); 68 | String str = CryptDialog.paranoidDecrypt(GUI.frame.getX()+20, GUI.frame.getY()+20, in); 69 | if(str==null) throw new Exception("Error in Wallet.open(). null Password."); 70 | if(str.equals("")) throw new Exception("Error in Wallet.open(). empty Password."); 71 | return new JSONObject(str); 72 | } 73 | 74 | 75 | 76 | /** Wird von der GUI_Wallet aufgerufen. 77 | @param jo JSONObject der Wallet wird im Klartext übergeben 78 | - Kopiert die CAGWallet.dat 79 | - ParanoidEcrypt wird gestartet und öffnet den Passwort-Dialog 80 | - Die übergebene Wallet "jo" wird mit ParanoidEncrypt verschlüsset 81 | - CAGWallet.dat wird nun mit der neuen verschlüsselten Wallet überschrieben **/ 82 | public static void save(JSONObject jo) throws Exception 83 | { 84 | kopieBackup(); 85 | jo.put("dateiID", dateiID); 86 | jo.put("progName", GUI.progName); 87 | jo.put("version", GUI.version); 88 | byte[] ch; 89 | File file = new File(fileName); 90 | if(file.exists()==true) ch = CryptDialog.paranoidEncrypt(GUI.frame.getX()+20, GUI.frame.getY()+20, jo ); 91 | else ch = CryptDialog.paranoidEncrypt(GUI.frame.getX()+20, GUI.frame.getY()+20, jo); 92 | if(ch == null) throw new Exception("Error Encrypt, NULL!"); 93 | BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(file)); 94 | bo.write(ch); 95 | bo.close(); 96 | } 97 | 98 | 99 | // Kopiert die vorhandene (alte) Wallet-Datei nach Backup_CAGWallet.dat 100 | private static void kopieBackup() throws IOException 101 | { 102 | File src = new File(fileName); 103 | File des = new File("old_"+fileName); 104 | if(src.exists()) Files.copy(src.toPath(), des.toPath(), StandardCopyOption.REPLACE_EXISTING); 105 | } 106 | } -------------------------------------------------------------------------------- /src/seedExtractor/SeedExtractor.java: -------------------------------------------------------------------------------- 1 | package seedExtractor; 2 | import BTClib3001.Calc; 3 | import BTClib3001.Convert; 4 | import BTClib3001.ScryptHash; 5 | import CoinGen.Action; 6 | import GUI.GUI; 7 | 8 | 9 | 10 | /******************************************************************** 11 | * V1.1 Angepasst für den CoinAddressGen. * 12 | * Führt die Berechnungen des SeedExtractors durch * 13 | *********************************************************************/ 14 | 15 | 16 | 17 | public class SeedExtractor 18 | { 19 | private static byte[] seed; 20 | public static byte[][] privKeys; 21 | 22 | 23 | 24 | 25 | 26 | // Entschlüsselt den Seed aus dem verschlüsseltem Seed und dem Passwort 27 | public static void encodeSeed(String vSeed, String passWort) throws Exception 28 | { 29 | if(vSeed.length() != 64) throw new Exception("Error Eingabe Seed! \nVerschlüsselter Seed muss genau 64 Zeichen lang sein!"); 30 | if(vSeed.matches("^[0-9a-fA-F]+$")==false) throw new Exception("Error Eingabe Seed! \nVerschlüsselter Seed darf nur Hexa-Zeichen enthalten!"); 31 | seed = getScryptHash(Convert.hexStringToByteArray(Calc.getHashSHA256_from_HexString(vSeed + Calc.getHashSHA256(passWort)))); 32 | GUI.txt_nr.setValue(1); 33 | privKeys = keyDuplication(seed, Integer.parseInt(GUI.txt_max.getText())); 34 | Action.go(); 35 | } 36 | 37 | 38 | /** Generiert eine beliebige Menge isolierter (SHA256) Schlüsseln aus einem Master-Key. 39 | Die Ausgegebenen Schlüssel werden deterministisch vom "masterKey" erzeugt mit der Eigenschaft, 40 | dass alle erzeugten Keys frei von Informationen der anderen Keys oder dem "masterKey" sind. 41 | @param masterKey als Byte-Array 42 | @param count die Anzahl der SHA256 Schlüssel die erzeugt werden sollen. 43 | @return Eine Menge von Schlüsseln mit einer Länge von 32Byte. Die Anzahl der Schlüssel hängt von "count" ab. **/ 44 | private static byte[][] keyDuplication(byte[] masterKey, int count) 45 | { 46 | byte[] mKey = Calc.getHashSHA256(masterKey); 47 | byte[] kn = mKey; 48 | byte[][] out = new byte[count][32]; 49 | for(int i=0;i= 1 sein. Hier wird 16 verwendet; 70 | final int outLen = 32; // Die Länge des Ausgabe Hash. 71 | return ScryptHash.getHash(data, salt, memory, r, p, outLen); 72 | } 73 | 74 | 75 | // Alt: Für ein separate Programm SeedExtractor vorgesehen. Nicht löschen! 76 | // Zeigt die Key´s mit dem Index "index" der AusgebeKonsole an. 77 | // public static void printKey(int index) 78 | // { 79 | // try 80 | // { 81 | // String addr; 82 | // byte[] priv = privKeys[index-1]; 83 | // PrvKey p = new PrvKey(priv,Convert.hexStringToByteArray("80")); 84 | // String prvHex = Convert.byteArrayToHexString(p.getHexPrivKey()); 85 | // String prvWIF = p.getBase58PrivKey(GUI.unCompressed.isSelected()==false); 86 | // String pub = Calc.getPublicKey(prvHex, GUI.unCompressed.isSelected()==false); 87 | // String h160 = Calc.getHashRIPEMD160_from_HexString(Calc.getHashSHA256_from_HexString(pub)); 88 | // if(GUI.bech32.isSelected()) 89 | // { 90 | // addr = Bech32Address.segwitToBech32("bc" , 0, Convert.hexStringToByteArray(h160)); 91 | // } 92 | // else 93 | // { 94 | // BitcoinAddr address = new BitcoinAddr(Convert.hexStringToByteArray(h160) , Convert.hexStringToByteArray("00")); 95 | // if(GUI.p2sh.isSelected()) addr = address.getP2SHAddress(Convert.hexStringToByteArray("05")); 96 | // else addr = address.getBase58Address(); 97 | // } 98 | // GUI.txt_out.setText("Priv Hex: "+prvHex+"\nPriv WIF: "+prvWIF+"\nPub Key: "+pub+"\nHash160: "+h160+"\nAddress: "+addr); 99 | // } 100 | // catch(Exception e) 101 | // { 102 | // GUI.txt_out.setText(e.getMessage()); 103 | // } 104 | // } 105 | 106 | 107 | } --------------------------------------------------------------------------------