Una pregunta similar tiene una respuesta en Python que enlaza con el foro de bitcointalk:
Pero me gustaría saber cómo se puede escribir un programa Java para la misma conversión. Noté que Java usa el tipo de datos byte y no puedo entender cómo operar con valores.
Sí, puede convertir una clave pública comprimida de 33 bytes en una clave pública sin comprimir de 65 bytes en Java.
Aquí está el código para realizar la operación. Es correcto, robusto y solo requiere clases de Java SE (no otras bibliotecas), pero me disculpo por la longitud de la implementación.
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;
}
Mi biblioteca de criptografía de Bitcoin implementa la aritmética de campo modulo-prime, pero también debería agregar la funcionalidad para descomprimir claves públicas...
022A779D25B43F04C3DD8A27B079FF4C6BECFBDE1419F1CF0B5CDA2AB001517884
¿podrías comprobarlo? falla enassert result.pow(2).mod(MODULUS).equals(value);
temp
(x ^ 2 + a) por x antes de agregar b y llamar a sqrtMod
. Además, para que la segunda afirmación funcione correctamente, debe reducir el argumento al sqrtMod
módulo p o hacer que la afirmación verifique la congruencia en lugar de la igualdad.Puede usar bouncycastle ECPoint para hacer esta conversión:
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);
}
En openssl, puede usar las funciones EC_POINT_point2oct y EC_POINT_oct2point para convertir entre comprimido y sin comprimir.
Compruebe si el primer octeto contiene POINT_CONVERSION_UNCOMPRESSED, si desea saber si está comprimido.
Jestin
profesor Zoom
Jestin