Private Key Operations

Core API allows you to reveal private keys for wallet export, backup, or migration purposes. Private keys are encrypted using RSA encryption to ensure secure transmission and can only be decrypted by the holder of the corresponding RSA private key.

Security Considerations

Private key revelation should only be used for legitimate purposes such as wallet export, backup, or migration. Once revealed, the private key provides full access to the wallet and should be handled with extreme care.
The private key is encrypted using RSA-OAEP encryption before transmission, ensuring that only the holder of the RSA private key can decrypt it.

Implementation Approaches

Using Magic’s iframe

The simplest approach uses Magic’s built-in iframe for secure key handling:
JavaScript
import { Magic } from 'magic-api-wallet';

const magic = new Magic()

// fetch PEM Base64 encoded RSA public key from iframe
const rsaPublicKey = await magic.wallet.getRSAPublicKey();

// send rsa_public_key to your backend service to be relayed to TEE API
const encryptedPrivateKey = await HttpService.BackendService.Post(
  Endpoint.BackendService.RevealPrivateKey,
  { authorization: `Bearer ${access_token}` },
  { rsa_public_key: rsaPublicKey },
);

// unwrap and render private key in secure iframe
await magic.wallet.unwrapPrivateKey(encryptedPrivateKey.privateKey);

Custom RSA Implementation

For applications requiring custom RSA key handling, you can implement the RSA encryption process manually:
JavaScript
// Step 1: Create RSA encryption keypair using the Web Crypto API
const keyPair = await window.crypto.subtle.generateKey(
  {
    name: 'RSA-OAEP',
    modulusLength: 4096,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: 'SHA-1',
  },
  false, // non-exportable from the browser (important)
  ['encrypt', 'decrypt'],
);

// Step 2: Export the raw public key
const exported = await window.crypto.subtle.exportKey('spki', keyPair.publicKey);

// Step 3: Convert the ArrayBuffer to a Base64 string
const exportedAsString = String.fromCharCode(...new Uint8Array(exported));
const exportedAsBase64 = window.btoa(exportedAsString);

// Step 4: Split the Base64 string into manageable chunks
const maxLineLength = 64;
let formattedBase64 = '';
for (let i = 0; i < exportedAsBase64.length; i += maxLineLength) {
  formattedBase64 += exportedAsBase64.slice(i, i + maxLineLength) + '\n';
}

// Step 5: Create the PEM header and footer
const rsa_public_key = `-----BEGIN PUBLIC KEY-----\n${formattedBase64}-----END PUBLIC KEY-----\n`;

// Step 6: Send rsa_public_key to backend to call /v1/api/wallet/reveal_pk
// Step 7: Use the RSA private key to decrypt the encrypted_private_key
const encryptedBuffer = base64ToUint8(encryptedKey);
const decryptedBuffer = await window.crypto.subtle.decrypt(
  { name: 'RSA-OAEP' },
  keypair.privateKey,
  encryptedBuffer,
);
const eoa_pk = new TextDecoder().decode(decryptedBuffer);

API Reference

Reveal Private Key

Reveal a wallet’s private key encrypted with the provided RSA public key.
cURL
curl -X POST 'https://tee.magiclabs.com/v1/api/wallet/reveal_pk' \
  -H 'Content-Type: application/json' \
  -H 'x-magic-secret-key: sk_live_XXXXXXXX' \
  -d '{
    "encryption_context": "hashed_passcode",
    "access_key": "key_shard",
    "wallet_id": "wallet_id",
    "rsa_public_key": "rsa_public_key"
  }'
Response:
{
  "data": {
    "encrypted_private_key": "encrypted_key_data"
  }
}

Request Parameters

rsa_public_key
string
required
Body
PEM Base64 encoded RSA public key
encryption_context
string
required
Body
The encryption context used to decrypt key shards. Must match the context used during wallet creation.
access_key
string
required
Body
The access key returned from wallet creation. Retrieve from your database.
wallet_id
string
required
Body
The wallet UUID returned from wallet creation.

Response Fields

data.encrypted_private_key
string
Private key encrypted by RSA public key sent to the endpoint and can only be decrypted by owner of RSA private key