I have some data that I'm signing on iOS with SecKeyRawSign
using Elliptic Curve private key. However, verifying that data in Java using Signature.verify()
returns false
The data is a random 64 bit integer, split into bytes like so
uint64_t nonce = (some 64 bit integer)
NSData *nonceData = [NSData dataWithBytes: &nonce length: sizeof(nonce)];
From that data I'm creating a SHA256 digest
int digestLength = CC_SHA256_DIGEST_LENGTH;
uint8_t *digest = malloc(digestLength);
CC_SHA256(nonceData.bytes, (CC_LONG)nonceData.length, digest);
NSData *digestData = [NSData dataWithBytes:digest length:digestLength];
and then signing it with private key
size_t signedBufferSize = kMaxCipherBufferSize;
uint8_t *signedBuffer = malloc(kMaxCipherBufferSize);
OSStatus status = SecKeyRawSign(privateKeyRef,
kSecPaddingPKCS1SHA256,
(const uint8_t *)digestData.bytes,
digestData.length,
&signedBuffer[0],
&signedBufferSize);
NSData *signedData = nil;
if (status == errSecSuccess) {
signedData = [NSData dataWithBytes:signedBuffer length:signedBufferSize];
}
Everything appears to work fine.
Then, in Java server, I'm trying to verify that signed data
PublicKey publicKey = (a public key sent from iOS, X509 encoded)
Long nonce = (64 bit integer sent from iOS)
String signedNonce = (base64 encoded signed data)
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
buffer.putLong(nonce);
byte[] nonceBytes = buffer.array();
byte[] signedNonceBytes = Base64.getDecoder().decode(signedNonce.getBytes());
Signature signer = Signature.getInstance( "SHA256withECDSA" );
signer.initVerify( publicKey );
signer.update( nonceBytes );
Boolean isVerified = signer.verify( signedNonceBytes );
At this point, signer.verify()
returns false
I also tried to sign plain data, instead of SHA256 digest, but that doesn't work either.
What am I missing? Am I signing the data correctly? Am I using correct padding? Is there something else to be done with data to be able to verify it with SHA256withECDSA
algorithm?
In getBytes() you could specify the encoding technique using java.nio.charset.StandardCharsets. and do the same with the decoder.
https://docs.oracle.com/javase/7/docs/api/java/nio/charset/StandardCharsets.html
I can advice you to use some Crypto Library which is available for both iOS and JAVA sides (f.e.:https://github.com/VirgilSecurity/virgil-crypto). This will ensure that the algorithm and block types (etc.) are the same in both cases and you won't need to worry about it anymore. I believe you will finds many crypto libraries on a GitHub.
The byte ordering does not match:
nonceData
, this order is retained.ByteBuffer
defaults to big endian, independent of the underlying operating system / hardware.So you need to change the byte order:
I'm a java guy, so I can't say anything about the iOS side, but a quick check of the java side can be done using the commented assumptions:
As you'd expect returns, the code above will always return that the signature is verified. Check your assumptions are accurate and validate the keys being used are correct on both sides.