---
updatedAt: 2026-10-06T11:57:59.000Z
agentTools:
  projectIndex: https://docs.useblinc.com/llms.txt
---

# How to Encrypt and Decrypt Data

<Callout icon="📘" theme="info">
  ### Prerequisite

  You must generate a 256-bit elliptic curve keypair (EC-256), also known as NIST P-256, secp256r1, or prime256v1, and share the public key with Blinc.

  The private key must be generated and securely stored in **PKCS#8 format** within your environment. **Never share the private key with Blinc.**

  We recommend using separate keypairs for **test** and **live/production** environments.
</Callout>

For a Blinc request that requires encryption, encrypt the plaintext JSON payload before transmission. Base64-encode the encrypted payload envelope and send it in the data field of the request body.

```json
--data '{"data":"<base64 encrypted payload envelope>"}'
```

Use Base64-encoded X.509 SubjectPublicKeyInfo (SPKI) DER public keys, without PEM header or footer lines. The sender also creates a fresh, single-use P-256 key pair for each encrypted request. This is separate from the long-term key pair above.

```text
// Sample public key
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3G+eSLh2rQ6zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdQpCOOJw==
```

## Encrypt a request to Blinc

Use this flow only when calling an E2EE-enabled Blinc endpoint. Encrypt the request body with <Anchor target="_blank" href="https://docs.useblinc.com/docs/public-key">**Blinc's public key**</Anchor>. Do not use the public key you shared with Blinc.

1. Import Blinc's public key from Base64.
2. Generate a new P-256 key pair for this request.
3. &#x20;Create a shared AES key using HKDF-SHA256 with info `blinc-e2ee`.
4. Convert the request body to UTF-8 bytes, create a random 12-byte nonce, and encrypt it with AES-256-GCM.
5. Export the new public key as `0x04 || X || Y`.
6. Join the new public key, nonce, ciphertext, and tag together, then Base64-encode the result to get the payload envelope.

```text Encrypted request envelope
single-use public key (65 bytes)
nonce (12 bytes)
ciphertext (N bytes)
GCM tag (16 bytes)
```

### Encrypt code examples

Each example takes the plaintext request body and Blinc's Base64 <Anchor target="_blank" href="https://docs.useblinc.com/docs/public-key">public key</Anchor>, then returns the Base64 envelope to send as the encrypted request body.

<Tabs>
  <Tab title="C#" icon="fab fa-microsoft">
    ```csharp Encrypt request (.NET 8+)
    using System;
    using System.Security.Cryptography;
    using System.Text;

    public static string Envelope(string plaintext, string publicKeyBase64)
    {
        // 1. Import public Key
        using var publicKey = ECDiffieHellman.Create();
        publicKey.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKeyBase64.Trim()), out _);

        // 2. Fresh key pair
        using var ephemeral = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256);

        // 3. Derive shared AES Key
        byte[] shared = ephemeral.DeriveRawSecretAgreement(publicKey.PublicKey);
        byte[] aesKey = HKDF.DeriveKey(HashAlgorithmName.SHA256, shared, 32,
            salt: Array.Empty<byte>(),
            info: Encoding.UTF8.GetBytes("blinc-e2ee")
        );

        // 4. Encrypt with AES-256-GCM
        byte[] nonce = RandomNumberGenerator.GetBytes(12);
        byte[] plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
        byte[] ciphertext = new byte[plaintextBytes.Length];
        byte[] tag = new byte[16];

        using (var aes = new AesGcm(aesKey, tagSizeInBytes: 16))
        {
            aes.Encrypt(nonce, plaintextBytes, ciphertext, tag);
        }

        // 5. Extract Ephemeral public key: 0x04 || X || Y
        ECParameters q = ephemeral.ExportParameters(false);
        byte[] ephemeralPublic = new byte[65];
        ephemeralPublic[0] = 0x04;
        q.Q.X!.CopyTo(ephemeralPublic, 1);
        q.Q.Y!.CopyTo(ephemeralPublic, 33);

        CryptographicOperations.ZeroMemory(shared);
        CryptographicOperations.ZeroMemory(aesKey);

        // 6. Return envelope as Base64String
        byte[] envelope = [.. ephemeralPublic, .. nonce, .. ciphertext, .. tag];
        return Convert.ToBase64String(envelope);
    }
    ```
  </Tab>

  <Tab title="Node.js" icon="fab fa-node-js">
    ```javascript Encrypt request
    const { webcrypto } = require("crypto");

    async function envelope(plaintext, publicKeyBase64) {
      // 1. Import public key
      const publicKey = await webcrypto.subtle.importKey(
        "spki",
        Buffer.from(publicKeyBase64.trim(), "base64"),
        { name: "ECDH", namedCurve: "P-256" },
        false,
        []
      );

      // 2. Fresh key pair
      const ephemeral = await webcrypto.subtle.generateKey(
        { name: "ECDH", namedCurve: "P-256" },
        true,
        ["deriveBits"]
      );

      // 3. Derive shared AES key
      const shared = await webcrypto.subtle.deriveBits(
        { name: "ECDH", public: publicKey },
        ephemeral.privateKey,
        256
      );
      const hkdfKey = await webcrypto.subtle.importKey("raw", shared, "HKDF", false, ["deriveKey"]);
      const aesKey = await webcrypto.subtle.deriveKey(
        {
          name: "HKDF",
          hash: "SHA-256",
          salt: new Uint8Array(),
          info: new TextEncoder().encode("blinc-e2ee"),
        },
        hkdfKey,
        { name: "AES-GCM", length: 256 },
        false,
        ["encrypt"]
      );

      // 4. Encrypt with AES-256-GCM
      const nonce = webcrypto.getRandomValues(new Uint8Array(12));
      const plaintextBytes = new TextEncoder().encode(plaintext);
      const encrypted = new Uint8Array(await webcrypto.subtle.encrypt(
        { name: "AES-GCM", iv: nonce, tagLength: 128 },
        aesKey,
        plaintextBytes
      ));
      const ciphertext = encrypted.slice(0, -16);
      const tag = encrypted.slice(-16);

      // 5. Extract ephemeral public key: 0x04 || X || Y
      const ephemeralPublic = new Uint8Array(await webcrypto.subtle.exportKey("raw", ephemeral.publicKey));

      // 6. Return envelope as Base64 string
      return Buffer.concat([
        Buffer.from(ephemeralPublic),
        Buffer.from(nonce),
        Buffer.from(ciphertext),
        Buffer.from(tag),
      ]).toString("base64");
    }
    ```
  </Tab>

  <Tab title="Python" icon="fab fa-python">
    ```python Encrypt request
    import base64
    import os

    from cryptography.hazmat.primitives import hashes, serialization
    from cryptography.hazmat.primitives.asymmetric import ec
    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
    from cryptography.hazmat.primitives.kdf.hkdf import HKDF


    def envelope(plaintext: str, public_key_base64: str) -> str:
        # 1. Import public key
        public_key = serialization.load_der_public_key(
            base64.b64decode(public_key_base64.strip())
        )

        # 2. Fresh key pair
        ephemeral = ec.generate_private_key(ec.SECP256R1())

        # 3. Derive shared AES key
        shared = ephemeral.exchange(ec.ECDH(), public_key)
        aes_key = HKDF(
            algorithm=hashes.SHA256(),
            length=32,
            salt=b"",
            info=b"blinc-e2ee",
        ).derive(shared)

        # 4. Encrypt with AES-256-GCM
        nonce = os.urandom(12)
        plaintext_bytes = plaintext.encode("utf-8")
        encrypted = AESGCM(aes_key).encrypt(nonce, plaintext_bytes, None)
        ciphertext = encrypted[:-16]
        tag = encrypted[-16:]

        # 5. Extract ephemeral public key: 0x04 || X || Y
        ephemeral_public = ephemeral.public_key().public_bytes(
            encoding=serialization.Encoding.X962,
            format=serialization.PublicFormat.UncompressedPoint,
        )

        # 6. Return envelope as Base64 string
        return base64.b64encode(ephemeral_public + nonce + ciphertext + tag).decode("ascii")
    ```
  </Tab>

  <Tab title="Java" icon="fab fa-java">
    ```java Encrypt request
    import java.math.BigInteger;
    import java.nio.charset.StandardCharsets;
    import java.security.KeyFactory;
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.SecureRandom;
    import java.security.interfaces.ECPublicKey;
    import java.security.spec.ECGenParameterSpec;
    import java.security.spec.X509EncodedKeySpec;
    import java.util.Arrays;
    import java.util.Base64;
    import javax.crypto.Cipher;
    import javax.crypto.KeyAgreement;
    import javax.crypto.Mac;
    import javax.crypto.spec.GCMParameterSpec;
    import javax.crypto.spec.SecretKeySpec;

    public class BlincE2EE {
        public static String envelope(String plaintext, String publicKeyBase64) throws Exception {
            // 1. Import public key
            byte[] publicKeyDer = Base64.getDecoder().decode(publicKeyBase64.trim());
            ECPublicKey publicKey = (ECPublicKey) KeyFactory.getInstance("EC")
                .generatePublic(new X509EncodedKeySpec(publicKeyDer));

            // 2. Fresh key pair
            KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
            generator.initialize(new ECGenParameterSpec("secp256r1"));
            KeyPair ephemeral = generator.generateKeyPair();

            // 3. Derive shared AES key
            KeyAgreement agreement = KeyAgreement.getInstance("ECDH");
            agreement.init(ephemeral.getPrivate());
            agreement.doPhase(publicKey, true);
            byte[] shared = agreement.generateSecret();
            byte[] aesKey = hkdfSha256(shared, "blinc-e2ee".getBytes(StandardCharsets.UTF_8));

            // 4. Encrypt with AES-256-GCM
            byte[] nonce = new byte[12];
            new SecureRandom().nextBytes(nonce);
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(aesKey, "AES"), new GCMParameterSpec(128, nonce));
            byte[] ciphertextAndTag = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));

            byte[] ciphertext = Arrays.copyOfRange(ciphertextAndTag, 0, ciphertextAndTag.length - 16);
            byte[] tag = Arrays.copyOfRange(ciphertextAndTag, ciphertextAndTag.length - 16, ciphertextAndTag.length);

            // 5. Extract ephemeral public key: 0x04 || X || Y
            ECPublicKey ephemeralPublicKey = (ECPublicKey) ephemeral.getPublic();
            byte[] ephemeralPublic = concat(new byte[] { 0x04 },
                toFixed32(ephemeralPublicKey.getW().getAffineX()),
                toFixed32(ephemeralPublicKey.getW().getAffineY()));

            // 6. Return envelope as Base64 string
            return Base64.getEncoder().encodeToString(concat(ephemeralPublic, nonce, ciphertext, tag));
        }

        private static byte[] hkdfSha256(byte[] secret, byte[] info) throws Exception {
            byte[] prk = hmacSha256(new byte[32], secret);
            return Arrays.copyOf(hmacSha256(prk, concat(info, new byte[] { 0x01 })), 32);
        }

        private static byte[] hmacSha256(byte[] key, byte[] data) throws Exception {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(key, "HmacSHA256"));
            return mac.doFinal(data);
        }

        private static byte[] toFixed32(BigInteger value) {
            byte[] bytes = value.toByteArray();
            if (bytes.length == 32) return bytes;
            if (bytes.length > 32) return Arrays.copyOfRange(bytes, bytes.length - 32, bytes.length);
            byte[] padded = new byte[32];
            System.arraycopy(bytes, 0, padded, 32 - bytes.length, bytes.length);
            return padded;
        }

        private static byte[] concat(byte[]... parts) {
            int length = 0;
            for (byte[] part : parts) length += part.length;
            byte[] output = new byte[length];
            int offset = 0;
            for (byte[] part : parts) {
                System.arraycopy(part, 0, output, offset, part.length);
                offset += part.length;
            }
            return output;
        }
    }
    ```
  </Tab>

  <Tab title="Go" icon="fab fa-golang">
    ```go Encrypt request
    package blince2ee

    import (
        "crypto/aes"
        "crypto/cipher"
        "crypto/ecdh"
        "crypto/ecdsa"
        "crypto/hkdf"
        "crypto/rand"
        "crypto/sha256"
        "crypto/x509"
        "encoding/base64"
        "io"
    )

    func Envelope(plaintext string, publicKeyBase64 string) (string, error) {
        // 1. Import public key
        der, err := base64.StdEncoding.DecodeString(publicKeyBase64)
        if err != nil {
            return "", err
        }

        parsed, err := x509.ParsePKIXPublicKey(der)
        if err != nil {
            return "", err
        }
        ecdsaPublicKey := parsed.(*ecdsa.PublicKey)
        publicKey, err := ecdsaPublicKey.ECDH()
        if err != nil {
            return "", err
        }

        // 2. Fresh key pair
        ephemeral, err := ecdh.P256().GenerateKey(rand.Reader)
        if err != nil {
            return "", err
        }

        // 3. Derive shared AES key
        shared, err := ephemeral.ECDH(publicKey)
        if err != nil {
            return "", err
        }
        aesKey := make([]byte, 32)
        if _, err := io.ReadFull(hkdf.New(sha256.New, shared, []byte{}, []byte("blinc-e2ee")), aesKey); err != nil {
            return "", err
        }

        // 4. Encrypt with AES-256-GCM
        nonce := make([]byte, 12)
        if _, err := rand.Read(nonce); err != nil {
            return "", err
        }
        block, err := aes.NewCipher(aesKey)
        if err != nil {
            return "", err
        }
        gcm, err := cipher.NewGCM(block)
        if err != nil {
            return "", err
        }
        encrypted := gcm.Seal(nil, nonce, []byte(plaintext), nil)
        ciphertext := encrypted[:len(encrypted)-16]
        tag := encrypted[len(encrypted)-16:]

        // 5. Extract ephemeral public key: 0x04 || X || Y
        ephemeralPublic := ephemeral.PublicKey().Bytes()

        // 6. Return envelope as Base64 string
        envelope := append(append(append(ephemeralPublic, nonce...), ciphertext...), tag...)
        return base64.StdEncoding.EncodeToString(envelope), nil
    }
    ```
  </Tab>
</Tabs>

## Decrypt a request from Blinc

Use this flow when Blinc sends you an encrypted request. Decrypt the request body with the private key that matches the public key you shared with Blinc.

1. Base64-decode the encrypted envelope.
2. Read the first 65 bytes as the sender's single-use public key.
3. Read the next 12 bytes as the nonce.
4. Read the last 16 bytes as the GCM tag.
5. Treat the bytes between the nonce and tag as the ciphertext.
6. Use ECDH and HKDF-SHA256 with info `blinc-e2ee` to recreate the AES key, then decrypt with AES-256-GCM.

### Decrypt code examples

<Tabs>
  <Tab title="C#" icon="fab fa-microsoft">
    ```csharp Decrypt request (.NET 8+)
    using System;
    using System.Security.Cryptography;
    using System.Text;

    public static string DecryptEnvelope(string encryptedDataBase64, string privateKeyBase64)
    {
        // 1. Decode the Base64 envelope
        byte[] envelope = Convert.FromBase64String(encryptedDataBase64);

        // 2. Extract ephemeral public key
        byte[] ephemeralPublic = envelope[..65];

        // 3. Extract nonce
        byte[] nonce = envelope[65..77];

        // 4. Extract ciphertext
        byte[] ciphertext = envelope[77..^16];

        // 5. Extract authentication tag
        byte[] tag = envelope[^16..];

        // 6. Derive the AES key and decrypt
        using var privateKey = ECDiffieHellman.Create();
        privateKey.ImportPkcs8PrivateKey(Convert.FromBase64String(privateKeyBase64.Trim()), out _);

        ECParameters point = new()
        {
            Curve = ECCurve.NamedCurves.nistP256,
            Q = new ECPoint
            {
                X = ephemeralPublic[1..33],
                Y = ephemeralPublic[33..65]
            }
        };

        using var ephemeral = ECDiffieHellman.Create(point);
        byte[] shared = privateKey.DeriveRawSecretAgreement(ephemeral.PublicKey);
        byte[] aesKey = HKDF.DeriveKey(HashAlgorithmName.SHA256, shared, 32,
            salt: Array.Empty<byte>(),
            info: Encoding.UTF8.GetBytes("blinc-e2ee")
        );

        byte[] plaintext = new byte[ciphertext.Length];
        using (var aes = new AesGcm(aesKey, tagSizeInBytes: 16))
        {
            aes.Decrypt(nonce, ciphertext, tag, plaintext);
        }
        CryptographicOperations.ZeroMemory(shared);
        CryptographicOperations.ZeroMemory(aesKey);

        return Encoding.UTF8.GetString(plaintext);
    }
    ```
  </Tab>

  <Tab title="Node.Js" icon="fab fa-node-js">
    ```javascript Decrypt request
    const { webcrypto } = require("crypto");

    async function decryptEnvelope(encryptedDataBase64, privateKeyBase64) {
      // 1. Decode the Base64 envelope
      const envelope = Buffer.from(encryptedDataBase64, "base64");

      // 2. Extract ephemeral public key
      const ephemeralPublicBytes = envelope.subarray(0, 65);

      // 3. Extract nonce
      const nonce = envelope.subarray(65, 77);

      // 4. Extract ciphertext
      const ciphertext = envelope.subarray(77, envelope.length - 16);

      // 5. Extract authentication tag
      const tag = envelope.subarray(envelope.length - 16);

      // 6. Derive the AES key and decrypt
      const privateKey = await webcrypto.subtle.importKey(
        "pkcs8",
        Buffer.from(privateKeyBase64.trim(), "base64"),
        { name: "ECDH", namedCurve: "P-256" },
        false,
        ["deriveBits"]
      );
      const ephemeralPublic = await webcrypto.subtle.importKey(
        "raw",
        ephemeralPublicBytes,
        { name: "ECDH", namedCurve: "P-256" },
        false,
        []
      );

      const shared = await webcrypto.subtle.deriveBits(
        { name: "ECDH", public: ephemeralPublic },
        privateKey,
        256
      );
      const hkdfKey = await webcrypto.subtle.importKey("raw", shared, "HKDF", false, ["deriveKey"]);
      const aesKey = await webcrypto.subtle.deriveKey(
        {
          name: "HKDF",
          hash: "SHA-256",
          salt: new Uint8Array(),
          info: new TextEncoder().encode("blinc-e2ee"),
        },
        hkdfKey,
        { name: "AES-GCM", length: 256 },
        false,
        ["decrypt"]
      );

      const encrypted = Buffer.concat([ciphertext, tag]);
      const plaintext = await webcrypto.subtle.decrypt(
        { name: "AES-GCM", iv: nonce, tagLength: 128 },
        aesKey,
        encrypted
      );

      return new TextDecoder().decode(plaintext);
    }
    ```
  </Tab>

  <Tab title="Python" icon="fab fa-python">
    ```python Python
    import base64

    from cryptography.hazmat.primitives import hashes, serialization
    from cryptography.hazmat.primitives.asymmetric import ec
    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
    from cryptography.hazmat.primitives.kdf.hkdf import HKDF


    def decrypt_envelope(encrypted_data_base64: str, private_key_base64: str) -> str:
        # 1. Decode the Base64 envelope
        envelope = base64.b64decode(encrypted_data_base64)

        # 2. Extract ephemeral public key
        ephemeral_public_bytes = envelope[:65]

        # 3. Extract nonce
        nonce = envelope[65:77]

        # 4. Extract ciphertext
        ciphertext = envelope[77:-16]

        # 5. Extract authentication tag
        tag = envelope[-16:]

        # 6. Derive the AES key and decrypt
        private_key = serialization.load_der_private_key(
            base64.b64decode(private_key_base64.strip()),
            password=None,
        )

        ephemeral_public = ec.EllipticCurvePublicKey.from_encoded_point(
            ec.SECP256R1(),
            ephemeral_public_bytes,
        )

        shared = private_key.exchange(ec.ECDH(), ephemeral_public)

        aes_key = HKDF(
            algorithm=hashes.SHA256(),
            length=32,
            salt=b"",
            info=b"blinc-e2ee",
        ).derive(shared)

        plaintext = AESGCM(aes_key).decrypt(nonce, ciphertext + tag, None)
        return plaintext.decode("utf-8")
    ```
  </Tab>

  <Tab title="Java" icon="fab fa-java">
    ```java Java
    import java.math.BigInteger;
    import java.nio.charset.StandardCharsets;
    import java.security.AlgorithmParameters;
    import java.security.KeyFactory;
    import java.security.PrivateKey;
    import java.security.spec.ECGenParameterSpec;
    import java.security.spec.ECParameterSpec;
    import java.security.spec.ECPoint;
    import java.security.spec.ECPublicKeySpec;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.util.Arrays;
    import java.util.Base64;
    import javax.crypto.Cipher;
    import javax.crypto.KeyAgreement;
    import javax.crypto.Mac;
    import javax.crypto.spec.GCMParameterSpec;
    import javax.crypto.spec.SecretKeySpec;

    public class BlincE2EEDecrypt {
        public static String decryptEnvelope(String encryptedDataBase64, String privateKeyBase64) throws Exception {
            // 1. Decode the Base64 envelope
            byte[] envelope = Base64.getDecoder().decode(encryptedDataBase64);

            // 2. Extract ephemeral public key
            byte[] ephemeralPublic = Arrays.copyOfRange(envelope, 0, 65);

            // 3. Extract nonce
            byte[] nonce = Arrays.copyOfRange(envelope, 65, 77);

            // 4. Extract ciphertext
            byte[] ciphertext = Arrays.copyOfRange(envelope, 77, envelope.length - 16);

            // 5. Extract authentication tag
            byte[] tag = Arrays.copyOfRange(envelope, envelope.length - 16, envelope.length);

            // 6. Derive the AES key and decrypt
            KeyFactory keyFactory = KeyFactory.getInstance("EC");
            PrivateKey privateKey = keyFactory.generatePrivate(
                new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyBase64.trim()))
            );

            AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC");
            parameters.init(new ECGenParameterSpec("secp256r1"));
            ECParameterSpec ecSpec = parameters.getParameterSpec(ECParameterSpec.class);

            ECPoint point = new ECPoint(
                new BigInteger(1, Arrays.copyOfRange(ephemeralPublic, 1, 33)),
                new BigInteger(1, Arrays.copyOfRange(ephemeralPublic, 33, 65))
            );

            KeyAgreement agreement = KeyAgreement.getInstance("ECDH");
            agreement.init(privateKey);
            agreement.doPhase(keyFactory.generatePublic(new ECPublicKeySpec(point, ecSpec)), true);

            byte[] shared = agreement.generateSecret();
            byte[] aesKey = hkdfSha256(shared, "blinc-e2ee".getBytes(StandardCharsets.UTF_8));

            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(aesKey, "AES"), new GCMParameterSpec(128, nonce));

            byte[] plaintext = cipher.doFinal(concat(ciphertext, tag));
            return new String(plaintext, StandardCharsets.UTF_8);
        }

        private static byte[] hkdfSha256(byte[] secret, byte[] info) throws Exception {
            byte[] prk = hmacSha256(new byte[32], secret);
            return Arrays.copyOf(hmacSha256(prk, concat(info, new byte[] { 0x01 })), 32);
        }

        private static byte[] hmacSha256(byte[] key, byte[] data) throws Exception {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(key, "HmacSHA256"));
            return mac.doFinal(data);
        }

        private static byte[] concat(byte[]... parts) {
            int length = 0;
            for (byte[] part : parts) length += part.length;

            byte[] output = new byte[length];
            int offset = 0;

            for (byte[] part : parts) {
                System.arraycopy(part, 0, output, offset, part.length);
                offset += part.length;
            }

            return output;
        }
    }
    ```
  </Tab>

  <Tab title="Go" icon="fab fa-golang">
    ```go Go
    package blince2ee

    import (
        "crypto/aes"
        "crypto/cipher"
        "crypto/ecdh"
        "crypto/ecdsa"
        "crypto/hkdf"
        "crypto/sha256"
        "crypto/x509"
        "encoding/base64"
        "io"
    )

    func DecryptEnvelope(encryptedDataBase64 string, privateKeyBase64 string) (string, error) {
        // 1. Decode the Base64 envelope
        envelope, err := base64.StdEncoding.DecodeString(encryptedDataBase64)
        if err != nil {
            return "", err
        }

        // 2. Extract ephemeral public key
        ephemeralPublicBytes := envelope[:65]

        // 3. Extract nonce
        nonce := envelope[65:77]

        // 4. Extract ciphertext
        ciphertext := envelope[77 : len(envelope)-16]

        // 5. Extract authentication tag
        tag := envelope[len(envelope)-16:]

        // 6. Derive the AES key and decrypt
        privateKeyDer, err := base64.StdEncoding.DecodeString(privateKeyBase64)
        if err != nil {
            return "", err
        }

        parsedPrivateKey, err := x509.ParsePKCS8PrivateKey(privateKeyDer)
        if err != nil {
            return "", err
        }

        ecdsaPrivateKey := parsedPrivateKey.(*ecdsa.PrivateKey)

        privateKey, err := ecdsaPrivateKey.ECDH()
        if err != nil {
            return "", err
        }

        ephemeralPublic, err := ecdh.P256().NewPublicKey(ephemeralPublicBytes)
        if err != nil {
            return "", err
        }

        shared, err := privateKey.ECDH(ephemeralPublic)
        if err != nil {
            return "", err
        }

        aesKey := make([]byte, 32)
        if _, err := io.ReadFull(hkdf.New(sha256.New, shared, []byte{}, []byte("blinc-e2ee")), aesKey); err != nil {
            return "", err
        }

        block, err := aes.NewCipher(aesKey)
        if err != nil {
            return "", err
        }

        gcm, err := cipher.NewGCM(block)
        if err != nil {
            return "", err
        }

        encrypted := append(ciphertext, tag...)

        plaintext, err := gcm.Open(nil, nonce, encrypted, nil)
        if err != nil {
            return "", err
        }

        return string(plaintext), nil
    }
    ```
  </Tab>
</Tabs>