Открытый ключ, созданный на устройстве iOS, недействителен на сервере Java - PullRequest
0 голосов
/ 16 мая 2018

У меня проблема с SecKeyRef, сгенерированным на устройстве iOS - при попытке использовать его на Java-сервере выдается исключение:

InvalidKeyException: параметры домена EC должны быть закодированы вИдентификатор алгоритма

Вот фрагмент кода из кода сервера:

String key = ...
byte[] byteKey =  Base64.decode(key.getBytes(StandardCharsets.UTF_8));
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey);
KeyFactory kf = KeyFactory.getInstance("EC");
return kf.generatePublic(X509publicKey);

Исключение выдается kf.generatePublic(X509publicKey);

Ключ создан на iOS,используя SecKeyGeneratePair

[keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeEC forKey:(__bridge id)kSecAttrKeyType];
[keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:256] forKey:(__bridge id)kSecAttrKeySizeInBits];

// Set the private key dictionary
[privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
[privateKeyAttr setObject:self.privateTag forKey:(__bridge id)kSecAttrApplicationTag];

// Set the public key dictionary
[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
[publicKeyAttr setObject:self.publicTag forKey:(__bridge id)kSecAttrApplicationTag];

// Set attributes to top level dictionary
[keyPairAttr setObject:privateKeyAttr forKey:(__bridge id)kSecPrivateKeyAttrs];
[keyPairAttr setObject:publicKeyAttr forKey:(__bridge id)kSecPublicKeyAttrs];

// Generate key pair
OSStatus sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef);

Пара ключей успешно создана.Затем я извлекаю битовые данные ключа, используя следующий код

CFDataRef publicKeyBitsRef = NULL;
NSMutableDictionary *queryPublicKey = [NSMutableDictionary dictionary];

// Set the public key query dictionary.
[queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPublicKey setObject:self.publicTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeEC forKey:(__bridge id)kSecAttrKeyType];

[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnData];

// Get the key bits.
OSStatus sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyBitsRef);

Затем я экспортирую ключ, используя CryptoExportImportManager

NSData *publicKeyIDERData = [manager exportPublicKeyToDER:keyBits keyType:(__bridge NSString*)kSecAttrKeyTypeEC keySize:256];
NSString *derKeyString = [publicKeyIDERData base64EncodedStringWithOptions:0];

Согласно этот ответ заголовок DER содержит информацию о типе ключа и параметрах, а для ключа secp256r1 он эквивалентен следующим данным

[
    0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 
    0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
    0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 
    0x42, 0x00
]

, которые действительно добавляются в заголовок ключа при экспорте.

derKeyString затем отправляется на сервер и обрабатывается с использованием кода Java, упомянутого выше.Однако исключение выбрасывается.

Этот же сервер обрабатывает также ключ, созданный на устройстве Android, используя следующий код

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
        keyPairGenerator.initialize(new KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_SIGN)
                .setDigests(KeyProperties.DIGEST_SHA256)
                .setAlgorithmParameterSpec(
                new ECGenParameterSpec("secp256r1"))
                .setUserAuthenticationRequired(true).build());
        keyPairGenerator.generateKeyPair();

Ключ Android работает отлично.

Что я делаю не так?Я что-то забыл при создании ключей с помощью SecKeyGeneratePair или при экспорте открытого ключа?

1 Ответ

0 голосов
/ 19 июля 2018

Я решил проблему, хотя я не уверен, почему.

Что я сделал, так это отключил библиотеку CryptoExportImportManager и вручную создаю ключевые данные, например:

unsigned char _encodedECOID[] = {
    0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
    0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
    0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
    0x42, 0x00
};

NSMutableData *data = [NSMutableData new];
[data appendBytes:_encodedECOID length:sizeof(_encodedECOID)];
[data appendData:keyBits]; // keyBits is od NSData type

Теперь сервер Java правильно создает открытый ключ из моей строки (base64, закодированный из data).

Однако, посмотрев на исходный код CryptoExportImportManager, способ, которым он создает закодированную строку из моих битов ключа, выглядит так (в Swift):

let curveOIDHeader: [UInt8] = [0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 
                               0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A,
                               0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 
                               0x42, 0x00]
let curveOIDHeaderLen: Int = 26

var data = Data(bytes: curveOIDHeader, count: curveOIDHeaderLen)
data.append(rawPublicKeyBytes)

Это в основном то же самое. Так в чем же разница?

Теперь единственное, что приходит на ум, - это разница в том, как хранится заголовок - в моем случае это массив unsigned char, в случае библиотеки это массив UInt8.

Согласно этот ответ , C типы unsigned char и uint8_t не эквивалентны, они гарантированно имеют только одинаковую длину, но могут различаться, т. Е. В порядке байтов.

Хотя этот вопрос не имел ничего общего с UInt8 Swift (но был помечен C, из которых Objective-C является надмножеством), документация типа UInt8 Swift ничего не говорит о его отношении к unsigned char type, это единственное разумное объяснение, которое я вижу.

...