Splitting a Solana Keypair into Public and Private Keys

In this guide we’re going to dive into how to take a Solana keypair and break it its corresponding public and private keys.

Lets go ahead and use the Solana CLI to generate a new wallet with the keypair stored in a File System Wallet:

$ solana-keygen new --outfile ~/my-solana-wallet/demo.json
      
Generating a new keypair
For added security, enter a BIP39 passphrase
NOTE! This passphrase improves security of the recovery seed phrase NOT the
keypair file itself, which is stored as insecure plain text
BIP39 Passphrase (empty for none): 
Wrote new keypair to /Users/matt/my-solana-wallet/demo.json
=============================================================================
pubkey: HAE1oNnc3XBmPudphRcHhyCvGShtgDYtZVzx2MocKEr1
=============================================================================
Save this seed phrase and your BIP39 passphrase to recover your new keypair:
favorite maid bind piece social father neutral over unusual tank brother code
=============================================================================

This command generates a JSON file containing the keypair:

$ cat ~/my-solana-wallet/demo.json

[4,182,130,247,119,117,227,207,112,73,170,126,222,197,244,99,215,107,255,202,33,43,36,17,104,111,157,246,196,192,174,95,240,23,238,206,118,215,154,238,229,96,11,37,156,123,51,223,5,231,17,117,86,136,103,14,75,95,175,132,148,54,1,46]  

This is an array containing 64 values:

The first 32 values represent the private key:

private_key_bytes = [4, 182, 130, 247, 119, 117, 227, 207, 112, 73, 170, 126, 222, 197, 244, 99, 215, 107, 255, 202, 33, 43, 36, 17, 104, 111, 157, 246, 196, 192, 174, 95]

The second 32 values represent the public key:

public_key_bytes = [240, 23, 238, 206, 118, 215, 154, 238, 229, 96, 11, 37, 156, 123, 51, 223, 5, 231, 17, 117, 86, 136, 103, 14, 75, 95, 175, 132, 148, 54, 1, 46]

While this is the public key, it’s not in the same format as the public key displayed by the Solana CLI above. In order to format it that way, we need to convert this array of integer values into base 58.

In Ruby we’ll first convert the array to hex (base 16):

public_key_hex = public_key_bytes.pack("C*").unpack("H*").first

And then we can use a base 58 script to convert the hex into the base 58:

public_key = Base58.encode(public_key_hex)

And that gives us the same public key aka wallet address that we saw earlier in the Solana CLI output:

HAE1oNnc3XBmPudphRcHhyCvGShtgDYtZVzx2MocKEr1

We can follow the same process to get the base 58 private key:

KQ3cGFBdjJuRsB7U1K4to6cTGBPhgukqPgsi5pryr8v

And also the base 58 keypair (which is the same thing you would get if you clicked the Export Private Key button in a Phantom Wallet, but note that it’s not actually the private key, it’s the keypair with both the private and public key – I suspect they call it that to make it clear to users that it includes the private key):

6Tyktf6mEqUMEKm2ZpLn3srEwk9zsT5jiE54EgPgToikMFYww1LGFUXgwgr6hvc9CikpaNaBH2vmkmqN3Usrxpd

Try it yourself

1. Create a new folder

2. Create a file called base58.rb and copy the base 58 Ruby script from this tutorial

3. Create another file named like keypair.rb and paste in the following script:

require_relative "base58"
def bytes_to_base58(bytes)
hex = bytes.pack("C*").unpack("H*").first
Base58.encode(hex)
end
keypair_bytes = [4,182,130,247,119,117,227,207,112,73,170,126,222,197,244,99,215,107,255,202,33,43,36,17,104,111,157,246,196,192,174,95,240,23,238,206,118,215,154,238,229,96,11,37,156,123,51,223,5,231,17,117,86,136,103,14,75,95,175,132,148,54,1,46]
private_key_bytes = keypair_bytes[0, 32]
public_key_bytes = keypair_bytes[32..-1]
puts "Keypair:", bytes_to_base58(keypair_bytes)
puts "\nPublic Key:", bytes_to_base58(public_key_bytes)
puts "\nPrivate Key:", bytes_to_base58(private_key_bytes)
view raw keypair.rb hosted with ❤ by GitHub

4. Replace the content of keypair_bytes with the value from your File System Wallet (you can use solana config get to get the path to your keypair, then use the cat command like above to get the content).

5. Then from the command line simply run ruby keypair.rb to run the script:

$ ruby keypair.rb 
 
Keypair:
6Tyktf6mEqUMEKm2ZpLn3srEwk9zsT5jiE54EgPgToikMFYww1LGFUXgwgr6hvc9CikpaNaBH2vmkmqN3Usrxpd

Public Key:
HAE1oNnc3XBmPudphRcHhyCvGShtgDYtZVzx2MocKEr1

Private Key:
KQ3cGFBdjJuRsB7U1K4to6cTGBPhgukqPgsi5pryr8v

Starting with a Base 58 Keypair

You can follow a similar process to take a base 58 keypair (like you would get from Export Private Key in a Phantom wallet) to derive the public and private keys:

require_relative "base58"
def bytes_to_base58(bytes)
hex = bytes.pack("C*").unpack("H*").first
Base58.encode(hex)
end
keypair = "6Tyktf6mEqUMEKm2ZpLn3srEwk9zsT5jiE54EgPgToikMFYww1LGFUXgwgr6hvc9CikpaNaBH2vmkmqN3Usrxpd"
keypair_hex = Base58.decode(keypair).rjust(128, "0")
keypair_bytes = [keypair_hex].pack('H*').bytes.to_a
private_key_bytes = keypair_bytes[0, 32]
public_key_bytes = keypair_bytes[32..-1]
puts "Public Key:", bytes_to_base58(public_key_bytes)
puts "\nPrivate Key:", bytes_to_base58(private_key_bytes)
view raw keypair58.rb hosted with ❤ by GitHub
$ ruby keypair58.rb

Public Key:
HAE1oNnc3XBmPudphRcHhyCvGShtgDYtZVzx2MocKEr1

Private Key:
KQ3cGFBdjJuRsB7U1K4to6cTGBPhgukqPgsi5pryr8v

One thought on “Splitting a Solana Keypair into Public and Private Keys

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s