100 lines
4.2 KiB
Java
100 lines
4.2 KiB
Java
import java.math.BigInteger;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.Arrays;
|
|
import java.util.stream.Collectors;
|
|
|
|
public final class BitcoinPublicPointToAddess {
|
|
|
|
public static void main(String[] args) {
|
|
String x = "50863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B2352";
|
|
String y = "2CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6";
|
|
|
|
if ( areValidCoordinates(x, y) ) {
|
|
System.out.println(encodeAddress(x, y));
|
|
} else {
|
|
System.out.println("Invalid Bitcoin public point coordinates");
|
|
}
|
|
}
|
|
|
|
// Return the encoded address of the given coordinates.
|
|
private static String encodeAddress(String x, String y) {
|
|
String publicPoint = BITCOIN_SPECIAL_VALUE + x + y;
|
|
if ( publicPoint.length() != 130 ) {
|
|
throw new AssertionError("Invalid public point string: " + publicPoint);
|
|
}
|
|
|
|
byte[] messageBytes = computeMessageBytes(publicPoint);
|
|
byte[] checksum = computeChecksum(messageBytes);
|
|
messageBytes = Arrays.copyOf(messageBytes, messageBytes.length + 4);
|
|
System.arraycopy(checksum, 0, messageBytes, 21, checksum.length);
|
|
return encodeBase58(messageBytes);
|
|
}
|
|
|
|
// Return the given byte array encoded into a base58 starting with most one '1'
|
|
private static String encodeBase58(byte[] bytes) {
|
|
final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
final int ALPHABET_SIZE = ALPHABET.length();
|
|
|
|
String[] temp = new String[34];
|
|
for ( int n = temp.length - 1; n >= 0; n-- ) {
|
|
int c = 0;
|
|
for ( int i = 0; i < bytes.length; i++ ) {
|
|
c = c * 256 + (int) ( bytes[i] & 0xFF );
|
|
bytes[i] = (byte) ( c / ALPHABET_SIZE );
|
|
c %= ALPHABET_SIZE;
|
|
}
|
|
temp[n] = ALPHABET.substring(c, c + 1);
|
|
}
|
|
|
|
String result = Arrays.stream(temp).collect(Collectors.joining(""));
|
|
while ( result.startsWith("11") ) {
|
|
result = result.substring(1);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Return whether the given coordinates are those of a point on the secp256k1 elliptic curve
|
|
private static boolean areValidCoordinates(String x, String y) {
|
|
BigInteger modulus = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
|
|
BigInteger X = new BigInteger(x, 16);
|
|
BigInteger Y = new BigInteger(y, 16);
|
|
return Y.multiply(Y).mod(modulus).equals(X.multiply(X).multiply(X).add(BigInteger.valueOf(7)).mod(modulus));
|
|
}
|
|
|
|
private static byte[] computeMessageBytes(String text) {
|
|
// Convert the hexadecimal string 'text' into a suitable ASCII string for the SHA256 hash
|
|
byte[] bytesOne = hexToBytes(text);
|
|
String asciiOne = new String(bytesOne, StandardCharsets.ISO_8859_1);
|
|
String hexSHA256 = SHA256.messageDigest(asciiOne);
|
|
// Convert the hexadecimal string 'hexSHA256' into a suitable ASCII string for the RIPEMD160 hash
|
|
byte[] bytesTwo = hexToBytes(hexSHA256);
|
|
String asciiTwo = new String(bytesTwo, StandardCharsets.ISO_8859_1);
|
|
String hexRIPEMD160 = BITCOIN_VERSION_NUMBER + RIPEMD160.messageDigest(asciiTwo);
|
|
return hexToBytes(hexRIPEMD160);
|
|
}
|
|
|
|
private static byte[] computeChecksum(byte[] bytes) {
|
|
// Convert the given byte array into a suitable ASCII string for the first SHA256 hash
|
|
String asciiOne = new String(bytes, StandardCharsets.ISO_8859_1);
|
|
String hexOne = SHA256.messageDigest(asciiOne);
|
|
// Convert the hexadecimal string 'hex1' into a suitable ASCII string for the second SHA256 hash
|
|
byte[] bytesOne = hexToBytes(hexOne);
|
|
String asciiTwo = new String(bytesOne, StandardCharsets.ISO_8859_1);
|
|
String hexTwo = SHA256.messageDigest(asciiTwo);
|
|
return Arrays.copyOfRange(hexToBytes(hexTwo), 0, 4);
|
|
}
|
|
|
|
private static byte[] hexToBytes(String text) {
|
|
byte[] bytes = new byte[text.length() / 2];
|
|
for ( int i = 0; i < text.length(); i += 2 ) {
|
|
final int firstDigit = Character.digit(text.charAt(i), 16);
|
|
final int secondDigit = Character.digit(text.charAt(i + 1), 16);
|
|
bytes[i / 2] = (byte) ( ( firstDigit << 4 ) + secondDigit );;
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
private static final String BITCOIN_SPECIAL_VALUE = "04";
|
|
private static final String BITCOIN_VERSION_NUMBER = "00";
|
|
}
|