es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Convertir un array de bytes a BigInteger de forma incorrecta.

Desde que intenté simular la conexión entre mi cliente y mi servidor trabajando en paquetes, he estado luchando con un array de bytes y BigInteger.

Cuando mi cliente se conectó al servidor, este envió una respuesta que incluía una clave pública RSA, luego un paquete en hexadecimal como este:

none C70001A100408031313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131E5AA194C3DBA71F9A38AF85D43C130A462BDCBACF85ED702B5E2FECB47197DAA433E7B5ACF5EF09A07C5DDE79B7A2EED76B91090CB981E13DBE316997FCF5DC51BE40B2D02D912457D97924B94094227F8BC65DD61FE78060B55BEAAA31C64E3B36863B407F7183DABA9D98F0C9061DED36AD16407F70C4925951BAA8807EC95

Los 64 bytes después de C70001A1004080 son el exponente público RSA.
Los siguientes 128 bytes después del exponente RSA son el módulo.

Entonces intenté extraer el exponente y el módulo para regenerar una clave pública RSA. Mis pasos:

if (socket == null) socket = new Socket(loginHost, loginPort);
if (is == null) is = socket.getInputStream();
if (os == null) os = socket.getOutputStream();

int packageLength = 199;

byte[] response = IOUtils.toByteArray(is, packageLength); 

byte[] rsaPubExponentBytes = Arrays.copyOfRange(response,7,71);
byte[] rsaModuloBytes = Arrays.copyOfRange(response,71,packageLength);

BigInteger rsaPubExponentNumber = new BigInteger(1, rsaPubExponentBytes);
BigInteger rsaModuloNumber = new BigInteger(1, rsaModuloBytes);

PublicKey pub = null;

try{
    RSAPublicKeySpec spec = new RSAPublicKeySpec(rsaModuloNumber, rsaPubExponentNumber);
    KeyFactory factory = KeyFactory.getInstance("RSA");

    pub = factory.generatePublic(spec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
    e.printStackTrace();
}

Finalmente tenía una clave pública:

none Sun clave pública RSA, 1024 bits
módulo: 161275860318613570660794349609651113455378591213521007305334206887210873414776462027746458127536797075994278751416175612148660222076731030937875746805281690156725876857482174279314968596913890067397869829419019179316569148699704292044128937649866682741942026327643537075317389490824101005022833230463069842581
exponente público: 2576402308106616697565204803576809648025446765525597158856684355852417401857269811228595453373248109634555141377011045066015451991315223244608818828620081

Comparado con el RSA del servidor:

none Sun clave pública RSA, 1024 bits
módulo: 105278801605955351183032861925660079240049371193037037019863031780531990060755731057393990664319512505978954889835933668032824049054190807125138251307247771506918060803959346902566451812689470408398611041302277800747341495594297701677567604614145074223483256879948522462707345079591319551080307454838134385381
exponente público: 2576402308106616697565204803576809648025446765525597158856684355852417401857269811228595453373248109634555141377011045066015451991315223244608818828620081

Luego, el módulo estaba incorrecto. Verifiqué duplicando la conversión de BigInteger a array de bytes y lo mostré en hexadecimal:

System.out.println(bytesToHex(rsaPubExponentNumber.toByteArray()));
System.out.println(bytesToHex(rsaModuloNumber.toByteArray()));

private String bytesToHex(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (byte b : bytes) {
       sb.append(String.format("%02X ", b));
    }
    return sb.toString().replaceAll(" ", "");
}

El resultado es:

Exponente:
31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131

Módulo
00E5AA194C3DBA71F9A38AF85D43C130A462BDCBACF85ED702B5E2FECB47197DAA433E7B5ACF5EF09A07C5DDE79B7A2EED76B91090CB981E13DBE316997FCF5DC51BE40B2D02D912457D97924B94094227F8BC65DD61FE78060B55BEAAA31C64E3B36863B407F7183DABA9D98F0C9061DED36AD16407F70C4925951BAA8807EC95

Ahora el módulo tiene 00 al principio y se generó la clave pública, pero una incorrecta.

También intenté:

BigInteger exponente = new BigInteger(exponenteHexString, 16);
BigInteger módulo = new BigInteger(móduloHexString, 16);

Sin embargo, el resultado sigue siendo el mismo. ¿Cómo puedo solucionar esto?

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    El problema en este caso es que el valor del módulo obtenido del paquete de respuesta no tiene el byte cero principal. En Java, el bit de signo de un BigInteger es representado por el bit más significativo del byte más significativo. Entonces, si el bit principal del primer byte es 1, Java considera el BigInteger como un número negativo.

    Para solucionar este problema, necesitas agregar un byte cero principal al array de bytes del módulo antes de crear el objeto BigInteger. Puedes hacer esto comprobando el bit de signo del primer byte y agregando un byte cero principal si es necesario. Aquí está el código actualizado:

    byte[] rsaModuloBytes = Arrays.copyOfRange(response, 71, packageLength);
    if ((rsaModuloBytes[0] & 0x80) != 0) {
        // Agregar byte cero principal si el bit de signo está activado
        byte[] paddedModuloBytes = new byte[rsaModuloBytes.length + 1];
        System.arraycopy(rsaModuloBytes, 0, paddedModuloBytes, 1, rsaModuloBytes.length);
        rsaModuloBytes = paddedModuloBytes;
    }
    
    BigInteger rsaModuloNumber = new BigInteger(1, rsaModuloBytes);
    

    Este código comprueba el bit de signo del primer byte del array de bytes del módulo usando una operación AND a nivel de bit con 0x80 (que es la máscara para el bit más significativo). Si el bit de signo está activado, crea un nuevo array de bytes con un byte cero principal adicional y copia el array de bytes del módulo original al nuevo array comenzando desde el índice 1. Finalmente, crea el objeto BigInteger usando el array de bytes del módulo rellenado.

    Al rellenar el array de bytes del módulo con un byte cero principal, te aseguras de que el objeto BigInteger se cree con un valor positivo, que es necesario para representar correctamente la clave pública RSA.

Comments are closed.