【问题标题】:CryptoKit authenticationFailure when try decrypt尝试解密时 CryptoKit 身份验证失败
【发布时间】:2021-06-19 14:14:42
【问题描述】:

我正在尝试使用 SymmetricKey 解密有效负载。我已经尝试使用 ChaChaPoly 和 AES.GCM 打开sealedBox,但我仍然收到CryptoKit.CryptoKitError.authenticationFailure 这是我的实现:

let iv: [UInt8] = [0x00, 0x01, 0x02, 0x03,
              0x04, 0x05, 0x06, 0x07,
              0x08, 0x09, 0x0A, 0x0B,
              0x0C, 0x0D, 0x0E, 0x0F]

func generatePair() {
        let priv = P256.KeyAgreement.PrivateKey()
        privateKey = priv
        publicKey = priv.publicKey
    }

func createSymmetricKey(serverPublicKeyPEM: String) -> SymmetricKey? {
        guard let privateKey = privateKey, 
              let publicKey = publicKey else { return nil }
        do {
            let serverPubKey = try P256.KeyAgreement.PublicKey(pemRepresentation: serverPublicKeyPEM)
            let shared = try privateKey.sharedSecretFromKeyAgreement(with: serverPubKey)
            let symetricKey = shared.hkdfDerivedSymmetricKey(using: SHA256.self,
                                                             salt: Data(bytes: iv, count: iv.count),
                                                             sharedInfo: publicKey.rawRepresentation + serverPubKey.rawRepresentation,
                                                             outputByteCount: 32)
            return symetricKey
        } catch {
            //TODO: Handle Error
            print("error \(error)")
            return nil
        }
    }

func decrypt(payload: String, symmetricKey: SymmetricKey) {
        guard let cipherText = Data(base64Encoded: payload) else { return }
        do {
//            let sealedBox = try ChaChaPoly.SealedBox(combined: cipherText)
//            let decrypted = try ChaChaPoly.open(sealedBox, using: symmetricKey)
            let sb = try AES.GCM.SealedBox(combined: cipherText)
            let decrypted = try AES.GCM.open(sb, using: symmetricKey)
            print("")
        } catch {
            print("error: \(error)") //here getting CryptoKit.CryptoKitError.authenticationFailure
        }
    }

我也知道后端的实现是怎样的:

public static String encrypt(String sessionKey, String devicePublicKey, String plainString) throws Exception {
        byte[] plain = Base64.getEncoder().encodeToString(plainString.getBytes(StandardCharsets.UTF_8)).getBytes();
        SecretKey key = generateSharedSecret(decodePrivateKey(sessionKey), decodePublicKey( devicePublicKey));
        Cipher encryptor = Cipher.getInstance("AES/CTR/NoPadding", BouncyCastleProvider.PROVIDER_NAME);
        IvParameterSpec ivSpec = new IvParameterSpec(INITIALIZATION_VECTOR);
        encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
        return Base64.getEncoder().encodeToString(encryptor.doFinal(plain, 0, plain.length));
    }

【问题讨论】:

  • Java 端的加密没有使用 Libsodiums/CryptoKit 密封盒完成。 Java 正在使用模式为“CTR”和“NoPadding”的 AES 算法,因此您需要在 ISO/swift 上找到支持此算法的库(对不起,我对这种组合没有任何经验)。
  • @MichaelFehr 谢谢。后端将 CTR 更改为 GCM,但我遇到了同样的错误,知道吗?
  • 无论 CTR 还是 GCM - Java 都不使用密封盒。尝试使用秘密盒子
  • @MichaelFehr 你的意思是在 java 端还是 swift 端?
  • 在 Swift 方面..

标签: java ios swift cryptography apple-cryptokit


【解决方案1】:

问题可能在于您使用的初始化向量或随机数。计算字节数,我们总共有 16 个 nonce 字节,尽管 GCM 只需要 12 个。现在,使用 16 不一定好或坏,但 CryptoKit 实现在调用 AES.GCM.SealedBox(combined:) 时假定 12 个字节。为了支持 16 个 nonce 字节,您必须改用 AES.GCM.SealedBox(nonce:ciphertext:tag:)

let ciphertext = Data(...)

do {
    let nonce = try AES.GCM.Nonce(data: ciphertext[0 ..< 16]
    let message = ciphertext[16 ..< ciphertext.count - 16]
    let tag = ciphertext[ciphertext.count - 16 ..< ciphertext.count]
    
    let sb = try AES.GCM.SealedBox(nonce: nonce, ciphertext: ciphertext, tag: tag)
    let decrypted = try AES.GCM.open(sb, using: key)
} catch {
    print("Error: \(error)")
}

查看您的服务器代码,确保共享密钥不“只是”共享密钥。 generateSharedSecret 听起来像是执行密钥交换后的秘密,但没有执行密钥派生(HKDF,如 Swift 代码所示)。

同时深入研究您的服务器代码,确保您的响应数据包含随机数、加密消息和标签。一些加密实现迫使您自己进行这种连接。因此,您应该确保 doFinal 包含标签(仅限 GCM),而不是 return Base64(doFinal)(伪代码),并返回 Base64(nonce + encrypted message + tag)。同样,仅在使用 GCM 时标记。

正如 cmets 中提到的,GCM 和 CTR 是 AES 的不同操作模式。确保在双方都使用相同的,所以要么在 iOS 和服务器上使用 GCM,要么在 iOS 和服务器上使用 CTR。不这样做,总是会导致解密失败。

如果您想使用 CTR,则必须查看旧的 Apple 加密库 CommonCrypto。这个实现了 CTR,但不支持 GCM(因为该实现从未发布过)。

最后一点,在使用 GCM 时,还要确保您的附加身份验证数据(如果有)是正确的。

【讨论】:

    猜你喜欢
    • 2014-05-29
    • 2014-07-05
    • 2021-12-17
    • 2014-08-17
    • 1970-01-01
    • 2014-12-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多