How do I locally sign a transaction with a private key?

When you are making a transaction using the Node API, you will need to sign the transaction locally using your private key:

Next, you need to sign the hashOfBlobToSign using the private key of your account (ie. the “from” account). The details of doing so are outside the scope of this doc, but Radix follows a similar signature method as that of Bitcoin, using ECDSA signatures using EC curve “secp256k1”.

The result should be a DER encoded signature in hex that you will use in the following step.

The following python script will perform the signing operation and produce the correct hex formatted DER encoded signature that you can use to complete the transaction process:

import ecdsa
import hashlib
from ecdsa.curves import SECP256k1
from ecdsa.util import sigencode_der

private_key_hex = "YOUR_PRIVATE_KEY_AS_A_HEX_STRING"
hash_to_sign = "HASH_OF_BLOB_TO_SIGN"

# Create Private Key object from Private Key Hex String
signing_key = ecdsa.SigningKey.from_string(bytearray.fromhex(private_key_hex), curve=SECP256k1, hashfunc=hashlib.sha256)

# Use the Private Key to Sign the Transaction Hash
signed_hash = signing_key.sign_digest(bytearray.fromhex(hash_to_sign), sigencode=sigencode_der)

print("Signed Hash (DER Format): ", signed_hash.hex())

Note: You will need to change the YOUR_PRIVATE_KEY_AS_A_HEX_STRING and HASH_OF_BLOB_TO_SIGN variables

6 Likes

hey, really really stupid question maybe, but: how do I get my private key hex? I only have my node-config file with password protection how can i read the private key from that?.. I’m sorry if that question is stupid but i am a beginner. brgds and thx

Hi, if you have your node’s keystore.ks file then you can extract the private key using part of @Stuart’s unregister script and hex encode it as follows:

import bech32
import ecdsa
import hashlib
from cryptography.hazmat.primitives.serialization import pkcs12
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
from cryptography.hazmat.backends import default_backend
from ecdsa.curves import SECP256k1
from ecdsa.util import sigencode_der
import requests
import json

password = b"<KEYSTORE PASSWORD>"

# Read the Radix Keystore File (which is in PKCS12 format)
with open("node-keystore.ks", "rb") as f:
private_key, certificate, additional_certificates = pkcs12.load_key_and_certificates(f.read(), password, default_backend())

# Extract the unencrypted Private Key bytes
private_key_bytes = private_key.private_bytes(Encoding.DER, PrivateFormat.PKCS8, NoEncryption())

# Convert into Elliptic Curve Digital Signature Algorithm (ecdsa) private key object
private_key = ecdsa.SigningKey.from_der(private_key_bytes, hashfunc=hashlib.sha256)
private_key_string = private_key.to_string()
print("Private Key (Hex): ", private_key_string.hex())
5 Likes

REALLY dumb question:
How do I get the private key from a Desktop Wallet-created address?

I’m trying to test out doing txns via APIs and that step has me stumped.

Per this: (Step 2)
https://docs.radixdlt.com/main/apis/making-transactions.html

Next, you need to sign the payload_to_sign using the private key of your account (ie. the “from” account). The details of doing so are outside the scope of this doc, but Radix follows a similar signature method as that of Bitcoin, using ECDSA signatures using EC curve “secp256k1”.
The result should be a DER encoded signature in hex that you will use in the following step.

I (obviously) know the 24 words, but how does that translate into the longstring needed?

No idea how to get that value.

2 Likes

Hi Peachy, I had the exact same problem and couldn’t figure it out so just created a development wallet using Stuart’s script here: Development Wallet Creation

This will allow you to create the necessary hex encoded keys to play around with txs using the API.

I believe 0xOmar in Discord has also created a python library that can extract the private key from the desktop wallet seed, so you could look into that if you want to use the desktop wallet created address.

1 Like

Hi @Peachy ! Are you comfortable with the programming languages python or elixir?

1 Like

Yeah, I read over that, but his steps are a 1-way process:
Generate hex(private) → generate hex(public) → generate account (RDX…etc)

Wanting to go backwards is what I’m after.

1 Like

More comfortable with Python, but it’s been a few years.
Anything you can provided would be welcomed.
Thanks!

1 Like

I use construction/build to build a tx, it returned {"unsigned_transaction":"070d8ba1cbec4f2779517da61fb","payload_to_sign":"05a987eec4fa0ea073f5dc68b2689fa19bd03bd35f055da9370a1d5457279342"} ,

then I use construction/submit to send tx , { "network_identifier": { "network": "mainnet" }, "signed_transaction": "07030e70947..." }

by locally signed tx, How to construct the content signed_transaction of the field?

How the content of the transaction and the signature of the transaction are structured into one field

@Faraz

1 Like

Once you have the unsigned_transaction blob, you need to finalise the transaction with this along with the signature. The signature is a hash of your private key and the payload_to_sign from the construction step. Here’s a python snippet for that part:

import ecdsa
import hashlib
from ecdsa.curves import SECP256k1
from ecdsa.util import sigencode_der

private_key_hex = "<enter private key here>"
hash_to_sign = "<enter payload to sign here>"

# Create Private Key object from Private Key Hex String
signing_key = ecdsa.SigningKey.from_string(bytearray.fromhex(private_key_hex), curve=SECP256k1, hashfunc=hashlib.sha256)
# Use the Private Key to Sign the Transaction Hash
signed_hash = signing_key.sign_digest(bytearray.fromhex(hash_to_sign), sigencode=sigencode_der)

print("Signed Hash (DER Format): ", signed_hash.hex())
1 Like

Once you have the signature, the finalise tx is as follows:

curl -s POST -k 'https://mainnet-gateway.radixdlt.com/transaction/finalize' -H 'Content-Type: application/json' \
-d '{
  "network_identifier": {
    "network": "mainnet"
  },
  "unsigned_transaction": "<unsigned tx blob>",
  "signature": {
    "bytes": "<signature from step above>",
    "public_key": {
      "hex": "<your public key>"
    }
  },
  "submit": true
}'
2 Likes

Hello, I want to ask a silly thing, I have learned to sign in transactions using my main wallet, to make transactions, the problem is if I leak the contents of wallet.js to other people. can the contents of my wallet be drained by someone else, using this script?

And is wallet.js on radix wallet not securely protected? I can edit and move easily. will there be any improvements to make it securely encrypted so it’s not easy to find or steal.

1 Like