Docs - API-Beispiele
Vollständige, funktionierende Beispiele für die häufigsten API-Aufgaben. Alle Beispiele verwenden reines JavaScript und die integrierte Web Crypto API, es werden also keine externen Bibliotheken benötigt. Die vollständige Endpunkt-Referenz finden Sie in der Dokumention - API.
Geheimnis erstellen
Ein Beispiel für das Verschlüsseln eines Geheimnisses und das Senden an die password.link-API mit JavaScript. Speichern Sie es als HTML-Datei, setzen Sie den API-Schlüssel und öffnen Sie es in einem Browser.
Ein Skript wie dieses kann auf einem lokalen Computer oder in einem lokalen Netzwerk verwendet werden, um bequem Geheimnisse in unserem Dienst zu erstellen. Legen Sie es jedoch niemals ohne angemessene Authentifizierung an einem öffentlichen Ort ab, da es Ihren privaten API-Schlüssel enthält.
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<script>
// Set the base URL for the generated link
const LINK_BASE_URL = "https://some.site/secret.html";
// Set the API key here - a private API key is required
const PRIVATE_API_KEY = "private_key_abcd...";
// ----------------------------------- //
// A function for sending the secret to password.link API
async function sendToPasswordlinkApi(secret) {
// Create the public and private password parts
const passwordPartPublic = generateString(18);
const passwordPartPrivate = generateString(18);
// Create a Web Crypto compatible Base64 encoded ciphertext
const ciphertext = await encryptSecret(passwordPartPublic, passwordPartPrivate, secret);
// Send a request to the password.link API and process the result
const response = await fetch("https://password.link/api/secrets", {
method: "POST",
headers: {
"Authorization": "ApiKey " + PRIVATE_API_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify({
"ciphertext": ciphertext,
"password_part_private": btoa(passwordPartPrivate)
})
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error.message);
}
const secretUrl = LINK_BASE_URL + "?" + data.data.id + "#" + btoa(passwordPartPublic);
const meta = data.metadata;
document.getElementById("secret").textContent = "URL to secret: " + secretUrl;
document.getElementById("secret-meta").textContent =
"Total secrets: " + meta.secrets_total +
" | Usage: " + meta.secrets_usage +
" | Allowance: " + meta.secrets_allowance;
}
// A function for encrypting a secret with the Web Crypto API (AES-GCM),
// returns a Base64 encoded ciphertext JSON string
async function encryptSecret(passwordPartPublic, passwordPartPrivate, secret) {
const iterations = 10000;
const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
// The encryption key is derived from the concatenated password parts
const passwordKey = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(passwordPartPrivate + passwordPartPublic),
"PBKDF2",
false,
["deriveKey"]
);
const key = await crypto.subtle.deriveKey(
{ name: "PBKDF2", salt: salt, iterations: iterations, 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(secret)
);
return btoa(JSON.stringify({
"cipher": bufferToBase64(cipher),
"iv": bufferToBase64(iv),
"salt": bufferToBase64(salt),
"iter": iterations
}));
}
// Base64 encode the bytes of an ArrayBuffer
function bufferToBase64(buffer) {
const bytes = new Uint8Array(buffer);
let binary = "";
for (let i = 0; i < bytes.length; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
// A function for generating a random string. Rejection sampling keeps
// the character distribution unbiased.
function generateString(length) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$|/!_+,.-?()[]{}<>&#^*=@";
const maxAcceptable = Math.floor(256 / chars.length) * chars.length;
let str = "";
while (str.length < length) {
const randomValues = new Uint8Array(length - str.length);
crypto.getRandomValues(randomValues);
for (let i = 0; i < randomValues.length && str.length < length; i++) {
if (randomValues[i] < maxAcceptable) {
str += chars.charAt(randomValues[i] % chars.length);
}
}
}
return str;
}
// Execute the send function when the button is clicked
document.addEventListener("DOMContentLoaded", function() {
document.getElementById("secret-button").addEventListener("click", function(e) {
e.preventDefault();
const secret = document.getElementById("secret-input").value;
sendToPasswordlinkApi(secret).catch(function(error) {
document.getElementById("secret").textContent = "Error: " + error.message;
});
});
});
</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>
Geheimnis mit Anhang erstellen
Dieses Beispiel zeigt die anhangsspezifischen Teile. Es setzt voraus, dass Sie den Geheimnis-Chiffretext und die Passwortteile bereits wie im obigen Beispiel Geheimnis erstellen erstellt haben.
// 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
Ein Beispiel für das Abrufen, Entschlüsseln und Anzeigen eines Geheimnisses mit JavaScript. Ein Skript wie dieses kann verwendet werden, um eine selbst gehostete Seite zum Anzeigen von Geheimnissen zu erstellen.
Der Ciphertext kann Web Crypto oder älteres SJCL sein. Das folgende Beispiel verarbeitet beide Varianten: SJCL-Payloads haben einen ct-Schlüssel. Für die SJCL-Entschlüsselung binden Sie sjcl.js in die Seite ein.
<html>
<head>
<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
const 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
const secretId = 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
const passwordPartPublic = location.hash.substr(1);
// Fetch the secret from the password.link API and decrypt it
async function fetchAndDecryptSecret() {
const response = await fetch("https://password.link/api/secrets/" + secretId, {
headers: {
"Authorization": "ApiKey " + PUBLIC_API_KEY
}
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error.message);
}
const secret = data.data;
const decryptedSecret = await decryptSecret(
passwordPartPublic,
secret.password_part_private,
secret.ciphertext
);
// Set the content of the element with id "secret" to the decrypted secret
// Use .textContent to avoid XSS
document.getElementById("secret").textContent = decryptedSecret;
}
// Decrypts a ciphertext received from the password.link API.
// All parameters are in Base64 format.
//
// Legacy SJCL payloads contain a "ct" key and need sjcl.js:
// https://github.com/bitwiseshiftleft/sjcl
async function decryptSecret(passwordPartPublic, passwordPartPrivate, ciphertext) {
const payload = JSON.parse(atob(ciphertext));
const password = atob(passwordPartPrivate) + atob(passwordPartPublic);
if (Object.prototype.hasOwnProperty.call(payload, "ct")) {
return sjcl.decrypt(password, atob(ciphertext));
}
const passwordKey = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(password),
"PBKDF2",
false,
["deriveKey"]
);
const key = await crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt: base64ToBytes(payload.salt),
iterations: payload.iter || 10000,
hash: "SHA-256"
},
passwordKey,
{ name: "AES-GCM", length: 256 },
false,
["decrypt"]
);
const plaintext = await crypto.subtle.decrypt(
{ name: "AES-GCM", iv: base64ToBytes(payload.iv) },
key,
base64ToBytes(payload.cipher)
);
return new TextDecoder().decode(plaintext);
}
// Decode a Base64 string into a byte array
function base64ToBytes(base64) {
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes;
}
fetchAndDecryptSecret().catch(function(error) {
document.getElementById("secret").textContent = "Error: " + error.message;
});
})();
</script>
</body>
</html>