Clipzy Online Clipboard

Category overview

An end-to-end encrypted text snippet service provided by the third-party service Clipzy™. For details, please refer to their Privacy Policy.

The core of Clipzy is "end-to-end encryption", which means the "keys" for encryption and decryption exist only on your and the recipient's devices. The server is only responsible for storing an "encrypted safe" that no one can open, thus ensuring absolute privacy of content.

How to Use: Two Methods

Depending on your security needs and usage scenarios, we provide two methods.

This method follows a strict end-to-end encryption model where your original text and key are never sent to the server.

Applicable Scenarios: Sharing sensitive information in web applications, building secure online notes, etc.

Workflow:

  • Step 1: Create and Encrypt (in your application)

    1. Generate Key: Create a unique key locally on the client side.
    2. Encrypt Content: Use this key to lock your original text into a safe.
    3. Upload Safe: Call the POST /api/store endpoint to hand over the locked safe (ciphertext) to the server for storage, and the server will give you a locker number (ID).
    4. Combine Share Link: Combine the locker number (ID) and your key into a special share link.
  • Step 2: Share and Decrypt (in the recipient's application)

    1. Parse Link: The recipient opens the link, and the application automatically separates the locker number and key from the link.
    2. Retrieve Safe: Call the GET /api/get endpoint to retrieve the encrypted safe using the ID.
    3. Unlock: Use the key to unlock the safe locally and view the original content.

The Complete JavaScript Example below demonstrates this method in detail.

Method 2: Server-side Decryption (Convenient for Automation)

This method has lower security because it requires you to send the key as a parameter to the server. However, it's very convenient for automated scripts (like curl or Python scripts) that cannot perform complex decryption operations.

Applicable Scenarios: Quickly retrieving previously stored configurations or secret information in command lines or backend services.

Workflow:

  1. Preparation: You first need to know the ID (locker number) and key of an encrypted snippet. This is usually something you created earlier via Method 1 and saved yourself.
  2. Request Decryption: Call the GET /api/raw/{id} endpoint, providing both the locker number (ID) and key to the server.
  3. Get Original Text: The server uses the key you provided to unlock the safe and then directly hands you what's inside (the original plain text).
Warning

Use with caution! Although the server promises not to log keys or decrypted content, this operation breaks the strict end-to-end encryption model. Please use only when you trust the server or when the content sensitivity is low.


Code Examples

Example 1 (Highest Security): Complete JavaScript Implementation

Online API testing tools cannot simulate the complete end-to-end encryption process. The following example shows how to implement all steps from key generation to final decryption in your website or application through JavaScript. You can save this code as an HTML file and run it directly in a browser.

HTML
<!DOCTYPE html>
<html>
<head>
    <title>Clipzy E2EE Demo</title>
    <!-- Import lz-string library -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.5.0/lz-string.min.js"></script>
</head>
<body>
    <h1>Clipzy End-to-End Encryption Example</h1>
    <textarea id="originalText" rows="10" cols="80" placeholder="Enter the text you want to encrypt and share here..."></textarea>
    <br>
    <button onclick="createEncryptedPaste()">Create Encrypted Link</button>
    <hr>
    <h2>Generated Share Link:</h2>
    <input id="shareableLink" type="text" size="100" readonly>
    <hr>
    <h2>Parse Content from Link:</h2>
    <input id="pasteLink" type="text" size="100" placeholder="Paste the generated link above here for decryption">
    <button onclick="decryptPasteFromLink()">Decrypt</button>
    <h3>Decrypted Content:</h3>
    <pre id="decryptedText" style="background-color: #f0f0f0; border: 1px solid #ccc; padding: 10px;"></pre>

    <script>
        // --- Helper functions: Convert between ArrayBuffer and Base64 ---
        function arrayBufferToBase64(buffer) {
            let binary = '';
            const bytes = new Uint8Array(buffer);
            const len = bytes.byteLength;
            for (let i = 0; i < len; i++) {
                binary += String.fromCharCode(bytes[i]);
            }
            return window.btoa(binary);
        }

        function base64ToArrayBuffer(base64) {
            const binary_string = window.atob(base64);
            const len = binary_string.length;
            const bytes = new Uint8Array(len);
            for (let i = 0; i < len; i++) {
                bytes[i] = binary_string.charCodeAt(i);
            }
            return bytes.buffer;
        }

        // --- Core encryption function ---
        async function createEncryptedPaste() {
            try {
                document.getElementById('shareableLink').value = 'Creating, please wait...';
                const originalText = document.getElementById('originalText').value;
                if (!originalText) {
                    alert('Please enter content!');
                    return;
                }

                // 1. Generate an AES-GCM key on the client side
                const key = await window.crypto.subtle.generateKey(
                    { name: "AES-GCM", length: 256 },
                    true, // a boolean value indicating whether the key can be extracted from the CryptoKey object
                    ["encrypt", "decrypt"]
                );

                // 2. Compress original text using LZ-String
                const compressedText = LZString.compressToUTF16(originalText);

                // 3. Encrypt compressed data using the key
                const iv = window.crypto.getRandomValues(new Uint8Array(12)); // GCM recommends 12-byte IV
                const encryptedData = await window.crypto.subtle.encrypt(
                    { name: "AES-GCM", iv: iv },
                    key,
                    new TextEncoder().encode(compressedText)
                );

                // Combine IV and encrypted data, convert to Base64
                const combined = new Uint8Array(iv.length + encryptedData.byteLength);
                combined.set(iv, 0);
                combined.set(new Uint8Array(encryptedData), iv.length);
                const dataToSend = arrayBufferToBase64(combined);
                
                // 4. Call POST /api/store to upload encrypted data
                const response = await fetch('https://paste.sdjz.wiki/api/store', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ compressedData: dataToSend, ttl: 3600 })
                });
                const result = await response.json();
                if (!response.ok) throw new Error(result.error || 'Upload failed');

                // 5. Combine ID and Base64 key into URL fragment
                const exportedKey = await window.crypto.subtle.exportKey("raw", key);
                const keyB64 = arrayBufferToBase64(exportedKey);
                const fragment = `${result.id}!${keyB64}`;

                // 6. Build complete share link
                const shareableLink = `https://paste.sdjz.wiki/#${fragment}`;
                document.getElementById('shareableLink').value = shareableLink;

            } catch (error) {
                console.error('Encryption failed:', error);
                document.getElementById('shareableLink').value = `Error: ${error.message}`;
            }
        }

        // --- Core decryption function ---
        async function decryptPasteFromLink() {
            try {
                document.getElementById('decryptedText').innerText = 'Decrypting, please wait...';
                const pasteLink = document.getElementById('pasteLink').value;
                const fragment = new URL(pasteLink).hash.substring(1);
                if (!fragment.includes('!')) throw new Error('Invalid link format');
                
                // 1. Parse ID and key from URL fragment
                const [id, keyB64] = fragment.split('!');
                const keyBuffer = base64ToArrayBuffer(keyB64);
                const key = await window.crypto.subtle.importKey(
                    "raw",
                    keyBuffer,
                    { name: "AES-GCM" },
                    true,
                    ["decrypt"]
                );

                // 2. Call GET /api/get to retrieve encrypted data
                const response = await fetch(`https://paste.sdjz.wiki/api/get?id=${id}`);
                const result = await response.json();
                if (!response.ok) throw new Error(result.error || 'Failed to get data');

                const combined = base64ToArrayBuffer(result.compressedData);
                const iv = combined.slice(0, 12);
                const data = combined.slice(12);

                // 3. Decrypt data on client side using the key
                const decryptedData = await window.crypto.subtle.decrypt(
                    { name: "AES-GCM", iv: iv },
                    key,
                    data
                );

                // 4. Decompress decrypted data
                const decompressedText = LZString.decompressFromUTF16(new TextDecoder().decode(decryptedData));
                
                // 5. Success, display original text
                document.getElementById('decryptedText').innerText = decompressedText;

            } catch (error) {
                console.error('Decryption failed:', error);
                document.getElementById('decryptedText').innerText = `Error: ${error.message}`;
            }
        }

    </script>
</body>
</html>

Example 2 (Convenient for Automation): Get Plain Text Directly Using cURL

Assuming you have already created an encrypted snippet through some method (e.g., running the JS example above) and obtained the ID and key:

  • ID: tzy4AllxQP
  • Key: a1b2c3d4e5f6g7h8 (this is just an example, actual keys are long Base64 strings)

Now, you can use curl directly in the command line anywhere to get the original text:

BASH
# Replace {id} and {key} with your own
curl "https://paste.sdjz.wiki/api/raw/tzy4AllxQP?key=a1b2c3d4e5f6g7h8"

The server will return the decrypted plain text, which is very suitable for use in scripts.