forked from gavinandresen/paymentrequest
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 93926d4
Showing
10 changed files
with
2,719 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
To compile and run, do something like this: | ||
|
||
$ g++ -g -o invoice-create -lssl -lcrypto -I/opt/local/include -L/opt/local/lib -lprotobuf invoice-create.cpp invoices.pb.cc && ./invoice-create | ||
File written successfully, see demo.bitcoin-invoice | ||
You can check it by running invoice-verify | ||
|
||
$ g++ -o invoice-verify -lssl -lcrypto -I/opt/local/include -L/opt/local/lib -lprotobuf invoice-verify.cpp invoices.pb.cc && ./invoice-verify | ||
Invoice is valid! Signed by www.plan99.net | ||
Label: Bobs Widget Emporium | ||
|
||
Files: | ||
- ca-bundle-startcom.pem: A bunch of trusted root authorities provided by StartCom Ltd. | ||
- pubcert.pem: An expired certificate issued to me for plan99.net | ||
- privkey.pem: The private key, this originally came appended to pubcert.pem and I split them. | ||
- sub.class1.server.ca.pem: Intermediate cert for StartCom certificates | ||
|
||
The intermediate cert is not an unusual requirement for SSL authorities. You are expected | ||
to provide it to your web server software along with your regular certificate so the chain | ||
can be formed back to the root CA. |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// Writing out a demo invoice file. Loads certs from PEM files | ||
// converts them to binary DER form and writes them out to a | ||
// protocol buffer. | ||
|
||
// Apple has deprecated OpenSSL in latest MacOS, shut up compiler warnings about it. | ||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" | ||
|
||
#include <string> | ||
#include <iostream> | ||
#include <fstream> | ||
#include <assert.h> | ||
|
||
#include <openssl/bio.h> | ||
#include <openssl/ssl.h> | ||
#include <openssl/pem.h> | ||
#include <openssl/x509.h> | ||
#include <openssl/x509_vfy.h> | ||
#include <openssl/evp.h> | ||
|
||
#include "invoices.pb.h"; | ||
|
||
using std::string; | ||
|
||
// Returns the files contents as a byte array. | ||
string load_file(const char *path) { | ||
string result; | ||
std::ifstream cert_file(path); | ||
result.assign(std::istreambuf_iterator<char>(cert_file), std::istreambuf_iterator<char>()); | ||
return result; | ||
} | ||
|
||
// Must be freed with BIO_free. | ||
BIO *string_to_bio(const string &str) { | ||
return BIO_new_mem_buf((void*)str.data(), str.size()); | ||
} | ||
|
||
// Take textual PEM data (concatenated base64 encoded x509 data with separator markers) | ||
// and return an X509 object suitable for verification or use. | ||
X509 *parse_pem_cert(string cert_data) { | ||
// Parse it into an X509 structure. | ||
BIO *bio = string_to_bio(cert_data); | ||
X509 *cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); | ||
assert(cert); | ||
BIO_free(bio); | ||
return cert; | ||
} | ||
|
||
string x509_to_der(X509 *cert) { | ||
unsigned char *buf = NULL; | ||
int buflen = i2d_X509(cert, &buf); | ||
string data((char*)buf, buflen); | ||
return data; | ||
} | ||
|
||
int main(int argc, char **argv) { | ||
SSL_library_init(); | ||
ERR_load_BIO_strings(); | ||
SSL_load_error_strings(); | ||
OpenSSL_add_all_algorithms(); | ||
// Verify that the version of the library that we linked against is | ||
// compatible with the version of the headers we compiled against. | ||
GOOGLE_PROTOBUF_VERIFY_VERSION; | ||
|
||
// Load my expired demo cert. | ||
X509 *my_cert = parse_pem_cert(load_file("pubcert.pem")); | ||
// Load StartComs intermediate cert. A real tool would let you specify all intermediate | ||
// certs you need to reach a root CA, or load from a config file or whatever. | ||
X509 *intermediate_cert = parse_pem_cert(load_file("sub.class1.server.ca.pem")); | ||
|
||
// Build the invoice and add the certs to it. | ||
Invoice invoice; | ||
invoice.set_label("Bobs Widget Emporium"); | ||
IdentityData *id_data = invoice.mutable_identity_data(); | ||
id_data->add_cert_chain(x509_to_der(intermediate_cert)); | ||
id_data->add_cert_chain(x509_to_der(my_cert)); | ||
Output *output = invoice.add_outputs(); | ||
output->set_value(1000); // 1000 satoshis | ||
output->set_script("this should obviously be binary data"); | ||
|
||
// Serialize the invoice in preparation for signing. | ||
string data_to_sign; | ||
invoice.SerializeToString(&data_to_sign); | ||
|
||
// Now we want to sign the invoice using the privkey that matches the cert. | ||
// There are many key formats and some keys can be password protected. We gloss | ||
// over all of that here and just assume unpassworded PEM. | ||
BIO *pkey = string_to_bio(load_file("privkey.pem")); | ||
EVP_PKEY *privkey = PEM_read_bio_PrivateKey(pkey, NULL, NULL, NULL); | ||
assert(privkey); | ||
|
||
EVP_MD_CTX ctx; | ||
EVP_MD_CTX_init(&ctx); | ||
assert(EVP_SignInit_ex(&ctx, EVP_sha256(), NULL)); | ||
assert(EVP_SignUpdate(&ctx, data_to_sign.data(), data_to_sign.size())); | ||
unsigned char *signature = new unsigned char[EVP_PKEY_size(privkey)]; | ||
unsigned int actual_signature_len; | ||
assert(EVP_SignFinal(&ctx, signature, &actual_signature_len, privkey)); | ||
|
||
// Now we have our signature, let's check it actually verifies. | ||
EVP_PKEY *pubkey = X509_get_pubkey(my_cert); | ||
EVP_MD_CTX_init(&ctx); | ||
assert(EVP_VerifyInit_ex(&ctx, EVP_sha256(), NULL)); | ||
assert(EVP_VerifyUpdate(&ctx, data_to_sign.data(), data_to_sign.size())); | ||
assert(EVP_VerifyFinal(&ctx, signature, actual_signature_len, pubkey)); | ||
|
||
// We got here, so the signature is self-consistent. Put it into the protobuf. | ||
string sigstr((char*)signature, actual_signature_len); | ||
id_data->set_signature(sigstr); | ||
|
||
std::fstream outfile("demo.bitcoin-invoice", std::ios::out | std::ios::trunc | std::ios::binary); | ||
assert(invoice.SerializeToOstream(&outfile)); | ||
printf("File written successfully, see demo.bitcoin-invoice\n"); | ||
printf("You can check it by running invoice-verify\n"); | ||
|
||
google::protobuf::ShutdownProtobufLibrary(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// Demo of how to verify X509 cert chain without an SSL connection. | ||
|
||
// Apple has deprecated OpenSSL in latest MacOS, shut up compiler warnings about it. | ||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" | ||
|
||
#include <string> | ||
#include <fstream> | ||
#include <assert.h> | ||
|
||
#include <openssl/bio.h> | ||
#include <openssl/ssl.h> | ||
#include <openssl/pem.h> | ||
#include <openssl/x509.h> | ||
#include <openssl/x509_vfy.h> | ||
|
||
#include "invoices.pb.h"; | ||
|
||
using std::string; | ||
|
||
// Take binary DER data and return an X509 object suitable for verification or use. | ||
X509 *parse_der_cert(string cert_data) { | ||
const unsigned char *data = (const unsigned char *)cert_data.data(); | ||
X509 *cert = d2i_X509(NULL, &data, cert_data.size()); | ||
assert(cert); | ||
return cert; | ||
} | ||
|
||
int main(int argc, char **argv) { | ||
SSL_library_init(); | ||
ERR_load_BIO_strings(); | ||
SSL_load_error_strings(); | ||
OpenSSL_add_all_algorithms(); | ||
// Verify that the version of the library that we linked against is | ||
// compatible with the version of the headers we compiled against. | ||
GOOGLE_PROTOBUF_VERIFY_VERSION; | ||
|
||
// Load all the cert authorities. | ||
// The cert store will have all of the certificates for the trusted root authorities. | ||
X509_STORE *cert_store = X509_STORE_new(); | ||
// This tells the store to find certificates by loading them from a file. | ||
// The X509_LOOKUP_file() method here is actually just telling OpenSSL | ||
// how to do it, it doesn't actually look anything up despite the name. | ||
X509_LOOKUP *lookup = X509_STORE_add_lookup(cert_store, X509_LOOKUP_file()); | ||
// Load all the root authority certificates. This file was retrieved from | ||
// http://www.startssl.com/certs/ca-bundle.pem on Nov 18th 2012. | ||
assert(X509_LOOKUP_load_file(lookup, "ca-bundle-startcom.pem", X509_FILETYPE_PEM)); | ||
|
||
// Load the invoice file. | ||
std::ifstream infile("demo.bitcoin-invoice", std::ios::in | std::ios::binary); | ||
Invoice invoice; | ||
assert(invoice.ParseFromIstream(&infile)); | ||
|
||
// Dump in raw text format, obviously this is mostly useless as bulk of | ||
// the data is binary. | ||
// printf("%s\n", invoice.DebugString().c_str()); | ||
|
||
// Load the certs from the invoice. | ||
const IdentityData &id_data = invoice.identity_data(); | ||
std::vector<X509*> certs; | ||
for (int i = 0; i < id_data.cert_chain_size(); i++) { | ||
X509 *cert = parse_der_cert(id_data.cert_chain(i)); | ||
certs.push_back(cert); | ||
} | ||
assert(certs.size() > 0); | ||
|
||
// The last cert is the signing cert, the rest are untrusted certs that chain | ||
// to a valid root authority. OpenSSL needs them separately. | ||
STACK_OF(X509) *chain = sk_X509_new_null(); | ||
for (int i = 0; i < certs.size() - 1; i++) { | ||
sk_X509_push(chain, certs[i]); | ||
} | ||
X509 *signing_cert = certs[certs.size() - 1]; | ||
|
||
// Now create a "store context", which is a single use object for checking, | ||
// load the signing cert into it and verify. | ||
X509_STORE_CTX *store_ctx = X509_STORE_CTX_new(); | ||
assert(X509_STORE_CTX_init(store_ctx, cert_store, signing_cert, chain)); | ||
// Override the verification time so Mikes expired cert can be used as a demo. | ||
// The verify param ownership is taken by the store context. Set this to false | ||
// to see "cert expired" as an error. Don't just comment out the set_time call | ||
// as setting an empty verify param apparently causes other issues. | ||
if (true) { | ||
X509_VERIFY_PARAM *vpm = X509_VERIFY_PARAM_new(); | ||
X509_VERIFY_PARAM_set_time(vpm, static_cast<time_t>(1293843600)); | ||
X509_STORE_CTX_set0_param(store_ctx, vpm); | ||
} | ||
// Now do the verification! | ||
int result = X509_verify_cert(store_ctx); | ||
X509_NAME *certname = X509_get_subject_name(signing_cert); | ||
if (result < 0) { | ||
int error = X509_STORE_CTX_get_error(store_ctx); | ||
printf("%d: %s\n", error, X509_verify_cert_error_string(error)); | ||
exit(1); | ||
} | ||
|
||
// The cert is valid. Now we need to check the signature. Start by deleting it | ||
// from the protobuf. | ||
string signature = id_data.signature(); | ||
invoice.mutable_identity_data()->clear_signature(); | ||
string data_to_verify; | ||
invoice.SerializeToString(&data_to_verify); | ||
|
||
EVP_MD_CTX ctx; | ||
EVP_PKEY *pubkey = X509_get_pubkey(signing_cert); | ||
EVP_MD_CTX_init(&ctx); | ||
assert(EVP_VerifyInit_ex(&ctx, EVP_sha256(), NULL)); | ||
assert(EVP_VerifyUpdate(&ctx, data_to_verify.data(), data_to_verify.size())); | ||
assert(EVP_VerifyFinal(&ctx, (const unsigned char*)signature.data(), signature.size(), pubkey)); | ||
|
||
// OpenSSL API for getting human printable strings from certs is baroque. | ||
int textlen = X509_NAME_get_text_by_NID(certname, NID_commonName, NULL, 0); | ||
char *website = new char[textlen + 1]; | ||
assert(X509_NAME_get_text_by_NID(certname, NID_commonName, website, textlen + 1) == textlen); | ||
printf("Invoice is valid! Signed by %s\n", website); | ||
printf("Label: %s\n", invoice.label().c_str()); | ||
|
||
// Avoid reported memory leaks. | ||
delete website; | ||
X509_STORE_CTX_free(store_ctx); | ||
for (int i = 0; i < certs.size(); i++) | ||
X509_free(certs[i]); | ||
X509_STORE_free(cert_store); | ||
google::protobuf::ShutdownProtobufLibrary(); | ||
} | ||
|
||
|
||
|
Oops, something went wrong.