How to add signed certificates to a bitcoin bip70 payment message? python - python

References:
https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki
https://github.com/aantonop/bitcoinbook/blob/develop/selected%20BIPs/bip-0070.mediawiki#paymentdetailspaymentrequest
message PaymentRequest {
##optional uint32 payment_details_version = 1 [default = 1]; # 'x509+sha256' in this case.
##optional string pki_type = 2 [default = "none"];
optional bytes pki_data = 3;
##required bytes serialized_payment_details = 4;
optional bytes signature = 5;
}
The ones with ## at the front are not a problem, I've solved them already.
optional bytes pki_data wants a byte encoded version of 'x509+sha256' so...
x509_bytes = open('/path/to/x509.der', 'rb').read()
pki_data = hashib.sha256(x509_bytes)
Is the above correct?
Next optional bytes signature, 'digital signature over a hash of the protocol buffer serialized variation of the PaymentRequest message'
I'm not sure how to achieve this so any suggestions would be greatly appreciated.
Finally I have...
message X509Certificates {
repeated bytes certificate = 1;
}
repeated bytes certificate 'Each certificate is a DER [ITU.X690.1994] PKIX certificate value. The certificate containing the public key of the entity that digitally signed the PaymentRequest MUST be the first certificate.'
I only have the one cert I got from the comodo root authority so I think I only need to supply the raw byte data of the cert to satisfy this one which already exists in the form of x509_bytes above, so...
repeated bytes certificate = x509_bytes
Am I close??
Also I notice that repeated bytes certificate comes after optional bytes signature but shouldn't I deal with that before message PaymentRequest so that I can serialise it into my http response somehow?
EDIT:
For what it's worth I'm aware that I need to import, instantiate and in some cases serialise these methods before sending them as a request/response but what I'm looking for are the methods on how to manipulate and supply the information required.
Thanks :)

To add PKI data to the PaymentRequest object:
pki_data = X509Certificates()
certificates = [your_cert_der_data, root_cert_der_data]
for cert in certificates:
pki_data.certificate.append(cert)
request.pki_data = pki_data.SerializeToString()
To add a signature (with pycrypto):
from Crypto.Hash import SHA256
from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA
# At this moment request object must contain serialized_payment_details, pki_type and pki_data
request.signature = "" # Add empty signature
request_hash = SHA256.new(request.SerializeToString())
private_key = RSA.importKey(private_key_der_data)
signer = PKCS1_v1_5.new(private_key)
request.signature = signer.sign(request_hash)
result = request.SerializeToString()

Related

Replicating HMAC from C# to Python

I'm working with an API provided by a client, who provided sample code on authorizing via C#, but my company works in Python. They authorize via HMAC, and following their sample code (on .net fiddle), I finally got to the point where our byte key and byte message match those of the C# call.
However, when we get to this in their code:
using (HMACSHA256 sha = new HMACSHA256(secretKeyByteArray))
{
// Sign the request
byte[] signatureBytes = sha.ComputeHash(signature);
Where our equivalent is
signature_hmac = hmac.new(
base64.b64decode(secretKey),
bytes(message, "utf8"),
digestmod=hashlib.sha256,
)
The byte value of their signatureBytes doesn't match with the byte value of our signature_hmac.digest(). If using the same hashing libraries, and the byte values of the inputs match, we should get the same result, right?
To make sure, when I say byte values match, the value of base64.b64decode(secretKey) (Python) matched var secretKeyByteArray = Convert.FromBase64String(secretKey); (C#).
Worked for me, make sure your input arrays are EXACTLY the same on both. I think you might be missing a base64 decode or encoding the strings differently.
Here's my 2 tests:
secretKey = "wxyz".encode()
message = "abcd".encode()
signature_hmac = hmac.new(
secretKey,
message,
digestmod=hashlib.sha256,
)
x = signature_hmac.hexdigest()
and C#
byte[] signatureBytes;
byte[] secretKeyByteArray = Encoding.UTF8.GetBytes("wxyz");
byte[] signature = Encoding.UTF8.GetBytes("abcd");
using (HMACSHA256 sha = new HMACSHA256(secretKeyByteArray))
{
signatureBytes = sha.ComputeHash(signature);
};
string x = Convert.ToHexString(signatureBytes);

Same public key differs with the way to get it

I'm writting a little program using SSL sockets. A client sends values to a server and when the server gets the value it checks the client's public key to make sure he's expected to send something. So at first the server is getting all the public keys like this :
cert = f.read()
crtObj = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
pubKeyObject = crtObj.get_pubkey()
pubKeyString = crypto.dump_publickey(crypto.FILETYPE_PEM,pubKeyObject)
with this method, the public key is :
b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7GIzek5JgfFzFCGwnx7X\ncE4QULV/9uyoGgd9HbHYyYcItEcSPU39ORXCrNQGxh09k4oFPBYntjD2gIORF8V4\n6EAC10bFaT18OuM1F/37v+K/+BuvCDTqcS9Y0CRalwPFVYB+yttvZ8fnvO2l/TxF\nsLmZh0yY/ajaHxey/ppUQycGy4xA8XD6VlWFM7+I0t/19rrLN9iMFSym/TgYpBbn\nxyZel8rMW/ACS09nSprEu1BuI+myhhej+cuy3wU8byRTwANpqHxsx5cTwp642TVx\nBKbuO8GHAzEKcrFZnrKcsXr9emWV5ouYiVzehOT4Pd3I2W8qSy6x/Ovv/iS3ojT4\ndQIDAQAB\n-----END PUBLIC KEY-----\n'
and when the server wants to get it from the socket connection like this :
test1 = writer.get_extra_info('ssl_object')
der =test1.getpeercert(binary_form=True)
test = test1.getpeercert()
cert = x509.Certificate.load(der)
pubkey= cert.public_key.unwrap()
print(pem.armor("PUBLIC KEY", pubkey.contents).decode("ASCII"))
the public key printed is :
-----BEGIN PUBLIC KEY-----
AoIBAQDsYjN6TkmB8XMUIbCfHtdwThBQtX/27KgaB30dsdjJhwi0RxI9Tf05FcKs
1AbGHT2TigU8Fie2MPaAg5EXxXjoQALXRsVpPXw64zUX/fu/4r/4G68INOpxL1jQ
JFqXA8VVgH7K229nx+e87aX9PEWwuZmHTJj9qNofF7L+mlRDJwbLjEDxcPpWVYUz
v4jS3/X2uss32IwVLKb9OBikFufHJl6Xysxb8AJLT2dKmsS7UG4j6bKGF6P5y7Lf
BTxvJFPAA2mofGzHlxPCnrjZNXEEpu47wYcDMQpysVmespyxev16ZZXmi5iJXN6E
5Pg93cjZbypLLrH86+/+JLeiNPh1AgMBAAE=
-----END PUBLIC KEY-----
So I don't know if it's a matter of format or if it's really not the same public key I'm getting... but it should be.
Sorry for the long post and thank you very much for reading.
Most(if not all) base64 encoding variants use A-Z, a-z, 0-9 characters to represent data the total is 62 character and some base64 encodings varies in the last 2 characters but most of them use + and / which will complete 64. So the above 2 keys seems to be completely different and the second key is also padded because of = sign at end. The standard base64 encoding is RFC 4648 variant which use + and / and make padding as optional not mandatory like RFC 4880 openpgp base64 encoding.
It was in fact the same keys but the way of getting it was wrong : here the right one from the ssl object :
test1 = writer.get_extra_info('ssl_object')
der =test1.getpeercert(binary_form=True)
crtObj = crypto.load_certificate(crypto.FILETYPE_ASN1, der)
pubKeyObject = crtObj.get_pubkey()
pubKeyString = crypto.dump_publickey(crypto.FILETYPE_PEM, pubKeyObject)

How use public key with pyOpenSSL for verify a signed message?

I try to use pyOpenSSL for signed a data, I create key pair (private and publique) and certificate.
I'm a beginner with this technology, I use OpenSSl, but if you have suggestions for generate a signed message with private and public key in python, I'm take !
I want to use RSA and DSA algorithm for my tests.
I find m2Crypto, pyCrypto and other. I do not know what is the best for this.
gnupg for python and pyOpenSSl are more recent visibly.
I used function for signed a message with my private key, and I verify the data.
But when I see the function for verify the signature, in parameters I need :
private key, signature, data and digest type.
I do not know where I am wrong in this code, I find some examples, but I do not understand how this can work because the first parameters for the verify function is a X509 object "certificate is a X509 instance corresponding to the private key which generated the signature." and the second is the signature generated with the private key..
This code work perfectly with the private key :
from OpenSSL import crypto
_k = crypto.PKey()
_cert = crypto.X509()
# Create keys
_k.generate_key(crypto.TYPE_RSA, 2048)
# Add argument for create certificate
_cert.gmtime_adj_notBefore(0)
_cert.gmtime_adj_notAfter(0*365*24*60*60) #10 years expiry date
_cert.set_pubkey(_k)
_cert.sign(_k, 'sha256')
# Create key's file
with open("public_key.pem",'w') as f:
f.write(crypto.dump_publickey(crypto.FILETYPE_PEM, _k))
with open("private_key.pem",'w') as f:
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, _k))
with open("certificate.pem",'w') as f:
f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, _cert))
#-------------------------------------------------------------------------------
# Open key and load in var
with open("private_key.pem",'r') as f:
priv_key = crypto.load_privatekey(crypto.FILETYPE_PEM, f.read())
with open("public_key.pem",'r') as f:
pub_key = crypto.load_publickey(crypto.FILETYPE_PEM, f.read())
with open("certificate.pem",'r') as f:
cert = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
# sign message 'hello world' with private key and certificate
sign = crypto.sign(priv_key, "hello world", 'sha256')
print crypto.verify(cert, sign, "hello world", 'sha256')
So, my question is, how use the public key for verify the data ?
If Bob give a public key to alice, How it checks the message with this public key ?
You have a idea ?
Thanks a lot,
Romain
I found a answer in this post.
from OpenSSL.crypto import load_publickey, FILETYPE_PEM, verify, X509
# ... code ...
x509 = X509()
x509.set_pubkey(pub_key)
# ... code ...
print verify(x509, sign, sha, 'sha256')
I'm wondering.. When I get a message, I use the public key for authenticating the transmitter, but why I have to use the signature to do the verification ? To generate the signature, I need the private key ..
Is there something I did not understand in the use of the library ?
ok I understood my mistake : Cryptography Digital signatures
source

CSR submitted to IIS CA fails due to ASN1 value

I have generated a private key / CSR from pyOpenSSL - code snippet below:
Key:
key = crypto.PKey()
key.generate_key(type, bits)
if os.path.exists(_keyfile):
print "Certificate file exists, aborting."
print " ", _keyfile
sys.exit(1)
else:
f = open(_keyfile, "w")
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key))
f.close()
return key
CSR:
req = crypto.X509Req()
# Return an X509Name object representing the subject of the certificate.
req.get_subject().countryName = country
req.get_subject().stateOrProvinceName = state
req.get_subject().localityName = location
req.get_subject().organizationName = organisation
req.get_subject().organizationalUnitName = organisational_unit
req.get_subject().CN = nodename
# Add in extensions
#base_constraints = ([
# crypto.X509Extension("keyUsage", False, "Digital Signature, Non Repudiation, Key Encipherment"),
# crypto.X509Extension("basicConstraints", False, "CA:FALSE"),
#])
#x509_extensions = ([])
x509_extensions = []
# If there are SAN entries, append the base_constraints to include them.
if ss:
san_constraint = crypto.X509Extension("subjectAltName", False, ss)
x509_extensions.append(san_constraint)
req.add_extensions(x509_extensions)
# Set the public key of the certificate to pkey.
req.set_pubkey(key)
# Sign the certificate, using the key pkey and the message digest algorithm identified by the string digest.
req.sign(key, "sha1")
# Dump the certificate request req into a buffer string encoded with the type type.
if os.path.exists(_csrfile):
print "Certificate file exists, aborting."
print " ", _csrfile
sys.exit(1)
else:
f = open(_csrfile, "w")
f.write(crypto.dump_certificate_request(crypto.FILETYPE_PEM, req))
f.close()
The error that I get back from the IIS CA is:
ASN1 bad tag value met. 0x8009310b (ASN: 267)
According to Microsoft this is caused by:
This behavior occurs when certificate request is stored in a file in Unicode encoding. Microsoft Certificate Services do not support Unicode-encoded files request files. Only ANSI encoding is supported.
I know that if I generate a CSR from openssl on the command line it is accepted and issued by the IIS CA RESTful webservice without error.
I want to know if there is some way I can generate 'ANSI' encoded files from pyOpenSSL - I am not sure if it is the keyfile or the CSR that is signed with the keyfile that is causing the issues.
I have solved it with the help of this stackoverflow question thanks to #yodatg.
The problem occurs due to a bug in pyOpenSSL that has been fixed.
By issuing:
openssl asn1parse -in certificates/cert.csr
I could see the ASN1 value:
8:d=2 hl=2 l= 1 prim: INTEGER :01
In a working CSR it looks like this:
8:d=2 hl=2 l= 1 prim: INTEGER :00
I then changed my code to include a set_version call on the req object prior to signing:
#set version - IIS CA required this
req.set_version(0)
# Set the public key of the certificate to pkey.
req.set_pubkey(priv_key)
This is now resolved.

Create compatible Java "RSA" signature using a PKCS#8 encoded private key

I have pkcs8_rsa_private_key file which generate by openssl from a rsa_private_key.pem file.
I need make a signature by the private key in python, make the same signature with below java code.
public static final String SIGN_ALGORITHMS = "SHA1WithRSA";
public static String sign(String content, String privateKey) {
String charset = "utf-8";
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
Base64.decode(privateKey));
KeyFactory keyf = KeyFactory.getInstance("RSA");
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
java.security.Signature signature = java.security.Signature
.getInstance(SIGN_ALGORITHMS);
signature.initSign(priKey);
signature.update(content.getBytes(charset));
byte[] signed = signature.sign();
return Base64.encode(signed);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
PKCS#8 defines a way to encode and transport secret keys and it is not specific to OpenSSL; PKCS#1 defines a way to use an RSA key (no matter how it was loaded into your application, with PKCS#8 or not) to carry out and verify digital signature on data.
The piece of code you have does three things:
It decodes Base64 into PKCS#8
It decodes PKCS#8 into the actual key in memory (mind you may need to provide a passphrase here)
It performs PKCS#1 v1.5 signature using SHA-1 using said key
It encodes the signature in Base64
The example for PKCS#1 v1.5 signing in the API of PyCrypto does exactly steps #2 and #3.
from Crypto.Signature import PKCS1_v1_5 as pk
from Crypto.Hash import SHA
class Crypt(object):
pkcs8_private_key = RSA.importKey(open('pkcs8_rsa_private_key.pem', 'r').read())
def rsa_sign(cls, des_reqdata):
"""
#:param reqdata: request reqData
"""
h = SHA.new(des_reqdata)
signer = pk.new(cls.pkcs8_private_key)
signature = signer.sign(h)
return base64.b64encode(signature)

Categories