Holen Sie sich einen unkomprimierten öffentlichen Schlüssel aus einer komprimierten Form

Eine ähnliche Frage hat eine Antwort in Python, die auf das bitcointalk-Forum verweist:

Aber ich würde gerne wissen, wie ein Java-Programm für die gleiche Konvertierung geschrieben werden kann? Mir ist aufgefallen, dass Java den Byte-Datentyp verwendet und ich nicht verstehen kann, wie man mit Werten arbeitet.

Es sieht so aus, als ob der Algorithmus, den Sie benötigen, auf Bitcointalk veröffentlicht wurde. Fragen Sie, ob Java die gleichen Fähigkeiten zum Konvertieren öffentlicher Schlüssel hat wie Python?
Rechts! Eigentlich habe ich angefangen, den Code zu machen, aber ich verstehe nicht, wie man den Teil p//4 (Floor Division) macht, da ich mit biginteger arbeite. Ich habe auch versucht, y=((x^3+7)^1/2)mod p zu machen, wobei p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F, aber es hat auch nicht funktioniert.
Wenn Ihre Frage ist, wie man eine bestimmte Sache in Java macht, könnte es sich lohnen, die Frage an StackOverflow zu posten. Ich bin sicher, dass es hier einige Leute gibt, die mit BigIntegers in Java gearbeitet haben, aber es gibt sicherlich mehr über SO.

Antworten (3)

Ja, Sie können einen 33-Byte-komprimierten öffentlichen Schlüssel in einen 65-Byte-unkomprimierten öffentlichen Schlüssel in Java konvertieren.

Hier ist der Code zum Ausführen der Operation. Es ist korrekt, robust und erfordert nur Java SE-Klassen (keine anderen Bibliotheken) - aber ich entschuldige mich für die Länge der Implementierung.

import java.math.BigInteger;
import java.util.Arrays;

static final BigInteger MODULUS =
    new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
static final BigInteger CURVE_A = new BigInteger("0");
static final BigInteger CURVE_B = new BigInteger("7");


// Given a 33-byte compressed public key, this returns a 65-byte uncompressed key.
byte[] decompressPubkey(byte[] compKey) {
    // Check array length and type indicator byte
    if (compKey.length != 33 || compKey[0] != 2 && compKey[0] != 3)
        throw new IllegalArgumentException();

    final byte[] xCoordBytes = Arrays.copyOfRange(compKey, 1, compKey.length);
    final BigInteger xCoord = new BigInteger(1, xCoordBytes);  // Range [0, 2^256)

    BigInteger temp = xCoord.pow(2).add(CURVE_A);
    temp = sqrtMod(temp.add(CURVE_B));
    boolean tempIsOdd = temp.testBit(0);
    boolean yShouldBeOdd = compKey[0] == 3;
    if (tempIsOdd != yShouldBeOdd)
        temp = temp.negate().mod(MODULUS);
    final BigInteger yCoord = temp;

    // Copy the x coordinate into the new
    // uncompressed key, and change the type byte
    byte[] result = Arrays.copyOf(compKey, 65);
    result[0] = 4;

    // Carefully copy the y coordinate into uncompressed key
    final byte[] yCoordBytes = yCoord.toByteArray();
    for (int i = 0; i < 32 && i < yCoordBytes.length; i++)
        result[result.length - 1 - i] = yCoordBytes[yCoordBytes.length - 1 - i];

    return result;
}


// Given x, this returns a value y such that y^2 % MODULUS == x.
BigInteger sqrtMod(BigInteger value) {
    assert (MODULUS.intValue() & 3) == 3;
    BigInteger pow = MODULUS.add(BigInteger.ONE).shiftRight(2);
    BigInteger result = value.modPow(pow, MODULUS);
    assert result.pow(2).mod(MODULUS).equals(value);
    return result;
}

Meine Bitcoin-Kryptografiebibliothek implementiert zwar die Modulo-Prime-Feldarithmetik, sollte aber auch die Funktionalität zum Dekomprimieren öffentlicher Schlüssel hinzufügen ...

Danke! Programm sieht sauber aus. Ich prüfe und gebe Bescheid.
Ich kann diese Antwort aufgrund der geringen Reputation nicht positiv bewerten.
Es schlägt für diesen komprimierten Schlüssel fehl. 022A779D25B43F04C3DD8A27B079FF4C6BECFBDE1419F1CF0B5CDA2AB001517884könnten Sie bitte überprüfen? scheitert anassert result.pow(2).mod(MODULUS).equals(value);
@light_keeper Bist du sicher, dass deine x-Koordinate von 2A77...7884 tatsächlich einen Punkt auf der elliptischen Kurve hat?
Sie müssen Ihr erstes temp(x^2+a) mit x multiplizieren, bevor Sie b hinzufügen und aufrufen sqrtMod. Damit die zweite Assertion korrekt funktioniert, müssen Sie entweder das Argument auf sqrtModModulo p reduzieren oder die Assertion auf Kongruenz statt auf Gleichheit prüfen lassen.

Sie können Bouncycastle ECPoint verwenden, um diese Konvertierung durchzuführen:

static ECParameterSpec SPEC = ECNamedCurveTable.getParameterSpec("secp256k1");

static byte[] compressedToUncompressed(byte[] compKey) {
    ECPoint point = SPEC.getCurve().decodePoint(compKey);
    byte[] x = point.getXCoord().getEncoded();
    byte[] y = point.getYCoord().getEncoded();
    // concat 0x04, x, and y, make sure x and y has 32-bytes:
    return concat(new byte[] {0x04}, x, y);
}

In openssl können Sie die Funktionen EC_POINT_point2oct und EC_POINT_oct2point verwenden, um zwischen komprimiert und unkomprimiert zu konvertieren.

Überprüfen Sie, ob das erste Oktett POINT_CONVERSION_UNCOMPRESSED enthält, wenn Sie wissen möchten, ob es komprimiert ist.