Ok,
just in case someone will stumble over this in the future:
I solved the issue.
It turns out the problem is the following:
As mentioned before the
Key k = kf.generateSecret(ks);
creates a different Key k when executed on the DevPhone and the SUN
JVM. The reason for that is that on the phone the implementation
behind kf is a Bouncy Castle implementation and on the JVM it is SUN's
default implementation of the SecretKeyFactory.
Now the difference is that the factory implementation from SUN takes
the input of generateSecret(...) - in our example ks - and calculates
a parity bit for each byte of ks like this:
let ks be a key with an internal array of bytes: byte[] values.
for each byte in values
parityBit = calculate the odd parity over the 7 most significant
bits
set LSB = parityBit
In words, for each byte of ks the algorithm checks the number of 1
bits in the 7 most significant bits. If that number is odd, the LSB of
the current byte is set to 0 (odd parity!), if the number is even, the
LSB is set to 1. Doing so for each byte in ks yields the resulting key
k.
The Bouncy Castle implementation on the phone however returns ks
unmodified, i.e. k == ks.
Knowing that, we can find a work around which I will describe now:
On the phone we need to calculate the parity for the values of the
'generated' key k as it is done on the JVM.
For that we can use the following method:
private byte calculate7BitOddParity(byte inputValue)
{
// 1 = 00000001
// 2 = 00000010
// 4 = 00000100
// 8 = 00001000
// 16 = 00010000
// 32 = 00100000
// 64 = 01000000
// -128 = 10000000, (127 = 11111111)
byte bitMasks[] = {2, 4, 8, 16, 32, 64, -128};
byte numberOfOnes = 0;
for(int i = 0; i < 7; i++)
{
if((inputValue & bitMasks[i]) > 0)
numberOfOnes++;
}
//if number of ones is even, the LSB is not set.
//if the number of ones is even, odd parity returns a 1 as
parity bit.
//This will take the place of the LSB.
if((numberOfOnes & 1) == 0)
{
inputValue |= ((byte) 1);
}
else
{
//if the number of ones is odd, odd parity returns a 0 as
parity bit.
//This will take the place of the LSB.
byte minus2 = -2;
inputValue &= minus2; //set lsb to 0 since -2 = 11111110
}
return inputValue;
}
This method does exactly what is described in words above. It may not
be the most performant, but it is fast and it works.
Next problem we face then is that we can get the byte[] values from
the generated Key k by calling k.getEncoded(), but we cannot
manipulate the values.
A Key instance is usually generated by a factory, like in our example,
but there is no constructor.
In fact, and here we are lucky, Key is an interface.
So, to manipulate the byte values of Key k, we do the following: we
write a wrapper like this one:
public class ModifiableKey implements Key
{
private Key unmodifiableKey;
private byte[] encoded;
public ModifiableKey(Key unmodifiableKey)
{
this.encoded = unmodifiableKey.getEncoded();
this.unmodifiableKey = unmodifiableKey;
}
/**
* @see java.security.Key#getAlgorithm()
*/
@Override
public String getAlgorithm()
{
return unmodifiableKey.getAlgorithm();
}
/**
* @see java.security.Key#getEncoded()
*/
@Override
public byte[] getEncoded()
{
return encoded;
}
/**
* @see java.security.Key#getFormat()
*/
@Override
public String getFormat()
{
return unmodifiableKey.getFormat();
}
}
This wrapper can take a Key as argument in the constructor and exposes
the input key's data to the outside world like a delegate. The only
exception is the getEncoded() method, which returns a modifiable
reference to the copy of the input key's byte[] values.
Since the wrapper is itself implementing the Key interface, we can
pass it to all methods that expect a key.
Thus we can define the following method:
private void adjustParity(ModifiableKey k)
{
byte[] encodedValues = k.getEncoded();
for(int i = 0; i < encodedValues.length; i++)
{
encodedValues[i] = calculate7BitOddParity(encodedValues[i]);
}
}
And finally we can write our original authentication / key generation
code like this:
1 Mac hmac = Mac.getInstance("HmacSHA256");
2 SecretKeyFactory kf = SecretKeyFactory.getInstance("DES");
3 Key k = kf.generateSecret(ks);
4 ModifiableKey modKey = new ModifiableKey(k);
5 adjustParity(modKey);
6 hmac.init(modKey);
7 finalKey = hmac.doFinal(s);
Notice that in line 4, we wrap the Key k into a our ModifiableKey
wrapper class, then adjust the parity information in line 5 and
finally pass the modified key to the hmac.init(...) method.
This approach works, and I tested it.
I will now go and send a bug report.
Happy authenticating... :)
On 30 Jun., 11:52, nleiptv <[email protected]> wrote:
> One more thing I just noticed:
>
> If you compare k and ks in the following line,
>
> Key k = kf.generateSecret(ks);
>
> you notice that the Bouncy Castle SecretKeyFactory obviously does not
> do anything with the input key ks.
> In fact, on the phone, k == ks. On the SUN VM, ks is mangled. Probably
> according tohttp://forums.sun.com/thread.jspa?threadID=493288
>
> Can anybody confirm that? Would you say that is a bug of the Bouncy
> Castle implementation?
>
> On 30 Jun., 11:40, nleiptv <[email protected]> wrote:
>
> > Hi,
>
> > I am writing an application that uses Digest MD5 to authenticate a
> > client (android phone) to a server (running on SUN Java 1.6.0_02). The
> > authentication, which works fine, if I run the client from a normal
> > computer (not the Dev phone), does not succeed when the client is
> > running on the phone.
>
> > I started digging deep into the client and server authentication code
> > and I figured out that the following code yields different results,
> > when executed on the Dev Phone and on the SUN JVM:
>
> > 1 Mac hmac = Mac.getInstance("HmacSHA256");
> > 2 SecretKeyFactory kf = SecretKeyFactory.getInstance("DES");
> > 3 Key k = kf.generateSecret(ks);
> > 4 hmac.init(k);
> > 5 finalKey = hmac.doFinal(s);
>
> > The fact is that the key 'k' contains different byte values (Debugger-->
> > key=byte[8]) on the phone and on the SUN JVM.
>
> > The reason is, that on the phone, the SecretKeyFactory kf which
> > generate k in line 3 returns a different results.
> > On the phone the implementation of the factory kf, which is returned
> > in line 2 is provided by the Bouncy Castle Provider. On the SUN JVM it
> > is the SUN implementation.
> > Obviously, both implementations - though using the same algorithm
> > (DES) - calculate different results.
>
> > Did anyone experience this before? Is that a known issue that might be
> > solved by a newer Java version?
> > Maybe it is a bug.
>
> > Oh one more thing: when you compare the values of the byte-array of
> > the Key k, which was generated by the SUN provider and the Bouncy
> > Castle one, you can see that the values are almost identical. They
> > only deviate by +1, 0, and -1 in an unregular pattern.
>
> > E.g. if
> > k_phone = 54, 51, 54, 57, 54, 53, 53, 52
> > then
> > k_sun = 55, 50, 55, 56, 55, 52, 52, 52
>
> > so that is: +1, -1, +1, -1, +1, -1, -1, 0
>
> > I wonder whether this has anything to do with parity bits or
> > something. I am not a crypto guy.
>
> > Any help is greatly appreciated.
>
> > Thanks.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en
-~----------~----~----~----~------~----~------~--~---