Docs - API-Beispiele

Geheimnis erstellen

API-Abfrage: POST https://password.link/api/secrets

Erforderlicher API-Schlüsseltyp: privater API-Schlüssel

Ein Beispiel für die Verschlüsselung eines Geheimnisses und dessen Übermittlung an die password.link-API mithilfe von JavaScript. Verwendet jQuery, seedrandom und SJCL.

Ein Skript wie dieses kann auf einem lokalen Computer oder in einem lokalen Netzwerk verwendet werden, um auf bequeme Weise Geheimnisse für unseren Dienst zu erstellen. Allerdings sollten Sie dieses Skript niemals an einem öffentlichen Ort ohne angemessene Authentifizierung einsetzen.

<html>
  <head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
    <meta content="utf-8" http-equiv="encoding">

    <!-- Get the latest jQuery from https://jquery.com -->
    <script src="jquery-3.4.1.min.js"></script>

    <!-- Get sjcl.js from https://github.com/bitwiseshiftleft/sjcl -->
    <script src="sjcl.js"></script>

    <!-- Get seedrandom.min.js from https://github.com/davidbau/seedrandom -->
    <script src="seedrandom.min.js"></script>

    <script>
      // Set the base URL for the generated link
      var LINK_BASE_URL = "https://some.site/secret.html";

      // Set the API key here - a private API key is required
      var PRIVATE_API_KEY = "private_key_abcd...";

      // ----------------------------------- //

      // A function for sending the secret to password.link API
      function send_to_passwordlink_api(secret) {
        // Create the public password part
        var password_part_public = generate_string();
        var password_part_public_base64 = btoa(password_part_public);

        // Create the private password part
        var password_part_private = generate_string();
        var password_part_private_base64 = btoa(password_part_private);

        // Create an SJCL compatible Base64 encoded ciphertext
        var ciphertext = encrypt_secret(password_part_public, password_part_private, secret);

        // The data which will be sent over to the password.link API
        var data = {
          "ciphertext": ciphertext,
          "password_part_private": password_part_private_base64
        };

        // Send a request to the password.link API and process the result
        $.ajax({
          type: "POST",
          url: "https://password.link/api/secrets",
          dataType: "json",
          contentType: "application/json",
          headers: {
            "Authorization": "ApiKey " + PRIVATE_API_KEY
          },
          data: JSON.stringify(data),
          success: function(data) {
            var secret = data.data;
            var meta = data.metadata;
            var secret_url = LINK_BASE_URL + "?" + secret.id + "#" + password_part_public_base64;
            $("#secret").html("URL to secret: " + secret_url);
            $("#secret-meta").html("Total secrets: " + meta.secrets_total);
            $("#secret-meta").append("<br> Usage: " + meta.secrets_usage);
            $("#secret-meta").append("<br> Allowance: " + meta.secrets_allowance);
          },
          error: function(data) {
            var error = data.responseJSON.error;
            $("#secret").html("Error: " + error.message);
          }
        });
      }

      // A function for encrypting a secret, returns a Base64 encoded SJCL compatible ciphertext
      function encrypt_secret(password_part_public, password_part_private, secret) {
        try {
          var ciphertext_base64 = btoa(sjcl.encrypt(
            password_part_private + password_part_public,
            secret,
            { "mode": "gcm", "ks": 256, "iter": 10000 }
          ));

          return ciphertext_base64;
        }
        // Catch and show errors
        catch(e) {
          $("#secret").html("Error during encryption: " + e);
        }
      }

      // A function for generating a random 18 characters long string
      function generate_string() {
        var len = 18;
        var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$|/\!_+,.-?()[]{}<>&#^*=@";

        // Use seedrandom.js to create autoseeded PRNG
        Math.seedrandom();

        var str = "";
        for (var i = 0; i < len; i++) {
          str += chars.charAt(Math.floor(Math.random() * chars.length));
        }

        return str;
      }

      // Execute the send function when the button is clicked
      $(document).ready(function() {
        $("#secret-button").click(function(e) {
          e.preventDefault();
          var secret = $("#secret-input").val();
          send_to_passwordlink_api(secret);
        });
      });
    </script>
    <style>
      body {
        font: 12px Arial;
      }
      .container {
        max-width: 960px;
        margin: 0 auto;
        text-align: center;
        margin-top: 60px;
      }
      #secret {
        margin-top: 30px;
        font-size: 18px;
      }
      #secret-meta {
        margin-top: 40px;
      }
      .secret-form {
        display: flex;
        flex-flow: row wrap;
        align-items: center;
        flex-direction: vertical;
        justify-content: center;
      }
      .secret-form input {
        vertical-align: middle;
        margin: 5px 10px 5px 0;
        padding: 10px;
        border: 1px solid #ddd;
        width: 200px;
      }
      .secret-form button {
        padding: 10px 20px;
        border: 1px solid #ddd;
      }
      .secret-form button:hover {
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <h2>Encrypt and create a secret on password.link</h2>
      <form class="secret-form">
        <input id="secret-input" type="text" name="secret">
        <button id="secret-button">Encrypt and create link</button>
      </form>

      <!-- This div will contain the link to the secret (or error) -->
      <div id="secret"></div>
      <div id="secret-meta"></div>
    </div>
  </body>
</html>

Create Secret with Attachment

This example shows the attachment-specific parts. It assumes you have already created the secret ciphertext and password parts as shown in the Create Secret example above.

// Set these first
const PASSWORDLINK_BASE_URL = "https://password.link";
const PRIVATE_API_KEY = "private_key_abcd...";

// These values come from the Create Secret example above
const ciphertext = "...";
const passwordPartPrivate = "...";
const passwordPartPublic = "...";
const passwordPartPrivateBase64 = btoa(passwordPartPrivate);

async function createSecretWithAttachment(file) {
  const createResponse = await fetch(`${PASSWORDLINK_BASE_URL}/api/secrets`, {
    method: "POST",
    headers: {
      "Authorization": "ApiKey " + PRIVATE_API_KEY,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      ciphertext: ciphertext,
      password_part_private: passwordPartPrivateBase64,
      attachment: {
        file_name: file.name,
        file_type: file.type || "application/octet-stream",
        file_size: file.size
      }
    })
  });

  const createData = await createResponse.json();

  if (!createResponse.ok) {
    throw new Error(createData.error?.message || "Failed to create secret");
  }

  const attachmentUpload = createData.data.attachment.upload;
  const fileDataUrl = await fileToDataUrl(file);
  const encryptedFile = await encryptAttachment(
    fileDataUrl,
    passwordPartPrivate + passwordPartPublic
  );

  const formData = new FormData();
  appendFormFields(formData, attachmentUpload.metadata);
  appendFormFields(formData, attachmentUpload.fields);
  formData.append("file", new Blob([encryptedFile], { type: "text/plain" }), "file.txt");

  const uploadUrl = new URL(attachmentUpload.url, PASSWORDLINK_BASE_URL);
  const uploadOptions = {
    method: "POST",
    body: formData
  };

  if (uploadUrl.origin === new URL(PASSWORDLINK_BASE_URL).origin) {
    uploadOptions.headers = {
      "Authorization": "ApiKey " + PRIVATE_API_KEY
    };
  } else {
    uploadOptions.mode = "no-cors";
  }

  const uploadResponse = await fetch(uploadUrl.toString(), uploadOptions);

  if (uploadResponse.status !== 0 && !uploadResponse.ok) {
    throw new Error("Failed to upload attachment");
  }

  return createData.data.id;
}

function appendFormFields(formData, fields) {
  if (!fields) {
    return;
  }

  Object.entries(fields).forEach(([key, value]) => {
    formData.append(key, value);
  });
}

function fileToDataUrl(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

async function encryptAttachment(plaintext, password) {
  const salt = crypto.getRandomValues(new Uint8Array(16));
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const passwordKey = await crypto.subtle.importKey(
    "raw",
    new TextEncoder().encode(password),
    "PBKDF2",
    false,
    ["deriveKey"]
  );

  const key = await crypto.subtle.deriveKey(
    { name: "PBKDF2", salt: salt, iterations: 10000, hash: "SHA-256" },
    passwordKey,
    { name: "AES-GCM", length: 256 },
    false,
    ["encrypt"]
  );

  const cipher = await crypto.subtle.encrypt(
    { name: "AES-GCM", iv: iv },
    key,
    new TextEncoder().encode(plaintext)
  );

  return btoa(JSON.stringify({
    cipher: bufferToString(cipher),
    iv: bufferToString(iv),
    salt: bufferToString(salt)
  }));
}

function bufferToString(buffer) {
  let binary = "";
  const bytes = new Uint8Array(buffer);

  for (let i = 0; i < bytes.byteLength; i++) {
    binary += String.fromCharCode(bytes[i]);
  }

  return binary;
}

Ansicht Geheimnis

API-Abfrage: GET https://password.link/api/secrets/<id>

Erforderlicher API-Schlüsseltyp: öffentlicher API-Schlüssel

Ein Beispiel für das Abrufen, Entschlüsseln und Anzeigen eines Geheimnisses mit JavaScript. Verwendet jQuery und SJCL.

Ein Skript wie dieses kann verwendet werden, um eine selbst gehostete Geheimhaltungsseite zu erstellen.

<html>
  <head>
    <!-- Get the latest jQuery from https://jquery.com -->
    <script src="jquery-3.4.1.min.js"></script>

    <!-- Get sjcl.js from https://github.com/bitwiseshiftleft/sjcl -->
    <script src="sjcl.js"></script>

    <style>
      body {
        font: 12px Arial;
      }
      .container {
        max-width: 960px;
        margin: 0 auto;
        text-align: center;
        margin-top: 60px;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <h2>Here's the secret</h2>

      <!-- This div will contain the decrypted secret (or error) -->
      <div id="secret"></div>
    </div>

    <script>
      (function() {
        // Set the API key here - a public API key is required
        var PUBLIC_API_KEY = "public_key_abcd...";

        // ----------------------------------- //

        // Get the secret ID from the query string part of the URL
        // E.g. https://some.site/secret.html?secret_id
        var secret_id = location.search.substr(1);

        // Get the public password (encryption key) part from the hash part of the URL, in Base64 format
        // E.g. https://some.site/secret.html?secret_id#password_part_public
        var password_part_public = location.hash.substr(1);

        // Send a request to the password.link API and process the result
        $.ajax({
          type: "GET",
          url: "https://password.link/api/secrets/" + secret_id,
          dataType: "json",
          headers: {
            "Authorization": "ApiKey " + PUBLIC_API_KEY
          },
          success: function(data) {
            var secret = data.data;
            decrypt_secret(password_part_public, secret.password_part_private, secret.ciphertext);
          },
          error: function(data) {
            var error = data.responseJSON.error;
            $("#secret").html("Error: " + error.message);
          }
        });

        // A function for decrypting the secret received from the password.link API
        function decrypt_secret(password_part_public, password_part_private, ciphertext) {
          try {
            // Decrypt the secret using SJCL
            // All parameters are in Base64 format
            var decrypted_secret = sjcl.decrypt(atob(password_part_private) + atob(password_part_public), atob(ciphertext));

            // Set the content of the element with id "secret" to the decrypted secret
            // Use .textContent to avoid XSS
            document.getElementById("secret").textContent = decrypted_secret;
          }
          // Catch and show errors
          catch(e) {
            document.getElementById("secret").textContent = "Error during decryption: " + e;
          }
        }
      })();
    </script>
  </body>
</html>