<pre><code>void Bitcoin::test ( )
{
  const QByteArray t1 ( QByteArray::fromHex ( "010000000...ac00000000" ) ); // strip
  const QByteArray t2 ( QByteArray::fromHex ( "010000000...ac00000000" ) ); // strip
  Stream stream1 ( t1 );
  Stream stream2 ( t2 );
  const Transaction tx1 ( stream1 );
  const Transaction tx2 ( stream2 );
  _trace ( QString ( "txid1=%1" ).arg ( tx1.getKey ( ).toString ( ) ) );
  _trace ( QString ( "txid2=%1" ).arg ( tx2.getKey ( ).toString ( ) ) );
  const TxInput in1 ( tx1.getInput ( 0 ) );
  const TxInput in2 ( tx2.getInput ( 0 ) );
  const QByteArray script1 ( in1.getScript ( ) );
  const QByteArray script2 ( in2.getScript ( ) );
  EvalScript escr1 ( script1 );
  EvalScript escr2 ( script2 );
  xassert ( escr1.evalInput ( ) == EVAL_I_SIG_PUB  );
  xassert ( escr2.evalInput ( ) == EVAL_I_SIG_PUB  );
  _trace ( QString ( "pub1=%1" ).arg ( escr1.getInPubKey ( ).toHex ( ).constData ( ) ) );
  _trace ( QString ( "pub2=%1" ).arg ( escr2.getInPubKey ( ).toHex ( ).constData ( ) ) );
  xassert ( escr1.getInPubKey ( ) == escr2.getInPubKey ( ) ); // same pubkeys?
  const MyByteArray sig1 ( escr1.getInSignature ( ) );
  const MyByteArray sig2 ( escr2.getInSignature ( ) );
  char buf [64];
  sig1.signatureRS ( buf );
  const MyKey32 R1 ( buf );
  const MyKey32 S1 ( buf + 32 );
  _trace ( QString ( "R1=%1" ).arg ( R1.toStringRev ( ) ) );
  _trace ( QString ( "S1=%1" ).arg ( S1.toStringRev ( ) ) );
  sig2.signatureRS ( buf );
  const MyKey32 R2 ( buf );
  const MyKey32 S2 ( buf + 32 );
  _trace ( QString ( "R2=%1" ).arg ( R2.toStringRev ( ) ) );
  _trace ( QString ( "S2=%1" ).arg ( S2.toStringRev ( ) ) );
  xassert ( R1 == R2 ); // are R-values really equal?
  const QByteArray xsc ( MyByteArray ( )
             .putInt8 ( OP_DUP )
             .putInt8 ( OP_HASH160 )
             .putPush ( MyKey20::calc ( escr1.getInPubKey ( ) ) )
             .putInt8 ( OP_EQUALVERIFY )
             .putInt8 ( OP_CHECKSIG ) );
  const MyKey32 Z1 ( tx1.getRawHash ( 0, xsc ) );
  const MyKey32 Z2 ( tx2.getRawHash ( 0, xsc ) );

  _trace ( QString ( "Z1=%1" ).arg ( Z1.toStringRev ( ) ) );
  _trace ( QString ( "Z2=%1" ).arg ( Z2.toStringRev ( ) ) );

  const MyKey20 address ( MyKey20::calc ( escr1.getInPubKey ( ) ) );
  const MyKey32 priv ( MyKey32::getPrivateKey ( R1, S1, Z1, S2, Z2, address ) );
  if ( priv.getAddressHashClassic ( ) == address )
    _trace ( QString ( "classic priv=%1" ).arg ( priv.toStringWif ( false ) ) );
  if ( priv.getAddressHashCompressed ( ) == address )
    _trace ( QString ( "compressed priv=%1" ).arg ( priv.toStringWif ( true ) ) );
}
</code></pre>

<p>and the output is:</p>

<pre><code>"txid1=8c1d0dbe2fb84ec4b14ee146e3e899623b3e5c30aef4c8238cf9fe2f8406246c"
"txid2=40e334f8addc00a67209b2818ceb32e1ebd5c28133479f3e408d99f28267828d"
"pub1=04cb5035b2d8fc1019146a7e4fba751a9a9bfafa2d1e35528a9397051c6e630513694ab925adc4083dcafc86c92796a2b6ae977adfc0bb47df9106a6a9263d35f6"
"pub2=04cb5035b2d8fc1019146a7e4fba751a9a9bfafa2d1e35528a9397051c6e630513694ab925adc4083dcafc86c92796a2b6ae977adfc0bb47df9106a6a9263d35f6"
"R1=bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e"
"S1=b973d6c776f1a8db2b7bc654c7e55384422677f41928ec4c30643f383d587bfd"
"R2=bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e"
"S2=be1c50365d8ed4e269cb7350dee3b5fcc9f81856726a590dcdd3f538dafcd045"
"Z1=187509d80dacdd9b35f626dedd2856c7a318455e2d37bcacfe006406b60cf2fe"
"Z2=41d91692534416f8e8d6f3480bb1eae488ce4e24e3afb2c830534411ea5efb1c"
"classic priv=5K1V3pAHQ5DgALeiCC2NfgKESKEaygmCLtBRPFbw9eu1uJrDHW1"
</code></pre>

<p>... aaaaaaand the private key is <strong>5K1V3pAHQ5DgALeiCC2NfgKESKEaygmCLtBRPFbw9eu1uJrDHW1</strong></p>

CC BY-SA 3.0
2016-02-06T08:50:00.307Z
2016-02-06T08:50:00.307Z
amaclin
<p>Hopefully most web wallets and wallet services are not re-using R values to sign transactions since this has been a known vulnerability for a while now. On a very low level you are turning an equation like x + y = 10 into x + 2 = 10 making it easy to find x. </p>

<p>First find the reused R values, in your case it's:
<code>2100bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e</code></p>

<p>Then find the signature coordinates in the two transactions:    </p>

<p>tx1 coordinate(S1):<br>
<code>00b973d6c776f1a8db2b7bc654c7e55384422677f41928ec4c30643f383d587bfd</code><br>
tx2 coordinate(S2):<br>
<code>00be1c50365d8ed4e269cb7350dee3b5fcc9f81856726a590dcdd3f538dafcd045</code></p>

<p>Now the tricky part, you can use the bitcoin secp256k1 curve to reverse calculate the private key, but you'll need also need to use a bitcoin transaction library to use <code>op_checksig()</code> for the missing sig hashes to find SOP1 and SOP2. I'll let one of the more advanced users explain finding the above two values, as I don't fully understand it myself. <a href="https://bitcointalk.org/index.php?topic=977070.msg11160403#msg11160403" rel="nofollow" title="link">Link</a></p>

<p>p = parameter for secp256k1<br>
Limit the secp256k1 field:<br>
<code>p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141</code><br>
<code>K = GF(p)</code></p>

<p>In the end it's:</p>

<p><code>K = (sop1*s2 - sop2*s1)/(r*(s1-s2))</code></p>

<p>This will output an integer, will need to be hexcoded, and then converted to WIF. </p>

<p>Source:<br>
<a href="https://bitcointalk.org/index.php?topic=977070.msg10669517#msg10669517" rel="nofollow">https://bitcointalk.org/index.php?topic=977070.msg10669517#msg10669517</a><br>
<a href="https://bitcointalk.org/index.php?topic=977070.msg11161149#msg11161149" rel="nofollow">https://bitcointalk.org/index.php?topic=977070.msg11161149#msg11161149</a>     </p>

CC BY-SA 3.0
2016-01-31T17:16:38.793Z
2016-01-31T17:16:38.793Z
m1xolyd1an
<p>I have 2 transactions.</p>

<pre><code>0100000001494d40d44b9ea0d652fca9258ab7caa42551eb52975887f96fb50cd632c8b497000000008c493046022100bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e022100b973d6c776f1a8db2b7bc654c7e55384422677f41928ec4c30643f383d587bfd014104cb5035b2d8fc1019146a7e4fba751a9a9bfafa2d1e35528a9397051c6e630513694ab925adc4083dcafc86c92796a2b6ae977adfc0bb47df9106a6a9263d35f6ffffffff0120030000000000001976a91468979da3812f340e577fc379c20a8f8a505f02dd88ac00000000



0100000001494d40d44b9ea0d652fca9258ab7caa42551eb52975887f96fb50cd632c8b427000000008c493046022100bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e022100be1c50365d8ed4e269cb7350dee3b5fcc9f81856726a590dcdd3f538dafcd045014104cb5035b2d8fc1019146a7e4fba751a9a9bfafa2d1e35528a9397051c6e630513694ab925adc4083dcafc86c92796a2b6ae977adfc0bb47df9106a6a9263d35f6ffffffff0120030000000000001976a91468979da3812f340e577fc379c20a8f8a505f02dd88ac00000000
</code></pre>

<p>I know that the k values and hence the R values of both these transactions are the same.</p>

<p>So now how do I recover the private key from these two transactions in hex?</p>

<p>A detailed step by step procedure on how to arrive at the answer will be appreciated.</p>

<transactions><private-key>
Find private key
Archisman Dinda
# Find private key

(Asked by: username_0 on 2016-01-31)

I have 2 transactions.
0100000001494d40d44b9ea0d652fca9258ab7caa42551eb52975887f96fb50cd632c8b497000000008c493046022100bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e022100b973d6c776f1a8db2b7bc654c7e55384422677f41928ec4c30643f383d587bfd014104cb5035b2d8fc1019146a7e4fba751a9a9bfafa2d1e35528a9397051c6e630513694ab925adc4083dcafc86c92796a2b6ae977adfc0bb47df9106a6a9263d35f6ffffffff0120030000000000001976a91468979da3812f340e577fc379c20a8f8a505f02dd88ac00000000



0100000001494d40d44b9ea0d652fca9258ab7caa42551eb52975887f96fb50cd632c8b427000000008c493046022100bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e022100be1c50365d8ed4e269cb7350dee3b5fcc9f81856726a590dcdd3f538dafcd045014104cb5035b2d8fc1019146a7e4fba751a9a9bfafa2d1e35528a9397051c6e630513694ab925adc4083dcafc86c92796a2b6ae977adfc0bb47df9106a6a9263d35f6ffffffff0120030000000000001976a91468979da3812f340e577fc379c20a8f8a505f02dd88ac00000000

I know that the k values and hence the R values of both these transactions are the same.
So now how do I recover the private key from these two transactions in hex?
A detailed step by step procedure on how to arrive at the answer will be appreciated.

**Tags:** transactions, private-key


---

## 2 answers

### Answer by username_1 on 2016-02-06. Score: 0 | Accepted answer

void Bitcoin::test ( )
{
  const QByteArray t1 ( QByteArray::fromHex ( "010000000...ac00000000" ) ); // strip
  const QByteArray t2 ( QByteArray::fromHex ( "010000000...ac00000000" ) ); // strip
  Stream stream1 ( t1 );
  Stream stream2 ( t2 );
  const Transaction tx1 ( stream1 );
  const Transaction tx2 ( stream2 );
  _trace ( QString ( "txid1=%1" ).arg ( tx1.getKey ( ).toString ( ) ) );
  _trace ( QString ( "txid2=%1" ).arg ( tx2.getKey ( ).toString ( ) ) );
  const TxInput in1 ( tx1.getInput ( 0 ) );
  const TxInput in2 ( tx2.getInput ( 0 ) );
  const QByteArray script1 ( in1.getScript ( ) );
  const QByteArray script2 ( in2.getScript ( ) );
  EvalScript escr1 ( script1 );
  EvalScript escr2 ( script2 );
  xassert ( escr1.evalInput ( ) == EVAL_I_SIG_PUB  );
  xassert ( escr2.evalInput ( ) == EVAL_I_SIG_PUB  );
  _trace ( QString ( "pub1=%1" ).arg ( escr1.getInPubKey ( ).toHex ( ).constData ( ) ) );
  _trace ( QString ( "pub2=%1" ).arg ( escr2.getInPubKey ( ).toHex ( ).constData ( ) ) );
  xassert ( escr1.getInPubKey ( ) == escr2.getInPubKey ( ) ); // same pubkeys?
  const MyByteArray sig1 ( escr1.getInSignature ( ) );
  const MyByteArray sig2 ( escr2.getInSignature ( ) );
  char buf [64];
  sig1.signatureRS ( buf );
  const MyKey32 R1 ( buf );
  const MyKey32 S1 ( buf + 32 );
  _trace ( QString ( "R1=%1" ).arg ( R1.toStringRev ( ) ) );
  _trace ( QString ( "S1=%1" ).arg ( S1.toStringRev ( ) ) );
  sig2.signatureRS ( buf );
  const MyKey32 R2 ( buf );
  const MyKey32 S2 ( buf + 32 );
  _trace ( QString ( "R2=%1" ).arg ( R2.toStringRev ( ) ) );
  _trace ( QString ( "S2=%1" ).arg ( S2.toStringRev ( ) ) );
  xassert ( R1 == R2 ); // are R-values really equal?
  const QByteArray xsc ( MyByteArray ( )
             .putInt8 ( OP_DUP )
             .putInt8 ( OP_HASH160 )
             .putPush ( MyKey20::calc ( escr1.getInPubKey ( ) ) )
             .putInt8 ( OP_EQUALVERIFY )
             .putInt8 ( OP_CHECKSIG ) );
  const MyKey32 Z1 ( tx1.getRawHash ( 0, xsc ) );
  const MyKey32 Z2 ( tx2.getRawHash ( 0, xsc ) );

  _trace ( QString ( "Z1=%1" ).arg ( Z1.toStringRev ( ) ) );
  _trace ( QString ( "Z2=%1" ).arg ( Z2.toStringRev ( ) ) );

  const MyKey20 address ( MyKey20::calc ( escr1.getInPubKey ( ) ) );
  const MyKey32 priv ( MyKey32::getPrivateKey ( R1, S1, Z1, S2, Z2, address ) );
  if ( priv.getAddressHashClassic ( ) == address )
    _trace ( QString ( "classic priv=%1" ).arg ( priv.toStringWif ( false ) ) );
  if ( priv.getAddressHashCompressed ( ) == address )
    _trace ( QString ( "compressed priv=%1" ).arg ( priv.toStringWif ( true ) ) );
}

and the output is:
"txid1=8c1d0dbe2fb84ec4b14ee146e3e899623b3e5c30aef4c8238cf9fe2f8406246c"
"txid2=40e334f8addc00a67209b2818ceb32e1ebd5c28133479f3e408d99f28267828d"
"pub1=04cb5035b2d8fc1019146a7e4fba751a9a9bfafa2d1e35528a9397051c6e630513694ab925adc4083dcafc86c92796a2b6ae977adfc0bb47df9106a6a9263d35f6"
"pub2=04cb5035b2d8fc1019146a7e4fba751a9a9bfafa2d1e35528a9397051c6e630513694ab925adc4083dcafc86c92796a2b6ae977adfc0bb47df9106a6a9263d35f6"
"R1=bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e"
"S1=b973d6c776f1a8db2b7bc654c7e55384422677f41928ec4c30643f383d587bfd"
"R2=bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e"
"S2=be1c50365d8ed4e269cb7350dee3b5fcc9f81856726a590dcdd3f538dafcd045"
"Z1=187509d80dacdd9b35f626dedd2856c7a318455e2d37bcacfe006406b60cf2fe"
"Z2=41d91692534416f8e8d6f3480bb1eae488ce4e24e3afb2c830534411ea5efb1c"
"classic priv=5K1V3pAHQ5DgALeiCC2NfgKESKEaygmCLtBRPFbw9eu1uJrDHW1"

... aaaaaaand the private key is 5K1V3pAHQ5DgALeiCC2NfgKESKEaygmCLtBRPFbw9eu1uJrDHW1


---

### Answer by username_2 on 2016-01-31. Score: 2

Hopefully most web wallets and wallet services are not re-using R values to sign transactions since this has been a known vulnerability for a while now. On a very low level you are turning an equation like x + y = 10 into x + 2 = 10 making it easy to find x.

First find the reused R values, in your case it's:
`2100bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e`

Then find the signature coordinates in the two transactions:

tx1 coordinate(S1):  
`00b973d6c776f1a8db2b7bc654c7e55384422677f41928ec4c30643f383d587bfd`  
tx2 coordinate(S2):  
`00be1c50365d8ed4e269cb7350dee3b5fcc9f81856726a590dcdd3f538dafcd045`

Now the tricky part, you can use the bitcoin secp256k1 curve to reverse calculate the private key, but you'll need also need to use a bitcoin transaction library to use `op_checksig()` for the missing sig hashes to find SOP1 and SOP2. I'll let one of the more advanced users explain finding the above two values, as I don't fully understand it myself. [Link](https://bitcointalk.org/index.php?topic=977070.msg11160403#msg11160403 "link")

p = parameter for secp256k1  
Limit the secp256k1 field:  
`p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141`  
`K = GF(p)`

In the end it's:

`K = (sop1*s2 - sop2*s1)/(r*(s1-s2))`

This will output an integer, will need to be hexcoded, and then converted to WIF.

Source:  
<https://bitcointalk.org/index.php?topic=977070.msg10669517#msg10669517>  
<https://bitcointalk.org/index.php?topic=977070.msg11161149#msg11161149>


---

