在iOS设备上生成的公钥在Java服务器上无效

在iOS设备上生成的公钥在Java服务器上无效,java,ios,objective-c,java-security,commoncrypto,Java,Ios,Objective C,Java Security,Commoncrypto,我在iOS设备上生成的SecKeyRef有问题-当尝试在Java服务器上使用它时,会引发异常: InvalidKeyException:必须在算法标识符中对EC域参数进行编码 以下是服务器代码中的代码片段: String key = ... byte[] byteKey = Base64.decode(key.getBytes(StandardCharsets.UTF_8)); X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(

我在iOS设备上生成的
SecKeyRef
有问题-当尝试在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);
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();
然后,我使用

根据,DER标头包含有关密钥类型和参数的信息,对于
secp256r1
key,它相当于以下数据

[
    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设备上创建的密钥

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);
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();
安卓的按键工作正常


我做错了什么?在使用
SecKeyGeneratePair
创建密钥或导出公钥时,我是否忘记了一些事情?

我解决了这个问题,尽管我不知道为什么

我所做的是抛弃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从
数据编码)

但是,在查看CryptoExportImportManager的源代码后,它从我的密钥位创建编码字符串的方式如下(在Swift中):

它基本上做了完全相同的事情。那么区别在哪里呢

现在唯一想到的是头的存储方式不同——在我的例子中是一个
无符号字符的数组,在library的例子中是一个
UInt8
的数组

根据
C
类型
无符号字符和
uint8_t
是不等价的,它们只能保证具有相同的长度,但可以在字节顺序上有所不同


虽然这个问题与Swift的
UInt8
(但是被标记为
C
,其中Objective-C是超集),但Swift的文档没有说明它与
无符号字符类型的关系,这将是我能看到的唯一合理解释。

你解决了这个问题吗?“面对同样的问题。”乔贴出了答案