Данные, подписанные на iOS, не могут быть проверены в Java

У меня есть некоторые данные, которые я подписываю на iOS с помощью SecKeyRawSign, используя закрытый ключ Elliptic Curve. Однако проверка данных в Java с использованием Signature.verify() возвращает false

Данные представляют собой случайное 64-битное целое число, разбитое на байты следующим образом.

uint64_t nonce = (some 64 bit integer)
NSData *nonceData = [NSData dataWithBytes: &nonce length: sizeof(nonce)];

Из этих данных я создаю дайджест SHA256.

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];

а затем подписать его закрытым ключом

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];
}

Все работает нормально.

Затем на Java-сервере я пытаюсь проверить подписанные данные.

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 );

В этот момент signer.verify() возвращает false

Я также пытался подписать простые данные вместо дайджеста SHA256, но это тоже не работает.

Что мне не хватает? Правильно ли я подписываю данные? Я использую правильный отступ? Нужно ли что-то еще сделать с данными, чтобы иметь возможность проверить их с помощью алгоритма SHA256withECDSA?


person mag_zbc    schedule 21.05.2018    source источник
comment
Можете ли вы зашифровать один и тот же Nonce в Java с помощью одного и того же алгоритма и посмотреть, будут ли разные зашифрованные версии? Таким образом, вы шифруете на обоих концах и смотрите, совпадают ли зашифрованные данные или нет.   -  person Tarun Lalwani    schedule 23.05.2018
comment
Я бы рекомендовал проверить правильность стороны ObjC: (1) является ли размер сгенерированной подписи ожидаемым размером? и (2) можете ли вы проверить подпись в коде ObjC? И дикая догадка: может быть, это проблема с порядком байтов? То есть [NSData dataWithBytes: &nonce... обращается к фактической необработанной памяти, в то время как сторона Java может видеть одноразовый номер в более общем сетевом порядке байтов. Поэтому попробуйте, работает ли одноразовый номер 0.   -  person mschmidt    schedule 24.05.2018
comment
Одна из ошибок — порядок байтов. iOS имеет обратный порядок байтов. При создании nonceData этот порядок сохраняется. Однако на стороне Java ByteBuffer по умолчанию имеет обратный порядок байтов. Итак, вам нужно добавить: buffer.order(ByteOrder.LITTLE_ENDIAN);   -  person Codo    schedule 29.05.2018
comment
@Codo, это было все. Пожалуйста, напишите это как ответ, чтобы я мог принять его.   -  person mag_zbc    schedule 01.06.2018
comment
Хорошо, я добавил это как ответ.   -  person Codo    schedule 01.06.2018


Ответы (4)


Порядок байтов не соответствует:

  • iOS использует little endian. При создании nonceData этот порядок сохраняется.
  • На стороне Java ByteBuffer по умолчанию имеет значение big endian, независимо от базовой операционной системы/оборудования.

Итак, вам нужно изменить порядок байтов:

ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putLong(nonce);
person Codo    schedule 01.06.2018

Я java-парень, поэтому я ничего не могу сказать о стороне iOS, но можно быстро проверить сторону java, используя прокомментированные предположения:

// Generate a new random EC keypair for testing
KeyPair keys = KeyPairGenerator.getInstance("EC").generateKeyPair();
PrivateKey privateKey = keys.getPrivate();
PublicKey publicKey = keys.getPublic();

// Generate a Random nonce to test with
byte[] nonceBytes = new byte[8]; // (some 64 bit integer)
new Random(System.nanoTime()).nextBytes(nonceBytes);

// sign     
Signature sign = Signature.getInstance("SHA256withECDSA");
sign.initSign(privateKey);
sign.update(nonceBytes);
byte[] signature = sign.sign();

//verify
Signature verify = Signature.getInstance("SHA256withECDSA");
verify.initVerify(publicKey);
verify.update(nonceBytes);
Boolean isVerified = verify.verify(signature);

// print results
System.out.println("nonce used  ::" + Base64.getEncoder().encodeToString(nonceBytes));
System.out.println("Signed nonce ::" + Base64.getEncoder().encodeToString(signature));
System.out.println("nonce used ::" + isVerified);

Как и следовало ожидать, приведенный выше код всегда будет возвращать сообщение о том, что подпись проверена. Убедитесь, что ваши предположения точны, и убедитесь, что используемые ключи верны на обеих сторонах.

person Jason Warner    schedule 25.05.2018

Я могу посоветовать вам использовать некоторую криптографическую библиотеку, которая доступна как для iOS, так и для JAVA (например: https://github.com/VirgilSecurity/virgil-crypto). Это гарантирует, что алгоритм и типы блоков (и т. д.) будут одинаковыми в обоих случаях, и вам больше не нужно об этом беспокоиться. Я думаю, вы найдете множество криптобиблиотек на GitHub.

person T. Pasichnyk    schedule 30.05.2018

В getBytes() вы можете указать метод кодирования, используя java.nio.charset.StandardCharsets. и сделать то же самое с декодером.

https://docs.oracle.com/javase/7/docs/api/java/nio/charset/StandardCharsets.html

person Saranya Vs    schedule 30.05.2018
comment
Я пробовал, если не указано, по умолчанию используется UTF-8, кодировка, которую я использую в iOS. - person mag_zbc; 01.06.2018
comment
Попробуйте этот тогда. docs.oracle.com/javase/7/ документы/апи/javax/безопасность/сертификат/ - person Saranya Vs; 01.06.2018
comment
Или, как предложил CoDo, вы можете использовать getBytes( StandardCharacters.UTF_16LE) - person Saranya Vs; 01.06.2018