发现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
'''
有点儿混乱,但能用,先记录一下,回头再细思考一下。