Related
tldr: generated rsa key pair using node-forge, encrypted hello and converted it to base64 in order to decrypt it using the same key pair (imported using pem files) in java but it throws an exception while the key pair is working fine with data encrypted by java
I am generating a key pair in node which works fine for decrypting and encrypting in both java and node, but I am unable to decrypt the encrypted value from node in java
the key pair is generated using the following nodejs code
var bip39 = require("bip39");
var forge = require("node-forge");
async function test() {
var mnemonic = "fever draw mind boil renew fire sleep palace useful ritual wood snake april polar brown off round zebra swallow health grass drink jelly beef";
console.log(mnemonic)
let seed = await bip39.mnemonicToSeed(mnemonic);
console.log(seed.toString("hex"))
const prng = forge.random.createInstance();
prng.seedFileSync = () => seed
const {
privateKey,
publicKey
} = forge.pki.rsa.generateKeyPair({
bits: 1024,
prng,
workers: 2
})
console.log(await forge.pki.publicKeyToRSAPublicKeyPem(publicKey));
console.log(await forge.pki.privateKeyToPem(privateKey));
let encrypted = publicKey.encrypt("hello");
var previousBase ="WTq03NoOtGHX+Xs1Mu6bbQmjQ+xSssj2mSeRf55+lw/tVaGRTI7KQZzu8DFqNJS4kzu6iJjqKTmnvtAMzPOo4KAmEtZAMDEZw7D1Y5ptb0cDP0SgROsZgZr5XpGbifQMDGT+rZr/5EfFOXdwDvUy1rUABAKpSbXFZTF64UFM4lk=";
var previous = atob(previousBase);
let base = btoa(encrypted);
console.log(base);
var x = new TextEncoder().encode(previousBase);
var all = "";
for (var i = 0; i < x.length; i++) {
all += x[i] + ", ";
}
console.log(all.substring(0, all.length - 2))
console.log(await privateKey.decrypt(previous));
console.log(await privateKey.decrypt(encrypted));
};
test();
since I am trying to decrypt/encrypt using the same private/public key in Java I decided to add a few logs so I have a base to work with
fever draw mind boil renew fire sleep palace useful ritual wood snake april polar brown off round zebra swallow health grass drink jelly beef
8f445e53ea42708dd4be30c646261c9fc3352125c42f8f5ead86d44c15adbac4a46ffe1dca758f99bf46c7b939fd3db0f2f3000b792bc9192156e5ef5936a732
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAK0HXXtavoJkYXYSBFvb+JINZ9MFZANLWRVOu69iiP6PgILqEbV8ybFm
vdSCuZu0BB/P5f0uW/0fw0149UZxXltrkPZHqYLPckXOxgRD3LAWbI+vwV0UqU0X
597hg5E8UnV+2S5VG4/QxpAS5iiAPwT96dxjxEhZ3mRqLX74BsrDAgMBAAE=
-----END RSA PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCtB117Wr6CZGF2EgRb2/iSDWfTBWQDS1kVTruvYoj+j4CC6hG1
fMmxZr3UgrmbtAQfz+X9Llv9H8NNePVGcV5ba5D2R6mCz3JFzsYEQ9ywFmyPr8Fd
FKlNF+fe4YORPFJ1ftkuVRuP0MaQEuYogD8E/encY8RIWd5kai1++AbKwwIDAQAB
AoGAFDPb8l8yBz95MbQA1kjkyQjPqo/ikY/motpCh8PVgwN2WdLCppIfaps4Zuus
iEWIhb5ceCdFjlR7FTyeRs9N2OTC2Mcp1n1sxfHNFMGPOsxyMAaIw9UhFzpr2cHy
PXiPbyg+y4EMwEbB2js7Zf2A2wNSda4VBkYUZIkBXIxmUwECQQD9a70jjdfN6Ldj
M6ZqPPg/V5GGsEohfsmsSJKUxzZ3AmFqrwzDmclbfviVmqTiULPDbl4Tb6qvhLc3
Qk//DUijAkEArsosXFv7DkZmMA3MTE8lURgOD509Ts1+4wWIDsbP54pvFoIPX7eG
6NR48SEvjAmZfGgwi6gbwUdBbRFFQwP3YQJAfuPmdZn9V5XR1XM0PXe/2X+QV3+H
7tOcSY6hDqvdIqPngVKbMombYBvofohLTSKZkB6ALn04WuA6GQo0IgJVvwJARvsH
JMKdo2BnYyBXVK3XY6U3IJQkL3o4Cw1WAVovV8HZ9vP+NkqbWLXgH1vwqRfE4saU
4EH2c3jyUs5uqBZWQQJAajdCCVRyPghnfqhzWl68gfvrEATDTWRt4UKY46IP+QUx
islQ2e9ZU+wbjBmqM5zd1oG9yUsxD8c5X0mCyZ1wSg==
-----END RSA PRIVATE KEY-----
HWqv4dvDtLPc8ZhBbTI4JJ89XgxASKacZKuaz+0HF0kInt9R/tDgv2lkNPl7RWC/SNacqY0DnBL1uAQvg8r75nVo7sZ29hhjph5n5ucBNXcT9OSYgGY4fjSmMLvLY3qVSYItSLsIbXrfemWDF2jaV2FbDb4dBCaeC02PjIJQGnw=
87, 84, 113, 48, 51, 78, 111, 79, 116, 71, 72, 88, 43, 88, 115, 49, 77, 117, 54, 98, 98, 81, 109, 106, 81, 43, 120, 83, 115, 115, 106, 50, 109, 83, 101, 82, 102, 53, 53, 43, 108, 119, 47, 116, 86, 97, 71, 82, 84, 73, 55, 75, 81, 90, 122, 117, 56, 68, 70, 113, 78, 74, 83, 52, 107, 122, 117, 54, 105, 74, 106, 113, 75, 84, 109, 110, 118, 116, 65, 77, 122, 80, 79, 111, 52, 75, 65, 109, 69, 116, 90, 65, 77, 68, 69, 90, 119, 55, 68, 49, 89, 53, 112, 116, 98, 48, 99, 68, 80, 48, 83, 103, 82, 79, 115, 90, 103, 90, 114, 53, 88, 112, 71, 98, 105, 102, 81, 77, 68, 71, 84, 43, 114, 90, 114, 47, 53, 69, 102, 70, 79, 88, 100, 119, 68, 118, 85, 121, 49, 114, 85, 65, 66, 65, 75, 112, 83, 98, 88, 70, 90, 84, 70, 54, 52, 85, 70, 77, 52, 108, 107, 61
hello
hello
the first challenge was to actually get the rsa key objects in Java to initialize without any exceptions, which in fact required me to add the dependency
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
now within java I am able to encrypt and decrypt using the keys, but I am unable to decrypt anything that was generated within nodejs as it throws this exception
javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:344)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
at Test.main(Test.java:44)
my current code for reference looks like this
import javax.crypto.Cipher;
import javax.xml.bind.DatatypeConverter;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class Test {
static {
java.security.Security.addProvider(
new org.bouncycastle.jce.provider.BouncyCastleProvider()
);
}
public static void main(String[] args) {
try {
RSAPrivateKey privateKey = parsePrivateKey(Files.readAllBytes(Paths.get("pri.pem")));
RSAPublicKey pub = readPublicKey(new File("pub.pem"));
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, pub);
String test = "hello";
byte[] testBytes = test.getBytes(StandardCharsets.UTF_8);
byte[] encryptedMessageBytes = encryptCipher.doFinal(testBytes);
String base64 = "WTq03NoOtGHX+Xs1Mu6bbQmjQ+xSssj2mSeRf55+lw/tVaGRTI7KQZzu8DFqNJS4kzu6iJjqKTmnvtAMzPOo4KAmEtZAMDEZw7D1Y5ptb0cDP0SgROsZgZr5XpGbifQMDGT+rZr/5EfFOXdwDvUy1rUABAKpSbXFZTF64UFM4lk=";
byte[] decodedBytes = Base64.getDecoder().decode(base64);
String decodedString = new String(decodedBytes);
byte[] secretMessageBytes = decodedString.getBytes(StandardCharsets.UTF_8.name());
Cipher decryptCipher = Cipher.getInstance("RSA");
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
System.out.println(new String(decryptCipher.doFinal(encryptedMessageBytes)));
byte[] decryptedMessageBytes = decryptCipher.doFinal(secretMessageBytes);
System.out.println(new String(decryptedMessageBytes));
} catch (Exception e) {
e.printStackTrace();
}
}
public static RSAPrivateKey parsePrivateKey(byte[] b) throws InvalidKeySpecException, NoSuchAlgorithmException {
byte[] privateKeyBytes = parseDERFromPEM(b);
return generatePrivateKeyFromDER(privateKeyBytes);
}
public static byte[] parseDERFromPEM(byte[] b) {
return DatatypeConverter.parseBase64Binary(parseKeyData(b));
}
public static String parseKeyData(byte[] b) {
String data = new String(b);
String[] tokens = data.split("-----BEGIN RSA PRIVATE KEY-----");
tokens = tokens[1].split("-----END RSA PRIVATE KEY-----");
return tokens[0];
}
public static RSAPrivateKey generatePrivateKeyFromDER(byte[] b) throws InvalidKeySpecException, NoSuchAlgorithmException {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(b);
KeyFactory factory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) factory.generatePrivate(spec);
}
public static RSAPublicKey readPublicKey(File file) throws Exception {
String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());
String publicKeyPEM = key
.replace("-----BEGIN PUBLIC KEY-----", "")
.replaceAll(System.lineSeparator(), "")
.replace("-----END PUBLIC KEY-----", "");
byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
}
}
is it somehow impossible todo what I am trying todo or am I just missing something somewhere since the keys are working with java for encryption and decryption as long as it was encrypted using java.
I believe all data required to replicate my issue is included in this post - if any further information is needed please let me know and I will provide
Encrypted data using modern (computer) ciphers is not text. And the data that can be encrypted can be any bits, not just text, although they are still called 'ciphertext' and 'plaintext' following a historical tradition from past centuries where they actually were text. Neither is other cryptographic data, such as digital signatures, hashes, keys, certificates, and such, although some of them are converted to and from text forms such as (several variants of) base64, hex, and PEM. Your node/forge code represents the RSA-encrypted data in base64, as is common to handle this, but your Java code after decoding the base64 correctly does the following
String decodedString = new String(decodedBytes);
byte[] secretMessageBytes = decodedString.getBytes(StandardCharsets.UTF_8.name());
which tries to treat it as text. This does not work, and turns much of your data into garbage, which also happens to make it longer and trigger the exception you got. You need to use the result of the base64 decoding directly in the decrypt call.
In addition your posted Java code doesn't and can't work on the public-key format you posted. You need to either use forge's publicKeyToPem (which produces the correct 'generic' PUBLIC KEY format -- not RSA PUBLIC KEY) or convert your posted output with something like openssl rsa -RSAPublicKey_in; I did the latter.
Finally, it isn't sufficient to just add Bouncy to the provider list. You must either:
specify the actual provider object in KeyFactory.getInstance
have Bouncy in the list (anywhere) and specify "BC" in KeyFactory.getInstance
put Bouncy at the beginning of the list with insertProviderAt(new org.bouncy...,1)
With those three changes, and some trivial simplification and hardcoded data for convenience, the following gives hello as expected:
String pubbad = "-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBAK0HXXtavoJkYXYSBFvb+JINZ9MFZANLWRVOu69iiP6PgILqEbV8ybFm\n"
+"vdSCuZu0BB/P5f0uW/0fw0149UZxXltrkPZHqYLPckXOxgRD3LAWbI+vwV0UqU0X\n"
+"597hg5E8UnV+2S5VG4/QxpAS5iiAPwT96dxjxEhZ3mRqLX74BsrDAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n";
String pubfix = "-----BEGIN PUBLIC KEY-----\n"
+"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCtB117Wr6CZGF2EgRb2/iSDWfT\n"
+"BWQDS1kVTruvYoj+j4CC6hG1fMmxZr3UgrmbtAQfz+X9Llv9H8NNePVGcV5ba5D2\n"
+"R6mCz3JFzsYEQ9ywFmyPr8FdFKlNF+fe4YORPFJ1ftkuVRuP0MaQEuYogD8E/enc\n"
+"Y8RIWd5kai1++AbKwwIDAQAB\n"
+"-----END PUBLIC KEY-----\n";
String prvpem = "-----BEGIN RSA PRIVATE KEY-----\n"
+"MIICWwIBAAKBgQCtB117Wr6CZGF2EgRb2/iSDWfTBWQDS1kVTruvYoj+j4CC6hG1\n"
+"fMmxZr3UgrmbtAQfz+X9Llv9H8NNePVGcV5ba5D2R6mCz3JFzsYEQ9ywFmyPr8Fd\n"
+"FKlNF+fe4YORPFJ1ftkuVRuP0MaQEuYogD8E/encY8RIWd5kai1++AbKwwIDAQAB\n"
+"AoGAFDPb8l8yBz95MbQA1kjkyQjPqo/ikY/motpCh8PVgwN2WdLCppIfaps4Zuus\n"
+"iEWIhb5ceCdFjlR7FTyeRs9N2OTC2Mcp1n1sxfHNFMGPOsxyMAaIw9UhFzpr2cHy\n"
+"PXiPbyg+y4EMwEbB2js7Zf2A2wNSda4VBkYUZIkBXIxmUwECQQD9a70jjdfN6Ldj\n"
+"M6ZqPPg/V5GGsEohfsmsSJKUxzZ3AmFqrwzDmclbfviVmqTiULPDbl4Tb6qvhLc3\n"
+"Qk//DUijAkEArsosXFv7DkZmMA3MTE8lURgOD509Ts1+4wWIDsbP54pvFoIPX7eG\n"
+"6NR48SEvjAmZfGgwi6gbwUdBbRFFQwP3YQJAfuPmdZn9V5XR1XM0PXe/2X+QV3+H\n"
+"7tOcSY6hDqvdIqPngVKbMombYBvofohLTSKZkB6ALn04WuA6GQo0IgJVvwJARvsH\n"
+"JMKdo2BnYyBXVK3XY6U3IJQkL3o4Cw1WAVovV8HZ9vP+NkqbWLXgH1vwqRfE4saU\n"
+"4EH2c3jyUs5uqBZWQQJAajdCCVRyPghnfqhzWl68gfvrEATDTWRt4UKY46IP+QUx\n"
+"islQ2e9ZU+wbjBmqM5zd1oG9yUsxD8c5X0mCyZ1wSg==\n"
+"-----END RSA PRIVATE KEY-----\n";
//RSAPrivateKey privateKey = parsePrivateKey(Files.readAllBytes(Paths.get("pri.pem")));
byte[] prvder = Base64.getDecoder().decode( prvpem.replaceAll("-----(BEGIN|END) RSA PRIVATE KEY-----","").replaceAll("\n","") );
RSAPrivateKey privateKey = (RSAPrivateKey) KeyFactory.getInstance("RSA","BC") .generatePrivate(new PKCS8EncodedKeySpec(prvder) );
// actually not PKCS8, but BCprov handles this as a special case
//RSAPublicKey pub = readPublicKey(new File("pub.pem"));
byte[] pubder = Base64.getDecoder().decode( pubfix.replaceAll("-----(BEGIN|END) PUBLIC KEY-----","").replaceAll("\n","") );
RSAPublicKey pub = (RSAPublicKey) KeyFactory.getInstance("RSA") .generatePublic(new X509EncodedKeySpec(pubder) );
String base64 = "WTq03NoOtGHX+Xs1Mu6bbQmjQ+xSssj2mSeRf55+lw/tVaGRTI7KQZzu8DFqNJS4kzu6iJjqKTmnvtAMzPOo4KAmEtZAMDEZw7D1Y5ptb0cDP0SgROsZgZr5XpGbifQMDGT+rZr/5EfFOXdwDvUy1rUABAKpSbXFZTF64UFM4lk=";
byte[] decodedBytes = Base64.getDecoder().decode(base64);
//BAD String decodedString = new String(decodedBytes);
//BAD byte[] secretMessageBytes = decodedString.getBytes(StandardCharsets.UTF_8.name());
Cipher decryptCipher = Cipher.getInstance("RSA");
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedMessageBytes = decryptCipher.doFinal(decodedBytes);
System.out.println(new String(decryptedMessageBytes));
And merry Xmas, until someone edits this out because Stack is supposed to be impersonal.
Since today I'm fronting a really weird error related to byte[] to String conversion.
Here is the code:
private static final byte[] test_key = {-112, -57, -45, 125, 91, 126, -118, 13, 83, -60, -119, 57, 38, 118, -115, -52, -92, 39, -24, 75, 59, -21, 88, 84, 66, -125};
public static void main(String[] args) {
byte[] encryptedArray = xor("ciao".getBytes(), test_key);
System.out.println("Encrypted arrray: " + Arrays.toString(encryptedArray));
final String encrypted = new String(encryptedArray);
System.out.println("Length: " + new String(encryptedArray).length());
System.out.println(Arrays.toString(encrypted.getBytes()));
System.out.println("Encrypted value: " + encrypted);
System.out.println("Decrypted value: " + new String(xor(encrypted.getBytes(), test_key)));
}
private static byte[] xor(byte[] data, byte[] key) {
byte[] result = new byte[data.length];
for (int i = 0; i < data.length; i++) {
result[i] = (byte) (data[i] ^ key[i % key.length]);
}
return result;
}
My output is:
Encrypted arrray: [-13, -82, -78, 18]
Length: 2
[-17, -65, -67, 18]
Encrypted value: �
Decrypted value: xno
Why does length() return 2? What am I missing?
There is no 1-to-1 mapping between byte and char, rather it depends on the charset you use. Strings are logically chars sequences. So if you want to convert between chars and bytes, you need a character encoding, which specifies the mapping from chars to bytes, and vice versa. Your bytes in encryptedArray are first converted to Unicode string, which attempts to create UTF-8 char sequence from these bytes.
If you want to use String and revert back the exact bytes, you need to do a Base64 of the encryptedArray and then do a new String() of it:
String encoded = new String(Base64.getEncoder().encode(encryptedArray));
To retreive, just decode:
Base64.getDecoder().decode(encoded);
I just thought of a good way of showing what happens by simply replacing the new String(byte[]) method by another one, which is why I will answer the question. This one performs the same basic action as the constructor, with one change: it throws an exception if any invalid characters are found.
private static final byte[] test_key = {-112, -57, -45, 125, 91, 126, -118, 13, 83, -60, -119, 57, 38, 118, -115, -52, -92, 39, -24, 75, 59, -21, 88, 84, 66, -125};
public static void main(String[] args) throws Exception {
byte[] encryptedArray = xor("ciao".getBytes(), test_key);
System.out.println("Encrypted arrray: " + Arrays.toString(encryptedArray));
final String encrypted = new String(encryptedArray);
// original
System.out.println("Length: " + new String(encryptedArray).length());
// replacement
System.out.println("Length: " + decode(encryptedArray).length());
System.out.println(Arrays.toString(encrypted.getBytes()));
System.out.println("Encrypted value: " + encrypted);
System.out.println("Decrypted value: " + new String(xor(encrypted.getBytes(), test_key)));
}
private static String decode(byte[] encryptedArray) throws CharacterCodingException {
var decoder = Charset.defaultCharset().newDecoder();
decoder.onMalformedInput(CodingErrorAction.REPORT);
var decoded = decoder.decode(ByteBuffer.wrap(encryptedArray));
return decoded.toString();
}
private static byte[] xor(byte[] data, byte[] key) {
byte[] result = new byte[data.length];
for (int i = 0; i < data.length; i++) {
result[i] = (byte) (data[i] ^ key[i % key.length]);
}
return result;
}
The method is called decode because that's what you are actually doing: you are decoding the bytes to a text. A character encoding is the encoding of characters as bytes, which means that the opposite must be decoding after all.
As you will see, the above will first print out 2 if your platform uses the default UTF-8 encoding (Linux, Android, MacOS). You can get the same result by replacing Charset.defaultCharset() with StandardCharsets.UTF_8 on Windows which uses the Windows-1252 charset instead (a single byte encoding which is an expansion of Latin-1, which itself is an expansion of ASCII). However, it will generate the following exception if you use the decode method:
java.nio.charset.MalformedInputException: Input length = 3
at java.base/java.nio.charset.CoderResult.throwException(CoderResult.java:274)
at java.base/java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:815)
at StackExchange/com.stackexchange.so.ShowBadEncoding.decode(ShowBadEncoding.java:36)
at StackExchange/com.stackexchange.so.ShowBadEncoding.main(ShowBadEncoding.java:24)
Now maybe you'd expect 4 here, the size of the byte array. But note that UTF-8 characters may be encoded over multiple bytes. The error occurs not on the entire string, but on the last character it is trying to read. Obviously it is expecting a longer encoding based on the previous byte values.
If you replace REPORT with the default decoding action REPLACE (heh) you will see that the result is identical to the constructor, and length() will now return the value 2 again.
Of course, Topaco is correct when he says you need to use base 64 encoding. This encodes bytes to characters instead so that all of the meaning of the bytes is maintained, and the reverse is of course the decoding of text back to bytes.
The elements of a String are not bytes, they are chars. A char is not a byte.
There are many ways of converting a char to a sequence of bytes (i.e., many character-set encodings).
Not every sequence of chars can be converted to a sequence of bytes; there is not always a mapping for every char. It depends on your chosen character-set encoding.
Not every sequence of bytes can be converted to a String; the bytes have to be syntactically valid for the specified character set.
So, I am trying to store 3 longs to a file, but it will a lot of data so I convert them to byte arrays and save them. My current method for saving them:
try (FileOutputStream output = new FileOutputStream(path, true)) {
//Put the data into my format
byte[] data = new byte[24];
main.getLogger().log(Level.INFO, "Saving most sig bits");
System.arraycopy(ByteUtils.longToBytes(uuid.getMostSignificantBits()), 0, data, 0, 8);
System.arraycopy(ByteUtils.longToBytes(uuid.getLeastSignificantBits()), 0, data, 8, 8);
System.arraycopy(ByteUtils.longToBytes(player.getTokens()), 0, data, 16, 8);
//Write data in the format
output.write(data);
}
longToBytes method:
private static ByteBuffer buffer = ByteBuffer.allocate(8);
public static byte[] longToBytes(long x) {
System.out.println(x);
buffer.putLong(0, x);
return buffer.array();
}
The byte array gets saved to the file, but the first byte gets truncated. the print statement in longToByes prints 8 three times.
The original longs are:
-9089798603852198353, -5339652910133477779, 5992
If I print the byte array I get:
-127, -38, -116, 84, 97, -116, 78, 47, -75, -27, -67, -8, 11, -100, -2, 109, 0, 0, 0, 0, 0, 0, 23, 104 (24 bytes)
But in the file I see:
ÚŒTaŒN/µå½ø(VT symbol)œþm(nul)(nul)(nul)(nul)(nul)(nul)(etb)h
which is 23 bytes (the first box doesn't show in notepad++)
but if I read it using
bytes = IOUtils.toByteArray(new FileReader(file));
I see:
64, -38, -116, 84, 97, -116, 78, 47, -75, -27, -67, -8, 11, -100, -2, 109, 0, 0, 0, 0, 0, 0, 23, 104 (24 bytes)
-127 is replaced with 64 somehow.
I concat the byte with "" to print it btw.
Do not use FileReader to read raw bytes from file. Use FileInputStream instead.
The problem with FileReader is that it reads chars, not bytes, from the file, by trying to decode the bytes using some character encoding (the default one if none was given).
bytes = IOUtils.toByteArray(new FileInputStream(file));
Alternatively you can use DataOutputStream to write long directly to an output stream and use DataInputStream to read from an input stream.
try (DataOutputStream out = new DataOutputStream(new FileOutputStream(file))) {
out.writeLong(uuid.getMostSignificantBits());
out.writeLong(uuid.getLeastSignificantBits());
out.writeLong(player.getTokens());
}
try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
long uuidMSB = in.readLong();
long uuidLSB = in.readLong();
long tokens = in.readLong();
}
I did a POC using apache codec base64 library, where I encrypted a string using SHA. (This can be ignored).
Step 1 - I printed byte array for that string.
Step 2 - Encoded the byte array and printed its value.
Step 3 - Decoded the encoded value and printed it.
public static void main(String[] args)
{
MessageDigest messageDigest = null;
String ALGORITHM = "SHA";
try
{
messageDigest = MessageDigest.getInstance(ALGORITHM);
byte[] arr = "admin1!".getBytes();
byte[] arr2 = messageDigest.digest(arr);
System.out.println(arr2);
String encoded = Base64.encodeBase64String(arr2);
System.out.println(encoded);
byte[] decoded = Base64.decodeBase64(encoded);
System.out.println(decoded);
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
}
Expected result : Step 1 and Step 3 should produce same output. But I am not getting that.
Output :
[B#5ca801b0
90HMfRqqpfwRJge0anZat98BTdI=
[B#68d448a1
Your program is all good and fine. Just one mistake.
System.out.println(byteArray); prints hashCode of byte array object. (Note: Arrays are object in Java not primitive type)
You should use System.out.println(Arrays.toString(byteArray)); instead and you will get same value for both steps 1 and 3.
As per javadocs Arrays.toString(byte[] a) returns a string representation of the contents of the specified array.
Your code after changes will be :
public static void main(String[] args)
{
MessageDigest messageDigest = null;
String ALGORITHM = "SHA";
try
{
messageDigest = MessageDigest.getInstance(ALGORITHM);
byte[] arr = "admin1!".getBytes();
byte[] arr2 = messageDigest.digest(arr);
System.out.println(Arrays.toString(arr2));
String encoded = Base64.encodeBase64String(arr2);
System.out.println(encoded);
byte[] decoded = Base64.decodeBase64(encoded);
System.out.println(Arrays.toString(decoded));
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
}
and output will be :
[-9, 65, -52, 125, 26, -86, -91, -4, 17, 38, 7, -76, 106, 118, 90, -73, -33, 1, 77, -46]
90HMfRqqpfwRJge0anZat98BTdI=
[-9, 65, -52, 125, 26, -86, -91, -4, 17, 38, 7, -76, 106, 118, 90, -73, -33, 1, 77, -46]
Note value of byte array is same.
I've created the following procedure in the run() method of the ConnectedThread taken from the BluetoothChat sample.
// Read from the InputStream
byte[] buffer = new byte[16];
int offset = 0;
while(buffer.length-offset != 0)
{
int bytesRead += mmInStream.read(buffer, offset, buffer.length-offset);
offset += bytesRead;
}
// Do stuff with the contents of the buffer
The buffer is loaded in with 16 bytes gradually as expected but for some reason at the 10th byte in the array a 0 is inserted and shifts the remaining part of the package(and as such corrupting the entire package)
Here is an example of what is happening
The following is sent from the other client :
[-11, 126, -16, -30, -92, 110, -26, 13, 22, 91, -31, 32, 54, -125, -112, 45]
This is what I receive :
[-11, 126, -16, -30, -92, 110, -26, 13, 22, 91, 0, -31, 32, 54, -125, -112]
As you can see, an extra 0 is pushed in as the 10th byte and the rest of the package is shifted to the right(cutting off the last byte)
As part of the debugging process we tried having a breakpoint at bytesRead += mmInStream.read(buffer, offset, buffer.length-offset) and to our surprise the entire original message was received. What gives?
How does the "break" in reads correct this issue? What am I doing wrong or not understanding?
Probably you meant
bytesRead = mmInStream.read(buffer, offset, buffer.length-offset);
offset += bytesRead;
instead of
bytesRead += ...
Throw it all away and use DataInputStream.readFully().