Grunnatriði
Auðkenning
Allar beiðnir nema GET /v1/status eru undirritaðar með Ed25519 einkalykli lykilsins. Myntkaup geymir aðeins dreifilykilinn, svo ekkert leyndarmál fylgir nokkurn tíma beiðni.
Hvernig það virkar
Þegar þú býrð til lykil býr vafrinn þinn til Ed25519 lyklapar og sendir Myntkaup aðeins dreifilykilinn. Til að kalla á API-ið undirritar forritið þitt stutta lýsingu á hverri beiðni (staðlaða strenginn) með einkalyklinum og sendir undirskriftina í haus. Myntkaup sannreynir hana með dreifilyklinum.
Undirskrift gildir fyrir eina beiðni: hún nær yfir aðferðina, slóðina, fyrirspurnina og meginmálið, tímastimpil sem gildir í 30 sekúndur og slembið nonce sem ekki má nota tvisvar. Undirskrift sem lekur úr annál er ekki hægt að endurspila eða nota í annað, og leki úr lyklageymslunni okkar kæmi engum að gagni til að kalla á API-ið.
SDK-in sjá um þetta allt. Lestu áfram ef þú ætlar að undirrita beiðnir án þeirra.
Hausar
Hver undirrituð beiðni ber fjóra hausa. Beiðnir með meginmáli þurfa líka Content-Type: application/json.
| Haus | Gildi |
|---|---|
MK-Key-Id | Auðkenni lykilsins: mk_live_ (raunumhverfi) eða mk_test_ (prófunarumhverfi), og svo 20 stafir a-z0-9. |
MK-Timestamp | Millisekúndur frá upphafi Unix tímatals, aðeins tölustafir, innan 30 sekúndna frá klukku netþjónsins. |
MK-Nonce | 32 hex stafir í lágstöfum (16 slembin bæti), sem þessi lykill hefur aldrei notað áður. |
MK-Signature | Venjulegt base64 (með fyllingu, 88 stafir) af 64 bæta Ed25519 undirskrift staðlaða strengsins. |
Staðlaði strengurinn
Sjö línur, aðskildar með einu línuskilatákni (\n), án línuskila í lokin:
myntkaup-public-v1
<MK-Key-Id>
<MK-Timestamp>
<MK-Nonce>
<METHOD>
<path and query>
<SHA-256 of the body, lowercase hex>- METHOD er aðferðin í hástöfum:
GETeðaPOST. - Slóð og fyrirspurn (path and query) er nákvæmlega það sem er í beiðnislínunni, án hýsils:
/v1/transactions?limit=10&asset=BTC. Hafðu færibreyturnar í sömu röð og þú sendir þær, og undirritaðu það sem HTTP forritasafnið þitt sendir í raun ef það kóðar fyrirspurnina upp á nýtt. - Tætigildi meginmálsins er reiknað af nákvæmlega þeim bætum sem þú sendir. Búðu JSON strenginn til einu sinni, undirritaðu hann og sendu sama strenginn. Beiðni án meginmáls notar tætigildi tóma strengsins:
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855. - Undirskriftin er reiknuð af staðlaða strengnum, kóðuðum sem UTF-8.
Lyklar
- Auðkenni lykils segir hvaða lykill þetta er. Það er ekki leyndarmál.
- Einkalykillinn er
mk_sk_og á eftir base64url (án fyllingar) af 32 bæta Ed25519 fræi. Forritasöfn sem lesa PKCS #8 lykla þurfa fræið á eftir forskeytinu302e020100300506032b657004220420(hex), eins og dæmin hér að neðan gera. mk_live_lyklar virka aðeins áhttps://public-api.myntkaup.is, ogmk_test_lyklar aðeins í prófunarumhverfinu. Lykill sem sendur er í rangt umhverfi fær401 invalid_signature.
Tímastimplar og nonce
MK-Timestampverður að vera innan 30 sekúndna frá klukku netþjónsins, annars fær beiðnin401 timestamp_out_of_range. Haltu klukkunni samstilltri (NTP).GET /v1/statusskilar klukku netþjónsins íserver_timeán undirskriftar.MK-Nonceeru 16 bæti úr dulritunarlega öruggum slembigjafa, sem hex. Sé það notað aftur með sama lykli fæst401 nonce_reused. Nonce er slembið frekar en hækkandi, svo þú getur sent beiðnir samhliða.- Til að reyna beiðni aftur skaltu undirrita hana aftur: nýr tímastimpill og nýtt nonce. Það sem gerir endurtekna
POSTbeiðni örugga er Idempotency-Key hennar, ekki undirskriftin.
Dæmi skref fyrir skref
Þessi gildi eru POST /v1/quotes tilvikið úr sameiginlegu prófunargögnunum sem API-ið og bæði SDK-in eru prófuð með. Ed25519 undirskriftir eru ákveðnar (deterministic), svo kóðinn þinn verður að fá nákvæmlega þessa undirskrift út úr þessum gildum. Öll fjögur tilvikin eru í test-vectors.json.
- Einkalykill
mk_sk_nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2APrófunarlykill 1 úr RFC 8032. Opinber; notaðu hann aldrei í alvöru.- Auðkenni lykils
mk_test_0123456789abcdefghij- Tímastimpill
1767225600123- Nonce
00112233445566778899aabbccddeeff- Beiðni
POST /v1/quotes- Meginmál
{"asset":"BTC","side":"buy","quote_amount":"100.00"}- SHA-256 af meginmálinu
82c3b1d587da21ee0fd5d8e59279cdea07a26416867024da753a066318f74e5b
myntkaup-public-v1
mk_test_0123456789abcdefghij
1767225600123
00112233445566778899aabbccddeeff
POST
/v1/quotes
82c3b1d587da21ee0fd5d8e59279cdea07a26416867024da753a066318f74e5bPOST /v1/quotes HTTP/1.1
Host: public-api.myntkaup.is
Content-Type: application/json
MK-Key-Id: mk_test_0123456789abcdefghij
MK-Timestamp: 1767225600123
MK-Nonce: 00112233445566778899aabbccddeeff
MK-Signature: g0UAy9BdJ+AxfeYqawRhlNpdrgx1GSoWMZUotDl4efiOFsA7UAkumblZ8TCJ3qF5VTEnqJDThZsZGtjCegfKDQ==
{"asset":"BTC","side":"buy","quote_amount":"100.00"}Kóði til undirritunar
Node.js
Notar aðeins node:crypto. Sendu inn nákvæmlega þann meginmálsstreng sem þú sendir.
// Signs a Myntkaup API request with nothing but node:crypto (Node 18 or later).
// test/samples.test.ts checks it against packages/signing/test-vectors.json.
import { createHash, createPrivateKey, randomBytes, sign } from "node:crypto";
// Turns the 32-byte seed in mk_sk_… into a PKCS #8 key Node can load.
const PKCS8_ED25519_PREFIX = Buffer.from("302e020100300506032b657004220420", "hex");
/**
* The four MK-* headers for one request. `body` must be the exact string or
* bytes you send; leave it empty for a GET.
*/
export function signHeaders({
keyId,
privateKey,
method,
pathAndQuery,
body = "",
timestamp = Date.now(),
nonce = randomBytes(16).toString("hex"),
}) {
const seed = Buffer.from(privateKey.replace(/^mk_sk_/, ""), "base64url");
const key = createPrivateKey({
key: Buffer.concat([PKCS8_ED25519_PREFIX, seed]),
format: "der",
type: "pkcs8",
});
const canonical = [
"myntkaup-public-v1",
keyId,
String(timestamp),
nonce,
method.toUpperCase(),
pathAndQuery,
createHash("sha256").update(body).digest("hex"),
].join("\n");
return {
"MK-Key-Id": keyId,
"MK-Timestamp": String(timestamp),
"MK-Nonce": nonce,
"MK-Signature": sign(null, Buffer.from(canonical, "utf8"), key).toString("base64"),
};
}import { signHeaders } from "./sign-headers.mjs";
const body = JSON.stringify({ asset: "BTC", side: "buy", quote_amount: "100.00" });
const headers = signHeaders({
keyId: process.env.MYNTKAUP_API_KEY_ID,
privateKey: process.env.MYNTKAUP_API_PRIVATE_KEY,
method: "POST",
pathAndQuery: "/v1/quotes",
body,
});
const response = await fetch("https://public-api.myntkaup.is/v1/quotes", {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body,
});Python
Notar cryptography pakkann.
"""Signs a Myntkaup API request with the `cryptography` package (Python 3.9+).
test/samples.test.ts checks it against packages/signing/test-vectors.json.
"""
import base64
import hashlib
import secrets
import time
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
def sign_headers(key_id, private_key, method, path_and_query, body=b"", timestamp=None, nonce=None):
"""Returns the four MK-* headers. `body` must be the exact bytes you send."""
if isinstance(body, str):
body = body.encode("utf-8")
seed = base64.urlsafe_b64decode(private_key.removeprefix("mk_sk_") + "=")
key = Ed25519PrivateKey.from_private_bytes(seed)
timestamp = str(timestamp if timestamp is not None else int(time.time() * 1000))
nonce = nonce or secrets.token_hex(16)
canonical = "\n".join(
[
"myntkaup-public-v1",
key_id,
timestamp,
nonce,
method.upper(),
path_and_query,
hashlib.sha256(body).hexdigest(),
]
)
signature = base64.b64encode(key.sign(canonical.encode("utf-8"))).decode("ascii")
return {
"MK-Key-Id": key_id,
"MK-Timestamp": timestamp,
"MK-Nonce": nonce,
"MK-Signature": signature,
}import json
import os
import httpx
from sign_headers import sign_headers
body = json.dumps({"asset": "BTC", "side": "buy", "quote_amount": "100.00"}, separators=(",", ":"))
headers = sign_headers(
os.environ["MYNTKAUP_API_KEY_ID"],
os.environ["MYNTKAUP_API_PRIVATE_KEY"],
"POST",
"/v1/quotes",
body,
)
response = httpx.post(
"https://public-api.myntkaup.is/v1/quotes",
content=body, # the signed bytes; json= would serialize them again
headers={**headers, "Content-Type": "application/json"},
)Skel
sign-headers.sh prentar hausana fjóra með OpenSSL 3 og coreutils. Það les lykilinn úr MYNTKAUP_API_KEY_ID og MYNTKAUP_API_PRIVATE_KEY, sömu nöfnum og í .env skránni sem þú sækir þegar þú býrð til lykil. curl dæmin í API lýsingunni nota það.
chmod +x sign-headers.sh
set -a; . ./.env; set +a # MYNTKAUP_API_KEY_ID and MYNTKAUP_API_PRIVATE_KEY
curl https://public-api.myntkaup.is/v1/me $(./sign-headers.sh GET /v1/me | sed 's/^/-H /')
body='{"asset":"BTC","side":"buy","quote_amount":"100.00"}'
curl https://public-api.myntkaup.is/v1/quotes \
-H "Content-Type: application/json" \
$(./sign-headers.sh POST /v1/quotes "$body" | sed 's/^/-H /') \
--data "$body"macOS kemur með LibreSSL sem openssl. Settu upp OpenSSL 3 (brew install openssl@3) og láttu skriftuna nota það með OPENSSL="$(brew --prefix openssl@3)/bin/openssl".
#!/usr/bin/env bash
# Prints the four MK-* headers for a request, with nothing but OpenSSL 3 and
# coreutils: the shell version of packages/signing (contract section 1).
#
# The key is read from MYNTKAUP_API_KEY_ID and MYNTKAUP_API_PRIVATE_KEY, the
# names in the .env file the webapp gives you (MK_KEY_ID / MK_PRIVATE_KEY
# still work):
#
# export MYNTKAUP_API_KEY_ID=mk_test_… MYNTKAUP_API_PRIVATE_KEY=mk_sk_…
# scripts/sign-headers.sh GET /v1/me
# curl https://public-api.staging.myntkaup.is/v1/me $(scripts/sign-headers.sh GET /v1/me | sed 's/^/-H /') # values have no spaces
#
# Sign the exact body bytes you send. MK_TIMESTAMP and MK_NONCE may be set to
# reproduce the test vectors.
set -euo pipefail
OPENSSL="${OPENSSL:-openssl}"
method="$1" path="$2" body="${3:-}"
key_id="${MYNTKAUP_API_KEY_ID:-${MK_KEY_ID:?set MYNTKAUP_API_KEY_ID}}"
private_key="${MYNTKAUP_API_PRIVATE_KEY:-${MK_PRIVATE_KEY:?set MYNTKAUP_API_PRIVATE_KEY}}"
timestamp="${MK_TIMESTAMP:-$(( $(date +%s) * 1000 ))}"
nonce="${MK_NONCE:-$("$OPENSSL" rand -hex 16)}"
key="$(mktemp)"; msg="$(mktemp)"; trap 'rm -f "$key" "$msg"' EXIT
{ printf '302e020100300506032b657004220420' | xxd -r -p
printf '%s=' "${private_key#mk_sk_}" | tr '_-' '/+' | base64 -d; } > "$key"
body_hash="$(printf '%s' "$body" | "$OPENSSL" dgst -sha256 -r | cut -d' ' -f1)"
printf 'myntkaup-public-v1\n%s\n%s\n%s\n%s\n%s\n%s' "$key_id" "$timestamp" "$nonce" "$method" "$path" "$body_hash" > "$msg"
signature="$("$OPENSSL" pkeyutl -sign -inkey "$key" -keyform DER -rawin -in "$msg" | base64 | tr -d '\n')"
echo "MK-Key-Id:$key_id"
echo "MK-Timestamp:$timestamp"
echo "MK-Nonce:$nonce"
echo "MK-Signature:$signature"Þegar auðkenning mistekst
| Staða | Kóði | Algeng ástæða |
|---|---|---|
| 401 | invalid_signature | Rangt auðkenni lykils eða rangt umhverfi, meginmál sem er ekki það sama og var undirritað, eða línuskil í lok staðlaða strengsins. |
| 401 | timestamp_out_of_range | Klukkan skeikar um meira en 30 sekúndur, eða undirrituð beiðni var send of seint. |
| 401 | nonce_reused | Beiðni var send aftur án þess að vera undirrituð upp á nýtt. |
| 401 | key_expired | Gildistími lykilsins er liðinn. |
| 401 | key_revoked | Lykillinn var afturkallaður í vefappinu. |
| 403 | ip_not_allowed | Lykillinn hefur leyfðar IP tölur og beiðnin kom frá annarri tölu. |
| 403 | insufficient_scope | Lykilinn vantar heimildina sem endapunkturinn þarf. |
Hver 401 og hver ip_not_allowed villa telst gegn IP tölunni sem kallar: 20 villur á 5 mínútum loka á hana í 15 mínútur, með 429 rate_limited. Lagaðu undirritunarkóðann áður en þú reynir aftur í lykkju.
Að fara vel með lykla
- Veittu sem minnstar heimildir. Lyklar geta sjálfgefið aðeins lesið. Veittu
tradeoggiftcards:createaðeins forritum sem þurfa á þeim að halda, og notaðu sérstakan lykil fyrir hvert forrit. - Takmarkaðu lykilinn við leyfðar IP tölur. Það er valfrjálst fyrir alla lykla, og mælt með því fyrir alla lykla sem geta átt viðskipti eða búið til gjafabréf: þá er ekki hægt að nota lykil sem lekur annars staðar frá.
- Geymdu einkalykla sem leyndarmál: í leyndarmálageymslu (secret manager) eða í
.envskrá utan útgáfustýringar. Settu þá aldrei í vafra, snjallsímaapp eða sameiginlegt spjall, og límdu þá aldrei inn í gervigreindartól. - Skiptu lyklum út reglulega. Lykill getur gilt í 30 daga, 90 daga, ár eða ótímabundið; við sendum tölvupóst 7 dögum áður en hann rennur út. Skiptu líka út ótímabundnum lyklum: búðu til nýja lykilinn, settu hann í notkun og afturkallaðu svo þann gamla.
- Afturkallaðu strax ef lykill gæti hafa lekið: Stillingar → API → Afturkalla, eða Afturkalla alla. Það þarf ekki rafræn skilríki til að afturkalla, og það tekur gildi strax.
- Fylgstu með lyklunum. Listinn í Stillingum sýnir hvenær og hvaðan hver lykill var síðast notaður, og eigandi aðgangsins fær tölvupóst um hvern nýjan, afturkallaðan eða stöðvaðan lykil.