Bitcoin Forum

Bitcoin => Development & Technical Discussion => Topic started by: gimme_bottles on December 08, 2011, 11:30:13 PM



Title: get address from private key
Post by: gimme_bottles on December 08, 2011, 11:30:13 PM
hey,

i am searching for a documentation describing the process of calculation the corresponding bitcoin address from a private key.
does anyone has a link to such a document?


gimme_bottles


Title: Re: get address from private key
Post by: MelMan2002 on December 08, 2011, 11:41:43 PM
https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses


Title: Re: get address from private key
Post by: gimme_bottles on December 08, 2011, 11:57:01 PM
i read that article, but i don't get how to get the address from a private key, i only see the generation process of a bitcoin address.


Title: Re: get address from private key
Post by: casascius on December 09, 2011, 12:21:57 AM
i read that article, but i don't get how to get the address from a private key, i only see the generation process of a bitcoin address.

From the private key, you get the public key.  From the public key, you can get the address, based on the steps in the article.

Are you wondering how to get the public key from the private key?


Title: Re: get address from private key
Post by: gimme_bottles on December 09, 2011, 08:17:01 AM

Are you wondering how to get the public key from the private key?

yes, i am missing that step :)


Title: Re: get address from private key
Post by: casascius on December 09, 2011, 08:32:46 AM

Are you wondering how to get the public key from the private key?

yes, i am missing that step :)

You multiply the private key by the constant G.  You can find G at https://en.bitcoin.it/wiki/Secp256k1.

G is an elliptic curve point - not a simple number.  So there is special math that redefines what "multiplying" is.  A point times a number equals another point.  You may or may not need to care about the intricate details of that math.  If you are simply trying to write a program and are using crypto code from somewhere else, you simply call the already-defined "point multiply" function and get the answer.

The answer comes as two numbers - X and Y - because it's a point on a sort of graph - and then you can plug them in to the instructions you see that explain how to go from a public key to a Bitcoin address.


Title: Re: get address from private key
Post by: gimme_bottles on December 10, 2011, 01:08:54 AM
so, based on your help and openssl i hacked some code to calculate the address out of a randomly generated private key.
sadly it doesn't work as expected.

this is my code, the reverse of the string in the base58-encoding function is still missing:
Code:
#include <stdio.h>
#include <stdlib.h>

#include <openssl/sha.h>
#include <openssl/ecdsa.h>
#include <openssl/ripemd.h>
#include <openssl/ec.h>
#include <openssl/obj_mac.h>
#include <openssl/bn.h>


#include "hash.h"


unsigned char* privkey2addr(unsigned char* input)
{
   unsigned char* output;

   int ret;    /* return value of the sign-function */
   ECDSA_SIG *sig;   /* result of the signing process, containing both r and s */
   EC_KEY *eckey = EC_KEY_new();    /* create new key for the signing process */
   EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(NID_secp256k1);    /* set secp256k1 as the signing method */

   unsigned char *r, *s;

   if(eckey == NULL || ecgroup == NULL) {    /* check for errors */
      printf("Initializing of eckey or ecgroup failed!\n");
      return NULL;
   }

   if(EC_KEY_set_group(eckey,ecgroup) != 1) {   /* set signing method and check for errors */
      printf("Setting group for eckey failed!\n");
      return NULL;
   }

   if(EC_KEY_generate_key(eckey) != 1) {  /* generete key and check for errors */
      printf("Generating eckey failed!\n");
      return NULL;
   }

   sig = ECDSA_do_sign(input, 32, eckey); /* sign the input */

   if(sig==NULL) {   /* error? */
      printf("Signing of input failed!\n");
      return NULL;
   }

   ret = ECDSA_do_verify(input, 32, sig, eckey);   /* verify result */

   if(ret == -1 || ret == 0) {   /* error? */
      printf("Verifying failed!\n");
      return NULL;
   }

   r = malloc(32);   /* allocate 32 bit space for both r and s */
   s = malloc(32);

   if(BN_bn2bin(sig->r, r) != 32) { /* export BIGNUM to array of chars, check for invalid length */
      printf("Invalid result length!\n");
      return NULL;
   }

   if(BN_bn2bin(sig->s, s) != 32) { /* export BIGNUM to array of chars, check for invalid length */
      printf("Invalid result length!\n");
      return NULL;
   }

   int i;

   printf("X: "); /* print both r and s for debug reasons */
   for(i=0; i<32; i++) {
      printf("%02x", (unsigned char) *(r+i));
   }
   putc('\n', stdout);

   printf("Y: ");
   for(i=0; i<32; i++) {
      printf("%02x", (unsigned char) *(s+i));
   }
   putc('\n', stdout);


   unsigned char *pubkey_raw; /* raw public key '*/

   pubkey_raw = malloc(65); /* reserve 65 byte of space for the pubkey */

   *pubkey_raw = 0x04;   /* merge 0x04, r and s to the pubkey */

   for(i=0; i < 32; i++)
      *(pubkey_raw+i+1) = *(r+i);

   for(i = 0; i < 32; i++)
      *(pubkey_raw+i+33) = *(s+i);

   free(r); /* free no longer required memory */
   free(s);
   EC_KEY_free(eckey);
   EC_GROUP_free(ecgroup);
   ECDSA_SIG_free(sig);

   unsigned char *pubkey_stage1; /* public key after first stage */

   pubkey_stage1 = malloc(20);   /* reserve 20 bytes of memory */

   pubkey_stage1 = RIPEMD160(SHA256(pubkey_raw, 65, (unsigned char*) NULL), 32, (unsigned char *) NULL); /* hash it! */

   unsigned char *pubkey_stage2; /* public key after seconf stage */

   pubkey_stage2 = malloc(25);   /* reserve 25 bytes of memory */

   *pubkey_stage2 = 0x00;  /* put 0x00 at the beginning for main network */

   for(i = 0; i < 20; i++) /* add the result of stage one */
      *(pubkey_stage2+i+1) = *(pubkey_stage1+i);

   unsigned char *result;  /* the binary address */

   result = malloc(32); /* reserve 32 bytes of memory */

   result = SHA256(SHA256(pubkey_stage2, 32, (unsigned char*) NULL), 32, (unsigned char*) NULL);   /* calculate checksum */


   for(i = 0; i < 4; i++)  /* append checksum to binary address */
      *(pubkey_stage2+i+21) = *(result+i);

   printf("Binary address: ");   /* print binary address */
   for(i=0; i<25; i++) {
      printf("%02x", (unsigned char) *(pubkey_stage2+i));
   }
   putc('\n', stdout);



   output = malloc(35);

   output = base58(pubkey_stage2);

   return output;
}




unsigned char* base58(unsigned char* input)
{
   unsigned char *output;
   BIGNUM *a;
   BIGNUM *b58;
   BIGNUM *rem;

   int i;

   unsigned char *bc58;
   bc58 = malloc(1);
   *bc58=58;

   unsigned char *rest;
   rest = malloc(1);

   const char *alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

   BN_CTX *ctx = BN_CTX_new();
   BN_CTX_init(ctx);


   a = BN_new();
   BN_init(a);

   b58 = BN_new();
   BN_init(b58);

   rem = BN_new();
   BN_init(rem);

   BN_bin2bn((const unsigned char*) input, 25, a);

   BN_bin2bn((const unsigned char*) bc58 , 1, b58);

   output = malloc(35);

   i = 0;

   while(BN_is_zero(a) != 1) {
      BN_div(a, rem, a, b58, ctx);
      BN_bn2bin(rem, rest);
      *(output+i) = alphabet[*rest];
      i++;
   }

   int n = 0, b = 0;

   for(b = 0; b < 25; b++) {
      if(*(input+b)==0)
         n++;
      if(*(input+b)!=0)
         break;
   }

   printf("%i, %i\n", n, i);

   for(b = 0; b < n; b++) {
      *(output+i+b) = alphabet[0];
   }




   return output;
}

my output looks the following, i also print some debug information:
Code:
Private Key: 1809c5d482b97ec84dd7e94cc4cbed460fd878025b52b1011970a3626e64eda8
X: c2429880bd9be5ebe72f00cc34a878cc6e5060f749b7b37957a2f087817e36c0
Y: 4117f999affb005828fd957cfaccc60530c871a05b7d205746d2ca68082d2d7d
Binary address: 0075c5672c37a9140631a0bf915c0e6aa356a832b04bd05648
1, 33
Address: 5ZL8VhHCBiePTgdNn4uQYanWMMgHFYijB1
355a4c3856684843426965505467644e6e34755159616e574d4d67484659696a423100


all outputs are in HEX-format, except for the encoded address, that you have to reverse in your minds :)
when i use bitcointools.appspot.com (http://bitcointools.appspot.com) to get the address from my private key, the result is different to mine...

any suggestions?


Title: Re: get address from private key
Post by: casascius on December 10, 2011, 01:55:44 AM
I looked not at your code, but your numbers.

The calculation from private key to public key is wrong.  Why, I don't know.

Private key 1809c5d482b97ec84dd7e94cc4cbed460fd878025b52b1011970a3626e64eda8 (I am assuming this is big-endian, like it usually is)

shoudl have public key 04 AE 64 14 8F B6 68 4D 51 CF 03 C5 60 D7 36 28 2E 86 54 FB 54 D2 21 0D 67 4A F1 32 B1 ED 2D 2E 87 D0 B1 05 D0 97 5D C7 5C 31 91 CF 7E 64 ED FB 06 98 AF 85 7A 4A 0A BF 45 D0 06 01 00 9C 9E B8 8A

which is
04

X = AE 64 14 8F B6 68 4D 51 CF 03 C5 60 D7 36 28 2E 86 54 FB 54 D2 21 0D 67 4A F1 32 B1 ED 2D 2E 87
Y = D0 B1 05 D0 97 5D C7 5C 31 91 CF 7E 64 ED FB 06 98 AF 85 7A 4A 0A BF 45 D0 06 01 00 9C 9E B8 8A

calculated by a known working utility.


Title: Re: get address from private key
Post by: gimme_bottles on December 10, 2011, 02:16:07 AM
yes, it is all big endian.
thanks for your example, it is great to have some values to work with :)


Title: Re: get address from private key
Post by: ByteCoin on December 10, 2011, 04:58:54 AM

Code:
   printf("X: "); /* print both r and s for debug reasons */
   for(i=0; i<32; i++) {
      printf("%02x", (unsigned char) *(r+i));
   }

   printf("Y: ");
   for(i=0; i<32; i++) {
      printf("%02x", (unsigned char) *(s+i));
   }

There's a reason why the things you're printing out and calling X and Y are really called r and s. That's because r and s have nothing to do with elliptic curve coordinates. Read the wikipedia article for ECDSA for the details. Generating an address from a public key does not require ECDSA_do_sign or do_verify so you can get rid of that code. The coordinates are likely to be stored in the public key structure in some fashion.

ByteCoin