ricmoo / GMEllipticCurveCrypto

Elliptic Curve Cryptography library for iOS (ECDSA and ECDH)
BSD 2-Clause "Simplified" License
122 stars 46 forks source link

Encoded Key Size Differences in Java #1

Closed gunhansancar closed 9 years ago

gunhansancar commented 9 years ago

Hi,

I want to use the generated public and private key in Bouncy Castle java. When i get crypto.publicKeyBase64 the returned data is too small to generate publickey in bouncy castle.

When I generate EC keypair in bc and then use it as publicKey.getEncoded() it returns 75bytes. I use also secp192r1 curve in bc.

In your library it gives me 24bytes which is 192bits. However it is not encoded the way bouncy castle encodes it.

My question is how can i syncronize those two systems?

ricmoo commented 9 years ago

My guess is that Bouncy Castle is using DER encoding, but I'm not sure. Can you send me a few examples of the output from Bouncy Castle? It would make sense for me to add DER encoded (and accept DER encoded) keys to my library anyways.

gunhansancar commented 9 years ago

Hi again, I generated sample public key which you can find details below:

EC Public Key X: a12587f324f331915a49f1e1d0972b21deb9d860ba97ee5c Y: 313039ac78a3a1507290b558bcbe500f99c3207b10bf1e48

publicKey encoded as byte array [48,73,48,19,6,7,42,-122,72,-50,61,2,1,6,8,42,-122,72,-50,61,3,1,1,3,50,0,4,-95,37,-121,-13,36,-13,49,-111,90,73,-15,-31,-48,-105,43,33,-34,-71,-40,96,-70,-105,-18,92,49,48,57,-84,120,-93,-95,80,114,-112,-75,88,-68,-66,80,15,-103,-61,32,123,16,-65,30,72]

publicKey encoded as hex [0x30 0x49 0x30 0x13 0x06 0x07 0x2a 0x86 0x48 0xce 0x3d 0x02 0x01 0x06 0x08 0x2a 0x86 0x48 0xce 0x3d 0x03 0x01 0x01 0x03 0x32 0x00 0x04 0xa1 0x25 0x87 0xf3 0x24 0xf3 0x31 0x91 0x5a 0x49 0xf1 0xe1 0xd0 0x97 0x2b 0x21 0xde 0xb9 0xd8 0x60 0xba 0x97 0xee 0x5c 0x31 0x30 0x39 0xac 0x78 0xa3 0xa1 0x50 0x72 0x90 0xb5 0x58 0xbc 0xbe 0x50 0x0f 0x99 0xc3 0x20 0x7b 0x10 0xbf 0x1e 0x48 ]

publicKey encoded as base 64 MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEoSWH8yTzMZFaSfHh0JcrId652GC6l+5cMTA5rHijoVBykLVYvL5QD5nDIHsQvx5I

ricmoo commented 9 years ago

I've looked more into Bouncy Castle, and I was wondering if you could post the code you use to generate the base64 encoded string? It seems it has multiple ways to export a public key. GMEllipticCurveCrypto uses base64 encoded compressed points, and the data you provided above certainly looks like DER encoded data, but when I decode it, it contains a bunch of identifiers as well as a 392 bit bit string, which would perfectly encode two 24-byte numbers with an extra byte for... Something?

http://lapo.it/asn1js/#3049301306072A8648CE3D020106082A8648CE3D03010103320004A12587F324F331915A49F1E1D0972B21DEB9D860BA97EE5C313039AC78A3A1507290B558BCBE500F99C3207B10BF1E48

I feel I'm close to understanding it's encoding type, and then I will either add a way to load the public keys or at least provide sample code to decode it in the read me. :)

ricmoo commented 9 years ago

btw. The bit string is indeed the public key, with the standard 0x04 in front indicating an uncompressed point and an unusual extra 0x00 in front of that...

GMEllipticCurveCrypto doesn't currently support setting the public key with an uncompressed point; I will add that shortly. It is still a little bit of logic to get Bouncy Castle (if Bouncy Castle also supports compressed points?) into a standard format though; was the above output a result from the getEncoded()?

gunhansancar commented 9 years ago

I am using the following code to generate keypair

SecureRandom random = SecureRandom.getInstance("SHA1PRNG");

KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); keyGen.initialize(192, random);

KeyPair pair = keyGen.generateKeyPair(); PublicKey pub = pair.getPublic(); byte[] array = pub.getEncoded(); String pub64 = Base64.encode(array); String hex = byteArrayToHex(array);

Btw; I found a way to import the key into bouncy castle, however, it is best for me to use same encoding decoding in both java and ios

//below public key generated by GMEllipticCurveCrypto //I can successfully convert it to a public key object in java byte[] ec64 = Base64.decode("AmVZlT8IQ/ED6ebK6IxkkLfVdrJQA11u8Q==");

KeyFactory kf = KeyFactory.getInstance("ECDH", "BC"); ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("prime192v1"); ECPoint point = ecSpec.getCurve().decodePoint(ec64); ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, ecSpec);

ECPublicKey ecPubKey = (ECPublicKey) kf.generatePublic(pubSpec);

At this moment, I am trying to sign in ios and verify in java.

ricmoo commented 9 years ago

I have just added full support for using uncompressed public key points, which should help, but will still require a little work for you (make sure you download the latest version). The following is more pseudo-code than anything, as my Java is rusty...

To go from BC to GMECC:

public static byte[] encodeECPublicKey(ECPublicKey pubKey) {
    int keyLengthBytes = pubKey.getParams().getOrder().bitLength() / 8;
    byte[] publicKeyEncoded = new byte[1 + 2 * keyLengthBytes];

    publicKeyEncdoed[0] = 0x04;

    // You probably know a better way to copy bytes...
    BigInteger x = pubKey.getW().getAffineX();
    byte[] xba = x.toByteArray();
    for (int i = 0; i < keyLengthBytes; i++) {
        publicKeyEncoded[1 + i] = xba[i];
    }

    BigInteger y = pubKey.getW().getAffineY();
    byte[] yba = y.toByteArray();
    for (int i = 0; i < keyLengthBytes; i++) {
        publicKeyEncoded[1 + keyLengthBytes + i] = yba[i];
    }

    return publicKeyEncoded;
}

To go from GMECC to BC:

Please let me know if these work.

ricmoo commented 9 years ago

One more note, for now if you just want to simply modify the iOS code to see if things work, then you should be able to do this:

int bits = 192;
int publicKeyLength = 1 + 2 * bits / 8;

NSData *decoded = [[NSData alloc] initWithBase64EncodedString:base64WeirdPayloadFromBouncyCastle options:0];

NSData *publicKey = [decoded subdataWithRange:NSMakeRange([decoded length] - publicKeyLength, publicKeyLength)];

GMEllipticCurveCrypto *crypto = [GMEllipticCurveCrypto cryptoForKey:publicKey];
// ... And now you can do stuff

(again, pseudo-code-ish...)

gunhansancar commented 9 years ago

Thank you for your effort.

I am trying to accomplish signing in ios and verifying in java so i need to opposite of them :) When I try to verify the signature in java it throws exception: java.security.SignatureException: error decoding signature bytes.

So I try to find the differences GMECC and BC. To find it I also sign it with BC.

When I sign with GMECC it produces this: 0xb8 0x0d 0x4f 0xce 0x1c 0x1b 0x6c 0x29 0xb6 0x7e 0xf7 0xa4 0xa6 0x18 0x76 0x3d 0xc4 0xbc 0x6d 0xf0 0xdb 0x65 0xa8 0x74 0xdd 0x90 0xc6 0x25 0xf8 0xf2 0x93 0xf2 0xd8 0x03 0x49 0x62 0x51 0xd8 0x69 0xd3 0x12 0x57 0xe9 0x18 0xb6 0x6f 0x38 0x15

when I sign with BC it produces this: 0x30 0x35 0x02 0x19 0x00 0x98 0x3e 0x75 0xa1 0xc1 0x69 0x0e 0x06 0xab 0x48 0x6e 0x0c 0xbd 0x03 0x59 0x19 0xb1 0xf7 0x1f 0xd7 0xde 0x33 0x55 0x90 0x02 0x18 0x5d 0x3d 0x09 0xea 0x46 0x18 0x62 0x46 0x4b 0x4a 0x89 0x52 0x5a 0xca 0xc8 0xa6 0x28 0xee 0x15 0x0b 0xe4 0x84 0xe6 0x75

and another sample of BC:

0x30 0x36 0x02 0x19 0x00 0xbb 0xaa 0x18 0x45 0xe5 0x1c 0xd8 0x60 0xeb 0xf3 0x94 0x9b 0xb7 0x33 0x98 0xac 0x8e 0xfe 0xc2 0x89 0xfa 0x0c 0xc4 0x42 0x02 0x19 0x00 0xe5 0x2e 0x37 0x2d 0xa7 0xb0 0x54 0x74 0x8a 0x6b 0x43 0x41 0x18 0xb2 0xe9 0xee 0x66 0x10 0xc8 0x75 0x6e 0xd1 0x49 0x03

I try to find the differences and the first bytes are looks like same in BC signature. I think its a format of ASN or something. 0x30 0x35 0x02 0x19 0x00 0x30 0x36 0x02 0x19 0x00

If i add those headers bytes to the signature which is produced by GMECC I hope I can verify the signature in java without any exception.

gunhansancar commented 9 years ago

Btw I use the below code to verify signature in java: Signature sig = Signature.getInstance("SHA256withECDSA"); sig.initVerify(pubKey); sig.update(unsignedData);

return sig.verify(signedData);

ricmoo commented 9 years ago

Ah yes... The signature is DER (ASN.1) encoded (this link is the second example of BC): http://lapo.it/asn1js/#3036021900BBAA1845E51CD860EBF3949BB73398AC8EFEC289FA0CC442021900E52E372DA7B054748A6B434118B2E9EE6610C8756ED14903

That is a useful feature. I will add it as an encoding option for signatures and verifying signatures to GMECC.

ricmoo commented 9 years ago

The DER encoding is far more complicated than that last snippet. I have an experimental branch you can try out. Clone this branch:

https://github.com/ricmoo/GMEllipticCurveCrypto/tree/der-encoder

And if you #import "GMEllipticCurveCrypto+hash.h" you should be able to use hashSHA256AndSignDataEncoded: to compute the DER encoded signature.

Once I have written some test cases, I will merge this to master.

gunhansancar commented 9 years ago

Thank you I am trying right now, the BC couldn't recognized your previous quick suggestion. While I was reading more about ECDSA , the length of the signature for BC is different sometimes. I hope you consider these as well.

gunhansancar commented 9 years ago

Oh finally, hashSHA256AndSignDataEncoded method worked like a charm :) I couldn't done this with apple's standard SecKeyRawSign. I used it for RSA encription and signing but for EC it just produces useless signature. And there was no example for that neither.

So I just want to ask you some questions? Is your library deterministic ECDSA or undeterministic ? Is the der encoder branch is stable will you merge it?

ricmoo commented 9 years ago

Awesome!

Yeah, DER encoding becomes slightly more complicated when dealing with integers' signs. Sorry, I removed that previous snippet as it was flawed. :)

So, the library is currently non-deterministic as the underlying library is non-deterministic. However, it would be easy-isn to add determinism, so I will add an issue to track it and will implement it in the future. If it is something you would prefer sooner than later, feel free to bump it. The iPhone has a good source of entropy (as far as I know), so I will probably leave it as lower priority.

Yes, once I finish writing test cases I will merge it to master. I should probably have test cases done by today or tomorrow.

ricmoo commented 9 years ago

I have completely re-implemented the DER decoding and added encoding, have added test cases and have merged the branch to master.

Please use the master branch to get the latest version now.

Thanks! :)

mallepulak commented 8 years ago

Hi, I need to generate 64 bytes publickey using ECDH P-256 algorithm. Can anybody help me to create?