发现r和s并计算z值

发现r和s


# -*- coding:utf-8 -*-
import json
import urllib2
import time
import sys,keyUtils


def get_r_and_s(input_scriptsig):
#print (input_scriptsig)
try:
sigLen = int(input_scriptsig[0:2], 16)
sig = input_scriptsig[2:2 + sigLen * 2]
pubLen = int(input_scriptsig[2 + sigLen * 2:2 + sigLen * 2 + 2], 16)
pub = input_scriptsig[2 + sigLen * 2 + 2:]
addr = keyUtils.pubKeyToAddr(pub)
tmp_sig = keyUtils.derSigToHexSig(sig[:-2])
r=tmp_sig[:64]
s=tmp_sig[-64:]
return r,s
except Exception as e:
return "",""

def rscan(addr):
"""Check address for duplicated r values."""
# TODO: add BCI API check address
print("-"*50)
print ("{}".format(str(addr)))
urladdr = 'https://blockchain.info/address/%s?format=json&offset=%s'


addrdata = json.load(urllib2.urlopen(urladdr % (addr, '0')))
ntx = addrdata['n_tx']
print "Data for pubkey: " + str(addr) + " has " + str(addrdata['n_tx']).center(6) + "Tx%s" % 's'[ntx==1:]
#print "number of txs: " + str(addrdata['n_tx'])


txs = []
for i in range(0, ntx//50 + 1):
#sys.stderr.write("Fetching Txs from offset\t%s\n" % str(i*50))
print("Fetching Txs from offset\t%s" % str(i*50))
jdata = json.load(urllib2.urlopen(urladdr % (addr, str(i*50))))
txs.extend(jdata['txs'])

assert len(txs) == ntx
addrdata['txs'] = txs


y = 0
inputs = []
while y < ntx:
zy = 0
while zy < addrdata['txs'][y]['vin_sz']:
inputs.append(addrdata['txs'][y]['inputs'][zy]['script']+"|"+addrdata['txs'][y]['hash'])
zy += 1
y += 1

xi = 0
zi = 1
lenx = len(inputs)
alert = 0
bad = []
for a in range(lenx):
for b in range(a+1,lenx):
##print("inputs[b]="+inputs[b])
script_sig_a = inputs[a].split("|")[0]
script_sig_b = inputs[b].split("|")[0]
if script_sig_a and script_sig_b:
r1,s1=get_r_and_s(script_sig_a)
r2,s2=get_r_and_s(script_sig_b)
if r1 and r2 and r1 == r2:
tx1=inputs[a].split("|")[1]
tx2=inputs[b].split("|")[1]
#print('r={}|s1={}|s2={}'.format(str(r),str(s1),str(s2)))
if (s1 and s2 and len(s1) == 64 and len(s2)==64 and str(s1) !=str(s2)):
#print('tx1={}|r={}|s1={}|tx2={}|s2={}'.format(str(tx1),str(r),str(s1),str(tx2),str(s2)))
print('{}|{}|{}|{}|{}|{}'.format(str(tx1),str(r1),str(s1),str(tx2),str(r2),str(s2)))
#print inputs[x+zi][10:74]
bad.append((int(a), str(r1),str(s1),str(s2)))
alert += 1
'''
while xi < lenx-1:
x = 0
while x < lenx-zi:
if inputs[xi][10:74] == inputs[x+zi][10:74]:
#print "Resued R-Value: "
r=inputs[x+zi][10:74]
s1=inputs[xi][78:140]
s2=inputs[x+zi][78:140]
print('r={}|s1={}|s2={}'.format(str(r),str(s1),str(s2)))
if (str(s1) !=str(s2)):
print('r={}|s1={}|s2={}'.format(str(r),str(s1),str(s2)))
#print inputs[x+zi][10:74]
bad.append((int(x), str(inputs[x+zi][10:74])))
alert += 1
#x += 1
zi += 1
xi += 1
'''

if alert < 1:
print "Good pubKey. No problems."
else:
print "Address %s has %d reused R value%s!" % (addr, len(bad), "s"[len(bad)==1:])
return bad
if __name__ == '__main__':
from sys import argv
import time,random
addr='1BMzWp77j7x3GKDYNbCP3df7YG3UEw1vVE'
rscan(addr)
'''
addr_file='myaddr.txt'
with open(addr_file,'r') as f:
cont = f.readline().strip()
while cont:
addr = cont
time.sleep(random.randint(1,5))
rscan(addr)
cont = f.readline().strip()
'''
以上用来发现符合规律的r和s

keyUtils代码见
import ecdsa import ecdsa.der import ecdsa.util import hashlib import unittest import random import re import struct #import utils import base58 # https://en.bitcoin.it/wiki/Wallet_import_format def privateKeyToWif(key_hex): return base58.b58encode_check(0x80, key_hex.decode('hex')) def wifToPrivateKey(s): b = base58.bs58decode_check(s) return b.encode('hex') # Input is a hex-encoded, DER-encoded signature # Output is a 64-byte hex-encoded signature def derSigToHexSig(s): s, junk = ecdsa.der.remove_sequence(s.decode('hex')) if junk != '': print('JUNK', junk.encode('hex')) assert(junk == '') x, s = ecdsa.der.remove_integer(s) y, s = ecdsa.der.remove_integer(s) return ('%064x%064x' % (x, y)) # Input is hex string def privateKeyToPublicKey(s): sk = ecdsa.SigningKey.from_string(s.decode('hex'), curve=ecdsa.SECP256k1) vk = sk.verifying_key return ('\04' + sk.verifying_key.to_string()).encode('hex') # Input is hex string def keyToAddr(s): return pubKeyToAddr(privateKeyToPublicKey(s)) def pubKeyToAddr(s): ripemd160 = hashlib.new('ripemd160') ripemd160.update(hashlib.sha256(s.decode('hex')).digest()) return base58.b58encode_check(ripemd160.digest()) def addrHashToScriptPubKey(b58str): assert(len(b58str) == 34) # 76 A9 14 (20 bytes) 88 AC return ('76a914' + utils.base58CheckDecode(b58str).encode('hex') + '88ac') class TestKey(unittest.TestCase): def test_privateKeyToWif(self): w = privateKeyToWif("0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D") self.assertEqual(w, "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ") def test_WifToPrivateKey(self): k = wifToPrivateKey("5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ") self.assertEqual(k.upper(), "0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D") def test_keyToAddr(self): a = keyToAddr("18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725") self.assertEqual(a, "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM") def test_pairs1(self): #blockchain.info wallet_addr = "1EyBEhrriJeghX4iqATQEWDq38Ae8ubBJe" wallet_private = "8tnArBrrp4KHVjv8WA6HiX4ev56WDhqGA16XJCHJzhNH" wallet_key = utils.base256encode(utils.base58decode(wallet_private)).encode('hex') self.assertEqual(keyToAddr(wallet_key), wallet_addr) # can import into multibit bitcoin_qt = "5Jhw8B9J9QLaMmcBRfz7x8KkD9gwbNoyBMfWyANqiDwm3FFwgGC" wallet_key = utils.base58CheckDecode(bitcoin_qt).encode('hex') self.assertEqual(keyToAddr(wallet_key), wallet_addr) wallet_key = "754580de93eea21579441b58e0c9b09f54f6005fc71135f5cfac027394b22caa" self.assertEqual(keyToAddr(wallet_key), wallet_addr) def test_pairs2(self): #http://gobittest.appspot.com/Address # Cannot import into multibit wallet_private = "BB08A897EA1E422F989D36DE8D8186D8406BE25E577FD2A66976BF172325CDC9" wallet_addr = "1MZ1nxFpvUgaPYYWaLPkLGAtKjRqcCwbGh" self.assertEqual(keyToAddr(wallet_private), wallet_addr) def test_pairs3(self): # Can import into multibit # http://bitaddress.org wallet_private = "5J8PhneLEaL9qEPvW5voRgrELeXcmM12B6FbiA9wZAwDMnJMb2L" wallet_addr = "1Q2SuNLDXDtda7DPnBTocQWtUg1v4xZMrV" self.assertEqual(keyToAddr(utils.base58CheckDecode(wallet_private).encode('hex')), wallet_addr) def test_der(self): self.assertEqual(ecdsa.der.encode_sequence( ecdsa.der.encode_integer(0x123456), ecdsa.der.encode_integer(0x89abcd)).encode('hex'), "300b020312345602040089abcd") def test_derSigToHexSig(self): derSig = "304502204c01fee2d724fb2e34930c658f585d49be2f6ac87c126506c0179e6977716093022100faad0afd3ae536cfe11f83afaba9a8914fc0e70d4c6d1495333b2fb3df6e8cae" self.assertEqual("4c01fee2d724fb2e34930c658f585d49be2f6ac87c126506c0179e6977716093faad0afd3ae536cfe11f83afaba9a8914fc0e70d4c6d1495333b2fb3df6e8cae", derSigToHexSig(derSig)) txn = ("0100000001a97830933769fe33c6155286ffae34db44c6b8783a2d8ca52ebee6414d399ec300000000" + "8a47" + "304402202c2e1a746c556546f2c959e92f2d0bd2678274823cc55e11628284e4a13016f80220797e716835f9dbcddb752cd0115a970a022ea6f2d8edafff6e087f928e41baac01" + "41" + "04392b964e911955ed50e4e368a9476bc3f9dcc134280e15636430eb91145dab739f0d68b82cf33003379d885a0b212ac95e9cddfd2d391807934d25995468bc55" + "ffffffff02015f0000000000001976a914c8e90996c7c6080ee06284600c684ed904d14c5c88ac204e000000000000" + "1976a914348514b329fda7bd33c7b2336cf7cd1fc9544c0588ac00000000") myTxn_forSig =("0100000001a97830933769fe33c6155286ffae34db44c6b8783a2d8ca52ebee6414d399ec300000000" + "1976a914" + "167c74f7491fe552ce9e1912810a984355b8ee07" + "88ac" + "ffffffff02015f0000000000001976a914c8e90996c7c6080ee06284600c684ed904d14c5c88ac204e000000000000" +"1976a914348514b329fda7bd33c7b2336cf7cd1fc9544c0588ac00000000" + "01000000") public_key = "04392b964e911955ed50e4e368a9476bc3f9dcc134280e15636430eb91145dab739f0d68b82cf33003379d885a0b212ac95e9cddfd2d391807934d25995468bc55" hashToSign = hashlib.sha256(hashlib.sha256(myTxn_forSig.decode('hex')).digest()).digest().encode('hex') sig_der = "304402202c2e1a746c556546f2c959e92f2d0bd2678274823cc55e11628284e4a13016f80220797e716835f9dbcddb752cd0115a970a022ea6f2d8edafff6e087f928e41baac01"[:-2] sig = derSigToHexSig(sig_der) vk = ecdsa.VerifyingKey.from_string(public_key[2:].decode('hex'), curve=ecdsa.SECP256k1) self.assertEquals(vk.verify_digest(sig.decode('hex'), hashToSign.decode('hex')), True) #OP_DUP OP_HASH160 167c74f7491fe552ce9e1912810a984355b8ee07 OP_EQUALVERIFY OP_CHECKSIG if __name__ == '__main__': unittest.main()

找到r和s以及相关的txid之后,根据txid找到raw tx信息,然后根据字段意义,去拆分一下,拆分后如下:
--------------------------------------------------
1BMzWp77j7x3GKDYNbCP3df7YG3UEw1vVE
Data for pubkey: 1BMzWp77j7x3GKDYNbCP3df7YG3UEw1vVE has  273  Txs
Fetching Txs from offset 0

Fetching Txs from offset 50

Fetching Txs from offset 100

Fetching Txs from offset 150

Fetching Txs from offset 200

Fetching Txs from offset 250

6c2d0bbb87350cd18d93ede269817767b84715a6292a022c68b327f704ce486f|b0c07b0c53df67139b216f2fbcfcc8719bd22b93b14f60c017ddfa8979958239|2cfa1babbe34862228db2f4956c60d9e5609de18e2bb1d78fb5993a3acfb0fa1|6c2d0bbb87350cd18d93ede269817767b84715a6292a022c68b327f704ce486f|b0c07b0c53df67139b216f2fbcfcc8719bd22b93b14f60c017ddfa8979958239|5b096f9d74592ab6432b2a852ac007002dbfd8d60ad022681193ca234f2c1284
Address 1BMzWp77j7x3GKDYNbCP3df7YG3UEw1vVE has 1 reused R value!


01000000
02
7b220029d11c6ca705cca0e41f84fd57253e5f709c7cd576bdc99ba0e636b2fd
01000000
8b
48
3045022100b0c07b0c53df67139b216f2fbcfcc8719bd22b93b14f60c017ddfa897995823902202cfa1babbe34862228db2f4956c60d9e5609de18e2bb1d78fb5993a3acfb0fa1
01
41
048eae75d946cf3da9b2b9a7f0e1eb37fd66f2fc46fc3bf7aa527c55ae296bc637f126c0933bee187b20d6792bc10ac360bdc6fb8376eed5757a5d2b568ccf4f54
ffffffff
f3a2dbe2191ca0af68be248599d7373e571d1b78d99c612e92e22d4e6584c6cc
01000000
8b
48
3045022100b0c07b0c53df67139b216f2fbcfcc8719bd22b93b14f60c017ddfa897995823902205b096f9d74592ab6432b2a852ac007002dbfd8d60ad022681193ca234f2c1284
01
41
048eae75d946cf3da9b2b9a7f0e1eb37fd66f2fc46fc3bf7aa527c55ae296bc637f126c0933bee187b20d6792bc10ac360bdc6fb8376eed5757a5d2b568ccf4f54
ffffffff
02
dfc1cfec02000000
1976a914f2163a112beb3cfb78ec47dd8f9ed1cbd1b8211788ac
0f38000000000000
1976a91471a9965c4365a813dbd3d086168803f18435a62988ac
00000000

-------------------------------------------------01000000
m1="01000000027b220029d11c6ca705cca0e41f84fd57253e5f709c7cd576bdc99ba0e636b2fd010000001976a91471a9965c4365a813dbd3d086168803f18435a62988acfffffffff3a2dbe2191ca0af68be248599d7373e571d1b78d99c612e92e22d4e6584c6cc0100000000ffffffff02dfc1cfec020000001976a914f2163a112beb3cfb78ec47dd8f9ed1cbd1b8211788ac0f380000000000001976a91471a9965c4365a813dbd3d086168803f18435a62988ac0000000001000000"



01000000027b220029d11c6ca705cca0e41f84fd57253e5f709c7cd576bdc99ba0e636b2fd01000000
1976a91471a9965c4365a813dbd3d086168803f18435a62988ac
ffffffff
f3a2dbe2191ca0af68be248599d7373e571d1b78d99c612e92e22d4e6584c6cc
01000000
00
ffffffff
02
dfc1cfec02000000
1976a914f2163a112beb3cfb78ec47dd8f9ed1cbd1b8211788ac
0f38000000000000
1976a91471a9965c4365a813dbd3d086168803f18435a62988ac
00000000


z1=0xce66074ef1955fd86a056ff26cdffba6f676f24f796b2366e0c790949a2f04c3
z2=0xb99181d6cc1a14f924516318b2c86225bf2213740edf08f6af581725e01325f1
--------------------------------------------------------

m2="01000000027b220029d11c6ca705cca0e41f84fd57253e5f709c7cd576bdc99ba0e636b2fd0100000000fffffffff3a2dbe2191ca0af68be248599d7373e571d1b78d99c612e92e22d4e6584c6cc010000001976a91471a9965c4365a813dbd3d086168803f18435a62988acffffffff02dfc1cfec020000001976a914f2163a112beb3cfb78ec47dd8f9ed1cbd1b8211788ac0f380000000000001976a91471a9965c4365a813dbd3d086168803f18435a62988ac0000000001000000"

01000000027b220029d11c6ca705cca0e41f84fd57253e5f709c7cd576bdc99ba0e636b2fd0100000000fffffffff3a2dbe2191ca0af68be248599d7373e571d1b78d99c612e92e22d4e6584c6cc010000001976a91471a9965c4365a813dbd3d086168803f18435a62988acffffffff02dfc1cfec020000001976a914f2163a112beb3cfb78ec47dd8f9ed1cbd1b8211788ac0f380000000000001976a91471a9965c4365a813dbd3d086168803f18435a62988ac0000000001000000


我们需要的就是m1和m2,它的拼接是根据txnUtils里的getSignableTxn生成的:
return first + "1976a914" + inputAddr.encode('hex') + "88ac" + rest + "01000000"。
然后根据m1和m2生成z1和z2 ,
z1 = hashlib.sha256(hashlib.sha256(m1.decode('hex')).digest()).digest().encode('hex_codec')
    z2 = hashlib.sha256(hashlib.sha256(m2.decode('hex')).digest()).digest().encode('hex_codec')
    
标红部分就是der_sig1和der_sig2,得到以上四个值后,就可以r-s-z-to-wif里生成wif了










2)、在本地获得原始transaction:


# -*- coding:utf-8 -*-
'''
Created on 2018年12月14日

@author: user
'''
import os, sys
import logging
#from importlib import reload
from bitcoinrpc.authproxy import AuthServiceProxy
import txnUtils,keyUtils
reload(sys) 
sys.setdefaultencoding('utf8')
import base58
import hashlib,json

   
def process_tx_addr_file(tx_file):
    with open(tx_file,'r') as f:
        cont = f.readline().strip()
        while cont:
            arr = cont.split(',')
            tx_id = arr[0]
            #addr_param = arr[4]
            
            raw_tx = rpc_conn.getrawtransaction(tx_id)
            str_tx =''
            try:
                str_tx = txnUtils.detail_parse_tx(tx_id,raw_tx)
            except Exception as e:
                #logger1.error(tx_id+"|"+addr_param)
                cont = f.readline().strip()
                continue
            txid= str_tx["txid"]
            version= str_tx["version"]
            input_count= str_tx["input_count"]
            inputs= str_tx["inputs"]
            output_count= str_tx["output_count"]
            outputs= str_tx["outputs"]
            block_lock_time= str_tx["block_lock_time"]
            
            for input in inputs:
                prev_output_hash = input["pre_output_hash"]
                pre_output_index = input["pre_output_index"]
                script_length = input["script_length"]
                script_sig = input["script_sig"]
                sig = input["sig"]
                pub = input["pub"]
                addr = input["addr"]
                r_value  = input["r"]
                s_value = input["s"]
                sequence = input["sequence"]
                #first = str(version)+str(input_count)+str(prev_output_hash)+str(pre_output_index)
                #print('my_first:'+first)
                log_cont = str(tx_id)+"|"+str(addr)+"|"+str(r_value)+"|"+str(s_value)+"|"
                logger.info(log_cont)
            
            cont = f.readline().strip()

if __name__ == '__main__':
    
    rpc_user = 'admin'
    rpc_pwd = 'Admin'
    rpc_host = '127.0.0.1'
    rpc_port = 8332
    url = "http://" + rpc_user + ":" + rpc_pwd + "@" + rpc_host + ":" + str(rpc_port)
    rpc_conn = AuthServiceProxy(url)
    
    txid_demo = '9ec4bc49e828d924af1d1029cacf709431abbde46d59554b62bc270e3b29c4b1'
    raw_tx = rpc_conn.getrawtransaction(txid_demo)
    print(raw_tx)
    
    tx_169="6c2d0bbb87350cd18d93ede269817767b84715a6292a022c68b327f704ce486f"
    raw_tx = rpc_conn.getrawtransaction(tx_169)
    print(raw_tx)
    
    ''''
    m1='01000000027b220029d11c6ca705cca0e41f84fd57253e5f709c7cd576bdc99ba0e636b2fd010000001976a914f2163a112beb3cfb78ec47dd8f9ed1cbd1b8211788acfffffffff3a2dbe2191ca0af68be248599d7373e571d1b78d99c612e92e22d4e6584c6cc0100000000ffffffff02dfc1cfec020000001976a914f2163a112beb3cfb78ec47dd8f9ed1cbd1b8211788ac0f380000000000001976a91471a9965c4365a813dbd3d086168803f18435a62988ac0000000001000000'
    m2='01000000027b220029d11c6ca705cca0e41f84fd57253e5f709c7cd576bdc99ba0e636b2fd0100000000fffffffff3a2dbe2191ca0af68be248599d7373e571d1b78d99c612e92e22d4e6584c6cc010000001976a91471a9965c4365a813dbd3d086168803f18435a62988acffffffff02dfc1cfec020000001976a914f2163a112beb3cfb78ec47dd8f9ed1cbd1b8211788ac0f380000000000001976a91471a9965c4365a813dbd3d086168803f18435a62988ac0000000001000000'
    
    z1 = hashlib.sha256(hashlib.sha256(m1.decode('hex')).digest()).digest().encode('hex_codec')
    z2 = hashlib.sha256(hashlib.sha256(m2.decode('hex')).digest()).digest().encode('hex_codec')
    print('-'*100)
    print z1
    print z2
    '''


有点儿混乱,但能用,先记录一下,回头再细思考一下。