Converting a hex String to x509 encoding - java

I'm working with some Android Java code that uses ECDSA keys. The code compiles and runs fine, but has some logic errors during the verification process. I want to try using a constant key pair (that's known to be valid) to troubleshoot the program.
Using an online generator, I got an EC public key in hex,
0x044fb7cebbb1f4a1e0412c8e0b6f2d675ebfee000c5e860a81ffd795b5743033dec0e114abfba3de8db8705fc8ed985c5550c66a6ee9fdd258d058a2ef749eba78
As well as a valid private key to complete the pair,
0x0c84e7e707b31ecf0254e8cb3040513883d92d81e977ad4754a409a6ab18ee51
I can convert the hex string to a primitive byte array, but that byte array appears to be invalid. I cannot figure out how to convert a hex representation of my keys to a X509 representation so that I can make a Java key object.
KeyFactory mFactory = KeyFactory.getInstance("EC");
X509EncodedKeySpec mPublicKey = new X509EncodedKeySpec(publicKeyBytes);
PublicKey publicKey = mFactory.generatePublic(mPublicKey);
That code results in:
java.security.spec.InvalidKeySpecException: com.android.org.conscrypt.OpenSSLX509CertificateFactory$ParsingException: Error parsing public key
I am reasonably sure that my conversion from hex string to byte array is working, but I'll include that method as well for a sanity check.
private static byte[] hexStringToByteArray(String s) throws IllegalArgumentException {
int len = s.length();
if (len % 2 == 1) {
throw new IllegalArgumentException("Hex string must have even number of characters");
}
byte[] data = new byte[len / 2]; // Allocate 1 byte per 2 hex characters
for (int i = 0; i < len; i += 2) {
// Convert each character into a integer (base-16), then bit-shift into place
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
The end goal is to have a constant, valid PublicKey/PrivateKey object for testing. Any advice about how to generate those objects would be greatly appreciated.

Related

Encryption key size DES Java

I want to encrypt a challenge (like 162236fe0bec620827958c8fdf7e4bc7 ) using this key C6864E7696C686 with the DES algorithm.
Here is my code :
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.xml.bind.DatatypeConverter;
import javax.crypto.spec.DESKeySpec;
def data = prev.getResponseData();
String challenge = javax.xml.bind.DatatypeConverter.printHexBinary(data);
final String strPassPhrase = "C6864E7696C686";
String param = challenge;
System.out.println("Text : " + param);
SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
SecretKey key = factory.generateSecret(new DESKeySpec(hexStringToByteArray(strPassPhrase)));
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
String str = DatatypeConverter.printBase64Binary(cipher.doFinal(param.getBytes()));
System.out.println("Text Encryted : " + str);
cipher.init(Cipher.DECRYPT_MODE, key);
String str2 = new String(cipher.doFinal(DatatypeConverter.parseBase64Binary(str)));
System.out.println("Text Decryted : " + str2);
But i get this exception :
java.security.InvalidKeyException: Wrong key size
Edit :
I have copy this function to convert my hex string to bytes :
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
But I get the same exception ...
Your DES key should be 8 bytes (56 bits + 8 parity bits).
The string you're using as a key looks like a hexadecimal representation of 7 bytes, but instead of decoding it as hex, you're getting the bytes for the characters in the hex string.
Since there are 14 characters, you will most likely (depending on your encoding) end up with 14 bytes, which is too long for DES.
There are a couple of approaches described in this question that explain how to convert the hex string to a byte array.
That will however only get you so far, because you're still one byte short. A traditional approach seems to be to take the 56 bits you have and spread them out over 8 bytes, adding one parity bit to each byte. A Java example of how to do that is described in this answer. Another approach could be to just add a null byte at the end of the key. Which approach you should take depends on the key's intended usage, especially the way it is used by the other parties you're exchanging information with.

30 char md5 digest

I use the following code for generating md5 for blob data in database.
md5Checksum.update(byte[] --- read from database);
String result = new BigInteger(1,md5Checksum.digest()).toString(16);
The checksum i obtain is varying in length(30-32) for different byte arrays.
For 31 char length checksum, as I understood can be an effect of the removal of leading zeros. (I handled it by adding leading zeros)
Can any one tell me why I am getting a 30 char hash in some cases?
Thanks,
Mithun
Do not convert a digest to a number!
Use something like:
byte[] b = md5Checksum.digest();
// now convert these bytes to chars
There are many different methods to convert byte[] to HexStrings:
public class HexConverter {
// thanks to http://www.rgagnon.com/javadetails/java-0596.html
static private final String HEXES = "0123456789ABCDEF";
public String toHex(byte[] raw) {
if (raw == null) {
return null;
}
final StringBuilder hex = new StringBuilder(2 * raw.length);
for (final byte b : raw) {
hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
}
return hex.toString();
}
}
or from Getting a File's MD5 Checksum in Java
(this link also shows how to se DigestUtils from Apache Commons Codec)
public static String getMD5Checksum(String filename) throws Exception {
byte[] b = createChecksum(filename);
String result = "";
for (int i=0; i < b.length; i++) {
result += Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
}
return result;
}
There is a chance that the high n bits could be zero. As you convert the byte[] to a number.If n=4,then you could lose one '0' char at the beginning.
If n=8, then you lose '00' at the beginning of your md5 hex code.

Android little endian MD5

I'm porting a Windows app to Android and I'm running into an issue with endianness. The app takes a series of text fields from the user and generates a password based on MD5. The problem is when I create the byte array to pass into the MD5 digest method, the bytes on the Android app are in big endian format. Thus, the MD5 output does not match between the two platforms.
I've tried using a ByteBuffer to convert to little endian and then copy that value back into the byte array using ByteBuffer.get(). Sadly, that doesn't work as it doesn't maintain the order setting.. This seems to be a known "gotcha" when dealing with ByteBuffers. If I compare the ByteBuffer.getLong() value and the equivalent in the windows version the values match but I don't know how to get the array back out of the ByteBuffer in the correct order.
Edit: I've attached both the java and C# functions below.
Below is the java version that doesn't try to fix the order/endianness:
public static final long md5(final String input) {
try {
// Create MD5
MessageDigest md5 = MessageDigest.getInstance("MD5");
// Read in string as an array of bytes.
byte[] originalBytes = input.getBytes("US-ASCII");
byte[] encodedBytes = md5.digest(originalBytes);
long output = 0;
long multiplier = 1;
// Create 64 bit integer from the MD5 hash of the input
for (int i = 0; i < encodedBytes.length; i++) {
output = output + encodedBytes[i] * multiplier;
multiplier = multiplier * 255;
}
return output;
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return 0;
}
And here is the C# version
private Int64 MD5(string input)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
byte[] originalBytes = ASCIIEncoding.ASCII.GetBytes(input);
byte[] encodedBytes = md5.ComputeHash(originalBytes);
Int64 output = 0;
Int64 Multiplyer = 1;
for (int i = 0; i < encodedBytes.Length; i++)
{
output = output + encodedBytes[i] * Multiplyer;
Multiplyer = Multiplyer * 255;
}
return output;
}
The problem is that this line of Java:
output = output + encodedBytes[i] * multiplier;
is subtly different from this line of C# code:
output = output + encodedBytes[i] * Multiplyer;
Specifically, the implicit conversion of encodedBytes[i] from byte to long (Java) or Int64 (C#) is a bit different.
You see, in Java, a byte is a signed value between -128 and 127, whereas in C#, it's an unsigned value between 0 and 255. So, for example, if encodedBytes[i] is B2 (1011 0010), then Java interprets that as -78, while C# interprets that as 178.
To emulate the C# interpretation in Java, you can write something like this:
output = output + ((encodedBytes[i] + 256) % 256) * multiplier;
(Fortunately, Java has the same handling for integer overflow as C#'s "unchecked" mode, which is apparently what you're using; that would have been much trickier to emulate, if you had to.)
The MD5 standard calls for 128-bit values, not 64-bit. So first of all, the signature private Int64 MD5(string input) makes no sense. You should not be converting these to integers and trying to compare them. Just pass around the byte[] references and compare those.

How to map bytes from 128 to 255 to the equivalent UTF16-LE surrogate pairs

I'm trying to achieve this:
I have a PDF byte[] in java web service that I must send as a base64 string to a .NET client that does this to reconstruct the file.
Encoding.Convert(Encoding.Unicode, Encoding.Default, Convert.FromBase64String(inputJava))
I cannot change the client code and right now the java web service is calling another .NET web service that does this to turn the byte[] into a base64 string:
System.Text.Encoding.Convert(System.Text.Encoding.GetEncoding(1252), System.Text.Encoding.Unicode, b);
Beside the base64 that I can make in various ways (e.g. with org.apache.commons.codec.binary.Base64), I have to turn the original byte[] into a UTF-16LE byte[]...
I tried this:
byte[] output = new byte[b.length * 2];
for(int i=0; i < b.length; i++)
{
int val = b[i];
if(val < 0) val += 256;
output[2*i + 0] = (byte) (val);
output[2*i + 1] = 0;
}
This works fine for values below 128 (e.g. for 1 => 0100, 2 => 0200, ... , 127 => 7F00) but for values above (128 -> 255) I don't know how to get the equivalent 2bytes values; I know that for byte 156 (9C) the corresponding value is 8301 (0x5301) and for byte 224 (E0) the corresponding value is 12501 (0x7D01) but I didn't manage to find an algorithm to get all the other values.
Is there a mapping table between byte value and the corresponding UTF-16LE surrogate pair or an algorithm to map values from 128 to 255?
Thanks in advance!
You don’t need surrogate pairs; they are a construct for dealing with characters outside Basic Multilingual Plane (BMP), and all windows-1252 characters are in BMP.
The official windows-1252 (alias cp1252) to Unicode mapping table is
http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT
It’s a plain text file in easy-to-process format, so if you don’t find an existing tool for the conversion, it should be rather straightforward to write mapping based on that file.
The file is indirectly cited in the official IANA registry:
http://www.iana.org/assignments/character-sets
byte[] encoded = new String(b, "windows-1252").getBytes("UTF-16LE");
I finally found a solution. It looks like that only bytes from 128 to 159 need the surrogate pairs. I use this piece of code to emulate .NET Unicode encoding:
public class Encoder {
static Map<Integer, Integer> mapTiny = new HashMap<Integer, Integer>() {
public Integer get(Object key) {
Integer code = super.get(key);
if (code == null)
code = (Integer) key;
return code;
}
};
static {
mapTiny.put(128,8364);
mapTiny.put(130,8218);
mapTiny.put(131,402);
mapTiny.put(132,8222);
mapTiny.put(133,8230);
mapTiny.put(134,8224);
mapTiny.put(135,8225);
mapTiny.put(136,710);
mapTiny.put(137,8240);
mapTiny.put(138,352);
mapTiny.put(139,8249);
mapTiny.put(140,338);
mapTiny.put(142,381);
mapTiny.put(145,8216);
mapTiny.put(146,8217);
mapTiny.put(147,8220);
mapTiny.put(148,8221);
mapTiny.put(149,8226);
mapTiny.put(150,8211);
mapTiny.put(151,8212);
mapTiny.put(152,732);
mapTiny.put(153,8482);
mapTiny.put(154,353);
mapTiny.put(155,8250);
mapTiny.put(156,339);
mapTiny.put(158,382);
mapTiny.put(159,376);
}
public static String encode(byte[] b) throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream(b);
ByteArrayOutputStream convFileByteArray = new ByteArrayOutputStream();
int i = in.read();
while (i != -1) {
convFileByteArray.write(new byte[] { (byte) (mapTiny.get(i) & 0xff), (byte) ((mapTiny.get(i) >> 8) & 0xff) });
i = in.read();
}
return Base64.encodeToString(convFileByteArray.toByteArray(), false);
}
}

String to byte array and then to MD5 in Java

in the last 5 hours im trying to do something that should be very simple and did it in like 10 minutes in C#, but no luck with Java.
I got a 32 UpperCase and Numeric String (A-Z0-9), I need to convert this String to Dec, and then md5 it.
My problem is that I dont have unsgined bytes so I cant md5 my array :\
Here is what I need to do in python:
salt = words[1].decode("hex")
passwordHash = generatePasswordHash(salt, pw)
generatePasswordHash(salt, password):
m = md5.new()
m.update(salt)
m.update(password)
return m.digest()
and here it is in C# :
public static string GeneratePasswordHash(byte[] a_bSalt, string strData) {
MD5 md5Hasher = MD5.Create();
byte[] a_bCombined = new byte[a_bSalt.Length + strData.Length];
a_bSalt.CopyTo(a_bCombined, 0);
Encoding.Default.GetBytes(strData).CopyTo(a_bCombined, a_bSalt.Length);
byte[] a_bHash = md5Hasher.ComputeHash(a_bCombined);
StringBuilder sbStringifyHash = new StringBuilder();
for (int i = 0; i < a_bHash.Length; i++) {
sbStringifyHash.Append(a_bHash[i].ToString("X2"));
}
return sbStringifyHash.ToString();
}
protected byte[] HashToByteArray(string strHexString) {
byte[] a_bReturn = new byte[strHexString.Length / 2];
for (int i = 0; i < a_bReturn.Length; i++) {
a_bReturn[i] = Convert.ToByte(strHexString.Substring(i * 2, 2), 16);
}
return a_bReturn;
}
I will be very happy to get a help with this :)
To parse a hex string into a byte : (byte) Integer.parseInt(s, 16).
To transform your password string into a byte array, using the default encoding (which I suggest not to do : always specify a specific encoding) : password.getBytes() (or password.getBytes(encoding) for a specific encoding).
To hash a byte array : MessageDigest.getInstance("MD5").digest(byte[]).
To transform a byte to a hex String : See In Java, how do I convert a byte array to a string of hex digits while keeping leading zeros?
I believe that something like the following will work:
// convert your hex string to bytes
BigInteger bigInt = new BigInteger(salt, 16);
byte[] bytes = bigInt.toByteArray();
// get the MD5 digest library
MessageDigest md5Digest = null;
try {
md5Digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
// error handling here...
}
// by default big integer outputs a 0 sign byte if the first bit is set
if (bigInt.testBit(0)) {
md5Digest.update(bytes, 1, bytes.length - 1);
} else {
md5Digest.update(bytes);
}
// get the digest bytes
byte[] digestBytes = md5Digest.digest();
Here's more ideas for converting a hex string to a byte[] array:
Convert a string representation of a hex dump to a byte array using Java?
You can use unsigned numbers in java with applying bit masks. Take a look details here.

Categories