I am attempting to convert some BigInteger objects and math from Java to C#.
The Java flow is as follows:
1. Construct 2 BigIntegers from a base-10 string (i.e. 0-9 values).
2. Construct a third BigInteger from an inputted byte array.
3. Create a fourth BigInteger as third.modPow(first, second).
4. Return the byte result of fourth.
The main complications in converting to C# seem to consist of endianness and signed/unsigned values.
I have tried a couple different ways to convert the initial 2 BigIntegers from Java->C#. I believe that using the base-10 string with BigInteger.Parse will work as intended, but I am not completely sure.
Another complication comes from the use of a BinaryReader/BinaryWriter implementation, in C#, that is already big-endian (like Java). I use the BR/BW to supply the byte array to create the third BigInteger and consume the byte array produced from the modPow (the fourth BigInteger).
I have tried reversing the byte arrays for input and output in every way, and still do not get the expected output.
Java:
public static byte[] doMath(byte[] input)
{
BigInteger exponent = new BigInteger("BASE-10-STRING");
BigInteger mod = new BigInteger("BASE-10-STRING");
BigInteger bigInput = new BigInteger(input);
return bigInput.modPow(exponent, mod).toByteArray();
}
C#:
public static byte[] CSharpDoMath(byte[] input)
{
BigInteger exponent = BigInteger.Parse("BASE-10-STRING");
BigInteger mod = BigInteger.Parse("BASE-10-STRING");
// big->little endian
byte[] reversedBytes = input.Reverse().ToArray();
BigInteger bigInput = new BigInteger(reversedBytes);
BigInteger output = BigInteger.ModPow(bigInput, exponent, mod);
// little->big endian
byte[] bigOutput = output.ToByteArray().Reverse().ToArray();
return bigOutput;
}
I need the same output from both.
Related
I have the following problem in Java. I am using an encryption algorithm that can produce negative bytes as outcome, and for my purposes I must be able to deal with them in binary. For negative bytes, the first or most significant bit in the of the 8 is 1. When I am trying to convert binary strings back to bytes later on, I am getting a NumberFormatException because my byte is too long. Can I tell Java to treat it like an unsigned byte and end up with negative bytes? My code so far is this:
private static String intToBinByte(int in) {
StringBuilder sb = new StringBuilder();
sb.append("00000000");
sb.append(Integer.toBinaryString(in));
return sb.substring(sb.length() - 8);
}
intToBinByte(-92); // --> 10100100
Byte.parseByte("10100100", 2) // --> NumberFormatException
Value out of range. Value:"10100100" Radix:2
Is there a better way to parse signed Bytes from binary in Java?
Thanks in advance!
You can just parse it with a larger type, then cast it to byte. Casting simply truncates the number of bits:
byte b = (byte) Integer.parseInt("10100100", 2);
I have written the following function to solve the problem:
private static byte binStringToByte(String in) {
byte ret = Byte.parseByte(in.substring(1), 2);
ret -= (in.charAt(0) - '0') * 128;
return ret;
}
Im working on a program that is an implementation of the RSA encryption algorithm, just as a personal exercise, its not guarding anyone's information or anything. I am trying to understand how a plaintext passage is being interpreted numerically, allowing it to be encrypted. I understand that most UTF-8 characters end up only using 1 byte of space, and not the 2 bytes one might think, but thats about it. Heres my code:
BigInteger ONE = new BigInteger("1");
SecureRandom rand = new SecureRandom();
BigInteger d, e, n;
BigInteger p = BigInteger.probablePrime(128, rand);
BigInteger q = BigInteger.probablePrime(128, rand);
BigInteger phi = (p.subtract(ONE)).multiply(q.subtract(ONE));
n = p.multiply(q);
e = new BigInteger("65537");
d = e.modInverse(phi);
String string = "test";
BigInteger plainText = new BigInteger(string.getBytes("UTF-8"));
BigInteger cipherText = plainText.modPow(e, n);
BigInteger originalMessage = cipherText.modPow(d, n);
String decrypted = new String(originalMessage.toByteArray(),"UTF-8");
System.out.println("original: " + string);
System.out.println("decrypted: " + decrypted);
System.out.println(plainText);
System.out.println(cipherText);
System.out.println(originalMessage);
System.out.println(string.getBytes("UTF-8"));
byte byteArray[] = string.getBytes("UTF-8");
for(byte littleByte:byteArray){
System.out.println(littleByte);
}
It outputs:
original: test
decrypted: test
1952805748
16521882695662254558772281277528769227027759103787217998376216650996467552436
1952805748
[B#60d70b42
116
101
115
116
Maybe more specifically i am wondering about this line:
BigInteger plainText = new BigInteger(string.getBytes("UTF-8"));
Does each letter of "test" have a value, and they are literraly added together here? Like say t=1,e=2,s=3,t=1 for example, if you get the bytes from that string, do you end up with 7 or are the values just put together like 1231? And why does
BigInteger plainText = new BigInteger(string.getBytes("UTF-8")); output 1952805748
I am trying to understand how a plaintext passage is being interpreted numerically, allowing it to be encrypted.
It really boils down to understanding what this line does:
BigInteger plainText = new BigInteger(string.getBytes("UTF-8"));
Lets break it down.
We start with a String (string). A Java string is a sequence of characters represented as Unicode code points (encoded in UCS-16 ...).
The getBytes("UTF-8") then encodes the characters as a sequence of bytes, and returns them in a newly allocated byte array.
The BigInteger(byte[]) constructor interprets that byte array as a number. As the javadoc says:
Translates a byte array containing the two's-complement binary representation of a BigInteger into a BigInteger. The input array is
assumed to be in big-endian byte-order: the most significant byte is
in the zeroth element.
The method that is being used here is not giving an intrisically meaningful number, just one that corresponds to the byte-encoded string. And going from the byte array to the number is simply treating the bytes as a bit sequence that represents an integer in 2's complement form ... which is the most common representation for integers on modern hardware.
The key thing is that the transformation from the text to the (unencrypted) BigInteger is lossless and reversible. Any other transformation with those properties could be used.
References:
The Wikipedia page on 2's Complement representation
The Wikipedia page on the UTF-8 text encoding scheme
javadoc BigInteger(byte[])
javadoc String.getBytes(String)
Im still not quite understanding how the the UTF-8 values for each character in "test", 116,101,115,116 respectively come together to form 1952805748?
Convert the numbers 116,101,115,116 to hex.
Convert the number 1952805748 to hex
Compare them
See the pattern?
The answer is in the output, "test" is encoded into array of 4 bytes [116, 101, 115, 116]. This is then interperted by BigInteger as binary integer representation. The value can be calculated this way
value = (116 << 24) + (101 << 16) + (115 << 8) + 116;
I want to compare two multiplication methods implemented in Java which use shift operations on big numbers. Thus I need sufficiently large BigIntegers.
Since I want to compare them bit-wise, what would be the best approach to generate BigIntegers with n bits which are fully used in the multiplication operation.
My approach so far is this:
byte[] bits = new byte[bitLength];
BigInteger number = new BigInteger(bits).flipBit(bitLength);
How about this:
import java.math.BigInteger;
public class Test
{
public static void main(String[] args)
{
int bits = 3;
BigInteger value = BigInteger.ZERO
.setBit(bits)
.subtract(BigInteger.ONE);
System.out.println(value); // Prints 7 == 111 in binary
}
}
In other words, set the bit which is one higher than you want, then subtract one to get a value which uses all the lower bits.
I would like to convert an integer array of values, which was original were bytes.
First, make sure you know in which format your int[] is meant to be interpreted.
Each int can be seen as consisting of four bytes, and these bytes together can be converted to an BigInteger. The details are the byte order - which byte is the most and which one the least significant?
Also, do you have a signed or unsigned number?
A simple way to convert your ints to bytes (for latter use in a BigInteger constructor) would be to use ByteBuffer and wrap an IntBuffer around it.
public BigInteger toBigInteger(int[] data) {
byte[] array = new byte[data.length * 4];
ByteBuffer bbuf = ByteBuffer.wrap(array);
IntBuffer ibuf = bbuf.asIntBuffer();
ibuf.put(data);
return new BigInteger(array);
}
Obvious adaptions would be to set the byte order of bbuf, or use another BigInteger constructor (for unsigned).
Well, what about new BigInteger(byte[] val)?
To quote the API docs I linked to:
Translates a byte array containing the two's-complement binary representation of a BigInteger into a BigInteger. The input array is assumed to be in big-endian byte-order: the most significant byte is in the zeroth element.
It seems I have a two's complement issue with Java's BigInteger.
I have a 64-bit integer where only the msb and the second msb are set to 1, the rest is 0.
In decimal this comes up to: -4611686018427387904
The Java side of my application receives this decimal number as a string, and converts it to BigInteger like so:
BigInteger bi = new BigInteger("-4611686018427387904", 10);
Then, it needs to display this number both in binary and hex forms.
I tried to use:
String bin = bi.toString(2);
String hex = bi.toString(16);
but I'm getting:
-100000000000000000000000000000000000000000000000000000000000000
-4000000000000000
whereas I expect to get:
1100000000000000000000000000000000000000000000000000000000000000
c000000000000000
Any tips?
Number always fits in 64 bits:
If your number always fits in 64 bits you can put it in a long and then print the bits / hex digits.
long l = bi.longValue();
String bin = Long.toBinaryString(l);
String hex = Long.toHexString(l);
System.out.println(bin);
System.out.println(hex);
Number may not always fit in 64 bits:
If the number does not always fit in 64 bits, you'll have to solve it "manually". To convert a number to it's two's complement representation you do the following:
If number is positive, do nothing
If number is negative:
Convert it to its absolute value
Complement the bits
Add 1
For a BigInteger the conversion looks as follows:
if (bi.compareTo(BigInteger.ZERO) < 0)
bi = bi.abs().not().add(BigInteger.ONE);
If you print it using bi.toString(2) you'll still get the sign character, instead of a leading 1. This can be solved by simply appending .replace('-', '1') to the string.
There is a BigInteger.toByteArray() method, that returns two's complement representation of BigInteger as a byte[]. All you need is to print that array in hex or binary form:
byte[] bs = bi.toByteArray();
for (byte b: bs) {
System.out.print(String.format("%02X", 0xff & b));
}
The binary number 1100000000000000000000000000000000000000000000000000000000000000 is definitely a positive number, right. It's equal to 2^63 + 2^62.
I don't see why you'd expect a negative number to become positive when you convert to base 2 or base 16.
You are confusing the base n representation with the internal representation of numbers.
If the number is 64 bits or less, then the simple way to solve this is to convert to a long and then use Long.toHexString().
what you mean?
Do you want to get Two's complement?
if you mean that, maybe i can give you an example
import java.util.*;
public class TestBina{
static void printBinaryInt(int i){
System.out.println("int:"+i+",binary:");
System.out.print(" ");
for(int j=31;j>=0;j--)
if(((1<<j)&i)!=0)
System.out.print("1");
else
System.out.print("0");
System.out.println();
}
public static void main(String [] args){
Random rand = new Random();
int i = rand.nextInt();
int j = rand.nextInt();
printBinaryInt(i);
printBinaryInt(j);
printBinaryInt(10);
printBinaryInt(-10);
}
}