安全な署名

トランザクションをXRP Ledgerに送信するには、秘密鍵のセキュリティを損なわない方法でトランザクションにデジタル署名する必要があります。(他の人があなたの秘密鍵にアクセスできる場合、その人はあなたと同じようにあなたのアカウントを操作できるため、すべての資金が盗まれたり消却されたりする可能性があります。)このページでは、トランザクションに安全に署名できる環境の設定方法について説明します。

ヒント: ネットワークにトランザクションを送信していない場合は、Rippleが運用しているサーバーなど、信頼できる公開サーバーを安全に使用して、着信トランザクションの監視やその他のネットワークアクティビティの読み取りを行うことができます。XRP Ledgerのすべてのトランザクション、残高、データは公開されています。

セキュリティのレベルが異なるさまざまな構成があるため、状況に応じて適したものは異なります。次の中からニーズに最適なものを選択してください。

安全でない構成

外部のソースからあなたの秘密鍵にアクセスできる構成は危険で、不正使用者によってあなたのすべてのXRP(およびあなたのXRP Ledgerのアドレスにあるすべてのもの)が盗まれる可能性があります。そのような構成の例としては、インターネット経由で他の人のrippledサーバーのsignメソッドを使用する構成や、秘密鍵をインターネットを経由してプレーンテキストで自己所有サーバーに送信する構成などがあります。

秘密鍵の秘匿性は常に保持する必要があります。自分にメールで送信したり、人の目に触れるところで入力したりしてはいけません。秘密鍵を使用しないときは、決してプレーンテキストではなく、暗号化された形式で保存する必要があります。セキュリティと利便性のバランスは、アドレスの保有額によっても変わります。さまざまな目的に合わせてさまざまなセキュリティ構成の複数のアドレスを使用することをお勧めします。

ローカルでrippledを実行する

この構成では、トランザクションを生成するマシンでrippledを実行します。 秘密鍵はマシンから出ていかないため、マシンへのアクセス権がない人は秘密鍵にアクセスできません。もちろん、マシンのセキュリティ保護に関する業界標準のプラクティスに従ってください。この構成を使用するには、次の手順を実行します。

  1. rippledをインストールします。

    ローカルマシンがrippledの最小システム要件を満たしていることを確認します。

  2. トランザクションに署名する必要がある場合は、localhostまたは127.0.0.1のサーバーに接続します。シングル署名の場合はsignメソッド、マルチシグの場合はsign_forメソッドを使用します。

    構成ファイルの例 では、ローカルループバックネットワーク上(127.0.0.1)のポート5005でJSON-RPC(HTTP)、ポート6006でWebSocket(WS)の接続をリッスンし、接続されるすべてのクライアントを管理者として扱っています。

    注意: 署名にコマンドラインAPIを使用する場合は、コマンドラインでないクライアントでWebsocket APIやJSON-RPC APIを使用する場合よりもセキュリティが弱くなります。コマンドライン構文を使用すると、秘密鍵がシステムのプロセスリストで他のユーザーに見える可能性があり、シェル履歴にプレーンテキスト形式でキーが保存される可能性があります。

  3. サーバーの使用中は、稼働状態と最新状態を維持して、ネットワークと同期されるようにしておく必要があります。

    注記: トランザクションを送信していないときはrippledサーバーをオフにすることが 可能 ですが、再び起動したときにネットワークとの同期に最大15分かかります。

同じLAN内でrippledを実行する

この構成では、署名するトランザクションを生成するマシンと同じプライベートローカルエリアネットワーク(LAN)内の専用マシンでrippledサーバーを実行します。この構成では、rippledを実行する専用の1台のマシンを使用しながら、中程度のシステムスペックの1台以上のマシンでトランザクションの指示を組み立てることができます。自己所有のデータセンターやサーバールームがある場合に魅力的な選択肢です。

この構成を使用するには、rippledサーバーをLAN内のwssおよびhttps接続を受け入れるように設定します。証明書ピンニング を使用する場合は自己署名証明書を使用できます。あるいは、社内や既知の認証局が署名した証明書を使用できます。Let's Encrypt などの一部の認証局は無料で証明書を自動発行しています。

必ず、マシンのセキュリティ保護に関する業界標準のプラクティスに従ってください。例えば、ファイアウォール、ウイルス対策、適切なユーザー権限を使用するなどです。

ローカル署名機能のあるクライアントライブラリを使用する

この構成では、トランザクションにローカルで署名するために使用しているプログラミング言語のクライアントライブラリを使用します。使用しているプログラミング言語に対応するクライアントライブラリが必要です。Rippleは、XRP Ledgerのトランザクションにローカルで署名することができる次のクライアントライブラリを公開しています。

Rippleが公開したものでないクライアントライブラリを使用する場合は、そのライブラリが実装している署名アルゴリズムの実装が適切で安全であることを確認してください。(例えば、クライアントライブラリがデフォルトのECDSAアルゴリズムを使用している場合は、そのライブラリはRFC6979 に記載されているとおりに決定論的ノンスを使用している必要があります。)Rippleが公開している上記のすべてのライブラリは、業界のベストプラクティスに従っています。

最高レベルのセキュリティを実現するために、クライアントライブラリを安定した最新バージョンの状態に保ってください。

クライアントライブラリを使用したローカル署名の例

// Sample code demonstrating secure offline signing using xrpl.js library.
const xrpl = require('xrpl')

// Load seed value from an environment variable:
const my_wallet = xrpl.Wallet.fromSeed(process.env['MY_SEED'])

// For offline signing, you need to know your address's next Sequence number.
// Alternatively, you could use a Ticket in place of the Sequence number.
// This is useful when you need multiple signatures and may want to process transactions out-of-order.
// For details, see: https://xrpl.org/tickets.html
let my_seq = 21404872

// Provide *all* required fields before signing a transaction
const txJSON = {
  "Account": my_wallet.address,
  "TransactionType":"Payment",
  "Destination":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
  "Amount":"13000000",
  "Flags":2147483648,
  "LastLedgerSequence":7835923, // Optional, but recommended.
  "Fee":"13",
  "Sequence": my_seq
}

const signed = my_wallet.sign(txJSON)

console.log("tx_blob is:", signed.tx_blob)
console.log("tx hash is:", signed.hash)
# Define signer address
import os  
my_secret = os.getenv("MYSECRET")
from xrpl.wallet import Wallet
wallet = Wallet.from_seed(seed=my_secret)
print(wallet.address)  # "raaFKKmgf6CRZttTVABeTcsqzRQ51bNR6Q"

# For offline signing, you need to know your address's next Sequence number.
# Alternatively, you could use a Ticket in place of the Sequence number.
# This is useful when you need multiple signatures and may want to process transactions out-of-order.
# For details, see: https://xrpl.org/tickets.html
sequence = 0

from xrpl.models.transactions import Payment
from xrpl.utils import xrp_to_drops
my_payment = Payment(
    account=wallet.address,
    amount=xrp_to_drops(22),
    fee="10",
    destination="rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
    sequence=sequence,
)
print("Payment object:", my_payment)

# Sign transaction -------------------------------------------------------------
import xrpl.transaction 
signed = xrpl.transaction.sign(my_payment, wallet)
print("Signed transaction blob:", signed)
////////////////////////////////////////////////////////////////////////////
// Sign using a SingleKeySignatureService:
// This implementation of SignatureService signs Transactions using a
// supplied PrivateKey. This may be suitable for some applications, but is
// likely not secure enough for server side applications, as keys should not
// be stored in memory whenever possible.
////////////////////////////////////////////////////////////////////////////

// Create a random wallet
KeyPair randomKeyPair = Seed.ed25519Seed().deriveKeyPair();
PublicKey publicKey = randomKeyPair.publicKey();
PrivateKey privateKey = randomKeyPair.privateKey()

// Construct a SignatureService
SignatureService<PrivateKey> signatureService = new BcSignatureService();

// Construct and sign the Payment
Payment payment = Payment.builder()
  .account(publicKey.deriveAddress())
  .destination(Address.of("rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe"))
  .amount(XrpCurrencyAmount.ofDrops(1000))
  .fee(XrpCurrencyAmount.ofDrops(10))
  .sequence(UnsignedInteger.valueOf(16126889))
  .signingPublicKey(publicKey)
  .build();
SingleSignedTransaction<Payment> signedPayment = signatureService.sign(privateKey, payment);
System.out.println("Signed Payment: " + signedPayment.signedTransaction());


////////////////////////////////////////////////////////////////////////////
// Sign using a DerivedKeysSignatureService:
// This implementation of SignatureService deterministically derives
// Private Keys from a secret value (likely a server secret) and a wallet
// identifier. That PrivateKey can then be used to sign transactions.
// The wallet identifier can be anything, but would likely be an existing ID
// tracked by a server side system.
//
// Though this implementation is more secure than SingleKeySignatureService
// and better suited for server-side applications, keys are still held
// in memory. For the best security, we suggest using a HSM-based
// implementation.
////////////////////////////////////////////////////////////////////////////

// Construct a DerivedKeysSignatureService with a server secret (in this case "shh")
SignatureService<PrivateKeyReference> derivedKeySignatureService = new BcDerivedKeySignatureService(
  () -> ServerSecret.of("shh".getBytes())
);

PrivateKeyReference privateKeyReference = new PrivateKeyReference() {
  @Override
  public KeyType keyType() {
    return KeyType.ED25519;
  }

  @Override
  public String keyIdentifier() {
    return "sample-keypair";
  }
};

// Get the public key and classic address for the given walletId
PublicKey publicKey = derivedKeySignatureService.derivePublicKey(privateKeyReference);
Address classicAddress = publicKey.deriveAddress();

// Construct and sign the Payment
Payment payment = Payment.builder()
  .account(classicAddress)
  .destination(Address.of("rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe"))
  .amount(XrpCurrencyAmount.ofDrops(1000))
  .fee(XrpCurrencyAmount.ofDrops(10))
  .sequence(UnsignedInteger.valueOf(16126889))
  .signingPublicKey(publicKey)
  .build();

SingleSignedTransaction<Payment> signedPayment = derivedKeySignatureService.sign(privateKeyReference, payment);
System.out.println("Signed Payment: " + signedPayment.signedTransaction());

セキュリティを強化するために、Vault などの管理ツールから秘密鍵を読み込みます。

専用の署名デバイスを使用する

専用の署名デバイスが各社から販売されており、例えばLedger Nano S は、秘密鍵をデバイスから出さずに使ってXRP Ledgerトランザクションに署名できます。すべてのタイプのトランザクションに対応していないデバイスもあります。

この構成の設定は、特定のデバイスによって異なります。場合によっては、署名デバイスと通信するためにマシンで「マネージャー」アプリケーションを実行する必要があります。そのようなデバイスの設定と使用方法については、メーカーの手順を参照してください。

リモートrippledサーバーに対して安全なVPNを使用する

この構成では、コロケーション施設や遠隔地のデータセンターなどにあるリモートでホストされているrippledサーバーを使用し、暗号化されたVPNを使用してそのサーバーに接続します。

この構成を使用するには、プライベートLANでrippledを実行するための手順に従いますが、VPNを使用してリモートrippledサーバーのLANに接続します。VPNの設定手順は環境によって異なり、このガイドでは説明しません。

関連項目