You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用react-native-sodium与PHP sodium扩展时crypto_box_seal_open()解密失败问题排查

Let's break down the possible issues that could be causing your crypto_box_seal_open() decryption failure — I've run into similar cross-platform libsodium headaches before, so here's what to check step by step:


1. You're Missing the Critical Encryption Step in PHP

Looking at your PHP code, there's no sign of calling sodium_crypto_box_seal() to actually encrypt the notification content! This is a huge red flag. You need to:

  • Convert the client's uploaded public key to binary (if the client sent it as base64, which it should)
  • Encrypt your plaintext notification with that public key
  • Encode the encrypted binary as base64 (since APNs payloads can't handle raw binary) and inject it into the push message

Here's a corrected PHP snippet:

// Assume $clientPublicKey is the base64-encoded key from your iOS app
$binaryPublicKey = base64_decode($clientPublicKey);
$plaintextContent = json_encode(['alert' => 'Your secure notification content']);

// Encrypt using libsodium's seal mode
$encryptedBinary = sodium_crypto_box_seal($plaintextContent, $binaryPublicKey);
$encryptedBase64 = base64_encode($encryptedBinary);

// Build the push message with the encrypted content
$msg = CloudMessage::fromArray(
    [
        "apns" => [
            "payload" => [
                "aps" => [
                    "sound" => 'default',
                    "content-available" => 1 // Enable silent processing if needed
                ],
                "encrypted_content" => $encryptedBase64 // Use a custom field instead of notification.body
            ],
        ]
    ]
);
sendMulticast($msg, $apn_tokens);

2. Your React Native Decoding Logic is Broken

Your current client code has two major encoding mistakes:

let note = JSON.parse(notification.notification.body.toString())
note = btoa(note)
  • If PHP sends a base64 ciphertext, JSON.parse() will fail (base64 isn't valid JSON)
  • btoa(note) will turn an object into "[object Object]" — this is nothing like your actual encrypted data

Fix it by directly decoding the base64 ciphertext to binary data (which react-native-sodium expects):

const handleIncomingNotification = async (notification) => { // Don't forget async!
    try {
        // Grab the base64-encrypted content from the custom payload field
        const encryptedBase64 = notification.data.encrypted_content;
        // Decode base64 to Uint8Array (react-native-sodium's expected format)
        const ciphertext = Sodium.from_base64(encryptedBase64, Sodium.base64_variants.ORIGINAL);
        // Decrypt using your app's own public and private keys
        const plaintextBinary = await Sodium.crypto_box_seal_open(ciphertext, public_key, private_key);
        // Convert binary back to a readable string
        const decryptedMessage = Sodium.to_string(plaintextBinary);
        console.log('Decrypted content:', decryptedMessage);
    } catch(e) {
        console.log('Decryption failed:', e);
    }
};

3. Key Format Mismatches Between Platforms

libsodium functions are strict about binary data — any encoding mismatch will break decryption:

  • When uploading the public key: Always send it as base64 from React Native, not raw binary (HTTP can corrupt raw bytes). Update your key upload code:
    const base64Pk = Sodium.to_base64(pk, Sodium.base64_variants.ORIGINAL);
    await sendToServer(base64Pk);
    
  • When storing keys locally: If you save public_key/private_key to AsyncStorage, store them as base64, then decode back to binary before use:
    // Example: Loading keys from storage
    const savedBase64Pk = await AsyncStorage.getItem('device_public_key');
    const public_key = Sodium.from_base64(savedBase64Pk, Sodium.base64_variants.ORIGINAL);
    
    const savedBase64Sk = await AsyncStorage.getItem('device_private_key');
    const private_key = Sodium.from_base64(savedBase64Sk, Sodium.base64_variants.ORIGINAL);
    

4. APNs Payload Gotchas

  • Size limits: Standard APNs payloads are capped at 2KB. If your encrypted content is too large, it'll get truncated — keep plaintext messages short.
  • Avoid notification.body: The notification field is meant for user-visible text. Use a custom payload field (like encrypted_content in the PHP example above) to avoid unintended formatting or truncation by APNs.

5. Debugging Tips

Since the error comes directly from libsodium's native crypto_box_seal_open call, the issue is almost certainly data/key corruption. Add logging to verify:

  • In PHP: Log the base64 public key you received and the base64 ciphertext you sent.
  • In React Native: Log the base64 ciphertext you receive, plus base64 versions of your stored keys. Compare these to PHP's logs to ensure no data was altered in transit.

内容的提问来源于stack exchange,提问作者UnknownFrequency

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 15:19:04