├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── bio ├── bio.go ├── bio.swig ├── bio_suite_test.go └── bio_test.go ├── crypto ├── crypto.go ├── crypto.swig ├── crypto_suite_test.go └── crypto_test.go ├── digest ├── digest.go ├── digest.swig ├── digest_suite_test.go ├── digest_test.go ├── hash.go └── hash_test.go ├── examples ├── crypto.go ├── httpsclient.go └── httpstransport.go ├── include ├── evp_typ.i └── ossl_typemaps.i ├── rand ├── rand.go ├── rand.swig ├── rand_suite_test.go ├── rand_test.go ├── read.go └── read_test.go ├── scripts ├── install_openssl.sh ├── install_swig.sh └── scripts_profile ├── ssl ├── context.go ├── httpsclient.go ├── httpsclient_test.go ├── httpsserver.go ├── httpsserver_test.go ├── ssl.go ├── ssl.swig ├── ssl_suite_test.go ├── ssl_test.go └── tests │ ├── certs │ ├── ca │ │ ├── ca.pem │ │ ├── ca.srl │ │ └── privkey.pem │ ├── client │ │ ├── client.key │ │ ├── client.pem │ │ └── client.req │ └── server │ │ ├── server.key │ │ ├── server.pem │ │ └── server.req │ └── httpsserver.go └── x509 ├── helpers_test.go ├── testcerts ├── github.crt └── google.crt ├── x509.go ├── x509.swig ├── x509_suite_test.go └── x509_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore bio file used during testing 2 | bio/*.out 3 | 4 | # Ignore httpsserver binary 5 | ssl/tests/httpsserver 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis configuration file for BlueMix OpenSSL Wrapper for Go 2 | language: go 3 | 4 | go: 5 | - 1.4.2 6 | 7 | env: 8 | - PATH=$PATH:/usr/local/bin:/tmp/deps/bin LD_LIBRARY_PATH=/tmp/deps/ssl/lib:$LD_LIBRARY_PATH LD_RUN_PATH=/tmp/deps/ssl/lib:$LD_RUN_PATH 9 | 10 | before_install: 11 | - ./scripts/install_swig.sh /tmp/deps 12 | - ./scripts/install_openssl.sh /tmp/deps 13 | - go get github.com/onsi/gomega 14 | - go get github.com/onsi/ginkgo 15 | - go get github.com/gorilla/mux 16 | - go get golang.org/x/tools/cmd/cover 17 | - pip install --user codecov 18 | 19 | # script: 20 | # - go test -v -coverprofile=coverage.txt -covermode=atomic ./... 21 | 22 | after_success: 23 | - codecov 24 | # - bash <(curl -s https://codecov.io/bash) 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for BlueMix OpenSSL Wrapper for Go 2 | 3 | PACKAGES=crypto ssl bio digest rand 4 | 5 | all: $(PACKAGES) run_unit_tests 6 | 7 | run_unit_tests: 8 | # rm -rf .cover 9 | # mkdir .cover 10 | for PKG in $(PACKAGES) ; do \ 11 | FULLPKG=github.com/IBM-Bluemix/golang-openssl-wrapper/$$PKG ; \ 12 | go test -v -coverprofile=$$PKG/coverage.txt -covermode=atomic $$FULLPKG ; done 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # golang-openssl-wrapper 2 | 3 | OpenSSL wrapper for go 4 | 5 | To use: 6 | 7 | import "github.com/IBM-Bluemix/golang-openssl-wrapper/crypto" // For encryption, e.g. EVP 8 | import "github.com/IBM-Bluemix/golang-openssl-wrapper/ssl" // TLS 9 | import "github.com/IBM-Bluemix/golang-openssl-wrapper/rand" // PRNG 10 | import "github.com/IBM-Bluemix/golang-openssl-wrapper/digest" // Message-digest (hash) functions 11 | import "github.com/IBM-Bluemix/golang-openssl-wrapper/bio" // OpenSSL-specific I/O handling 12 | 13 | This library is being actively developed. It provides access to much of the OpenSSL APIs for cryptography (libcrypto) and TLS (libssl). We do not plan to provide the complete API at first, but if there is a function you need, please open an issue or submit a PR. The wrapper is built on `swig`, so you will need to work directly with the `.swig` files to add functionality. 14 | 15 | If you submit a pull request, please be sure to include complete unit test coverage (we use `ginkgo` and `gomega`, which sit on top of golang's native testing facility), or your PR will be declined. 16 | -------------------------------------------------------------------------------- /bio/bio.go: -------------------------------------------------------------------------------- 1 | package bio 2 | // #cgo CFLAGS: -I/usr/local/ssl/include 3 | // #cgo LDFLAGS: -L /usr/local/ssl/lib -lssl -lcrypto 4 | import "C" 5 | -------------------------------------------------------------------------------- /bio/bio.swig: -------------------------------------------------------------------------------- 1 | %module bio 2 | 3 | %{ 4 | #include 5 | #include 6 | %} 7 | 8 | %include "typemaps.i" 9 | %include "../include/ossl_typemaps.i" 10 | 11 | /* General BIO */ 12 | BIO_METHOD * BIO_s_mem(void); 13 | BIO_METHOD * BIO_s_file(void); 14 | 15 | extern int BIO_free(BIO *a); 16 | extern void BIO_free_all(BIO *a); 17 | extern BIO *BIO_new(BIO_METHOD *method); 18 | // extern int BIO_make_bio_pair(BIO *bp1, BIO *bp2); 19 | 20 | /* File */ 21 | extern BIO *BIO_new_file(const char *filename, const char *mode); 22 | extern int BIO_puts(BIO *bp, const char *buf); 23 | 24 | int BIO_read_filename(BIO *b, char *name); 25 | int BIO_write_filename(BIO *b, char *name); 26 | int BIO_append_filename(BIO *b, char *name); 27 | int BIO_rw_filename(BIO *b, char *name); 28 | 29 | BIO *BIO_new(BIO_METHOD *method); 30 | int BIO_free(BIO *a); 31 | // int BIO_make_bio_pair(BIO *bp1, BIO *bp2); 32 | int BIO_puts(BIO *bp, const char *buf); 33 | int BIO_printf(BIO *bio, const char *format, ...); 34 | long BIO_seek(BIO *bp, int ofs); 35 | long BIO_flush(BIO *bp); 36 | int BIO_reset(BIO *b); 37 | 38 | %apply char *CHARBUF { char *outbuf }; 39 | int BIO_gets(BIO *bp, char *outbuf, int size); 40 | 41 | %apply char *CHARBUF { void *buf }; 42 | int BIO_read(BIO *b, void *buf, int len); 43 | 44 | %apply void *VOIDSTRINGBUF { const void *buf }; 45 | int BIO_write(BIO *b, const void *buf, int len); 46 | 47 | 48 | /* File storage */ 49 | BIO_METHOD * BIO_s_file(void); 50 | BIO *BIO_new_file(const char *filename, const char *mode); 51 | 52 | int BIO_read_filename(BIO *b, char *name); 53 | int BIO_write_filename(BIO *b, char *name); 54 | int BIO_append_filename(BIO *b, char *name); 55 | int BIO_rw_filename(BIO *b, char *name); 56 | 57 | /* Connect */ 58 | BIO_METHOD *BIO_s_connect(void); 59 | extern BIO *BIO_new_connect(char *name); 60 | extern long BIO_do_connect(BIO *bp); 61 | extern long BIO_do_handshake(BIO *bp); 62 | 63 | long BIO_set_conn_hostname(BIO *b, char *name); 64 | long BIO_set_conn_port(BIO *b, char *port); 65 | // long BIO_set_conn_ip(BIO *b, char *ip); 66 | // long BIO_set_conn_int_port(BIO *b, char *port); 67 | char *BIO_get_conn_hostname(BIO *b); 68 | char *BIO_get_conn_port(BIO *b); 69 | // char *BIO_get_conn_ip(BIO *b, dummy); 70 | // long BIO_get_conn_int_port(BIO *b, int port); 71 | 72 | /* 73 | * From openssl/ssl.h 74 | */ 75 | // More BIO_ctrl() macros... 76 | long BIO_get_ssl(BIO *bp, SSL *sp); 77 | long BIO_set_ssl_mode(BIO *bp, int client); 78 | 79 | BIO_METHOD *BIO_f_ssl(void); 80 | BIO *BIO_new_ssl_connect(SSL_CTX *ctx); 81 | // long BIO_do_handshake(BIO *bp); 82 | // BIO_METHOD *BIO_f_ssl(void); 83 | // BIO *BIO_new_ssl(SSL_CTX *ctx, int client); 84 | // BIO *BIO_new_buffer_ssl_connect(SSL_CTX *ctx); 85 | // int BIO_ssl_copy_session_id(BIO *to, BIO *from); 86 | // void BIO_ssl_shutdown(BIO *ssl_bio); 87 | // int SSL_CTX_set_cipher_list(SSL_CTX *, const char *str); 88 | // int SSL_set_cipher_list(SSL *s, const char *str); 89 | 90 | // #define BIO_get_ssl(b,sslp) BIO_ctrl(b,BIO_C_GET_SSL,0,(char *)sslp) 91 | -------------------------------------------------------------------------------- /bio/bio_suite_test.go: -------------------------------------------------------------------------------- 1 | package bio_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestBio(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Bio Suite") 13 | } 14 | -------------------------------------------------------------------------------- /bio/bio_test.go: -------------------------------------------------------------------------------- 1 | package bio_test 2 | 3 | import ( 4 | . "github.com/IBM-Bluemix/golang-openssl-wrapper/bio" 5 | 6 | "encoding/json" 7 | "fmt" 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | "io/ioutil" 11 | "strings" 12 | ) 13 | 14 | type UserAgentJSON struct { 15 | UserAgent string `json:"user-agent"` 16 | } 17 | 18 | var _ = Describe("Bio", func() { 19 | Describe("Basic I/O", func() { 20 | var ( 21 | b BIO 22 | text = "Some really really really really really really really really really really really long test data" 23 | ) 24 | 25 | Context("Using a memory store", func() { 26 | It("Should create a new bio using memory store", func() { 27 | b = BIO_new(BIO_s_mem()) 28 | Expect(b).NotTo(BeNil()) 29 | }) 30 | 31 | It("Should be writable", func() { 32 | r := BIO_puts(b, text) 33 | Expect(r).NotTo(Equal(-2)) 34 | }) 35 | 36 | It("Should be readable", func() { 37 | buf := make([]byte, len(text)) 38 | r := BIO_gets(b, buf, len(text)+1) 39 | Expect(r).To(Equal(len(text))) 40 | Expect(string(buf)).To(Equal(text)) 41 | }) 42 | 43 | It("Should free the memory store bio", func() { 44 | Expect(BIO_free(b)).To(Equal(1)) 45 | }) 46 | }) 47 | 48 | Context("Using file storage", func() { 49 | It("Should create a new bio using file storage", func() { 50 | b = BIO_new(BIO_s_file()) 51 | Expect(b).NotTo(BeNil()) 52 | }) 53 | 54 | It("Should free the file storage bio", func() { 55 | Expect(BIO_free(b)).To(Equal(1)) 56 | }) 57 | 58 | It("Should be writable with BIO_printf", func() { 59 | b = BIO_new(BIO_s_file()) 60 | Expect(BIO_write_filename(b, "biotest.out")).To(Equal(1)) 61 | Expect(BIO_seek(b, 0)).To(BeEquivalentTo(0)) 62 | Expect(BIO_printf(b, text)).To(Equal(len(text))) 63 | Expect(BIO_free(b)).To(Equal(1)) 64 | }) 65 | 66 | It("Should be writable with BIO_write", func() { 67 | b = BIO_new(BIO_s_file()) 68 | Expect(BIO_write_filename(b, "biotest.out")).To(Equal(1)) 69 | Expect(BIO_seek(b, 0)).To(BeEquivalentTo(0)) 70 | Expect(BIO_write(b, text, len(text))).To(Equal(len(text))) 71 | Expect(BIO_free(b)).To(Equal(1)) 72 | }) 73 | 74 | It("Should be readable with BIO_gets", func() { 75 | buf := make([]byte, len(text)) 76 | b = BIO_new(BIO_s_file()) 77 | Expect(BIO_read_filename(b, "biotest.out")).To(Equal(1)) 78 | Expect(BIO_seek(b, 0)).To(BeEquivalentTo(0)) 79 | Expect(BIO_gets(b, buf, len(text)+1)).To(Equal(len(text))) 80 | Expect(string(buf)).To(Equal(text)) 81 | Expect(BIO_free(b)).To(Equal(1)) 82 | }) 83 | 84 | It("Should be readable with BIO_read", func() { 85 | buf := make([]byte, len(text)) 86 | b = BIO_new(BIO_s_file()) 87 | Expect(BIO_read_filename(b, "biotest.out")).To(Equal(1)) 88 | Expect(BIO_seek(b, 0)).To(BeEquivalentTo(0)) 89 | Expect(BIO_read(b, buf, len(text)+1)).To(Equal(len(text))) 90 | Expect(string(buf)).To(Equal(text)) 91 | Expect(BIO_free(b)).To(Equal(1)) 92 | }) 93 | }) 94 | 95 | Context("Using a connection", func() { 96 | var ( 97 | host, port, ua, request string 98 | ) 99 | 100 | /* 101 | * Setup a basic connect BIO, configure it, make the connection 102 | */ 103 | BeforeEach(func() { 104 | host = "httpbin.org" 105 | port = "http" 106 | ua = "https://github.com/IBM-Bluemix/golang-openssl-wrapper" 107 | request = strings.Join([]string{ 108 | "GET /user-agent HTTP/1.1", 109 | fmt.Sprintf("User-Agent: %s", ua), 110 | fmt.Sprintf("Host: %s", host), 111 | "Accept: */*", 112 | "\r\n", 113 | }, "\r\n") 114 | b = BIO_new(BIO_s_connect()) 115 | Expect(b).NotTo(BeNil()) 116 | Expect(BIO_set_conn_hostname(b, host)).To(BeEquivalentTo(1)) 117 | Expect(BIO_set_conn_port(b, port)).To(BeEquivalentTo(1)) 118 | Expect(BIO_do_connect(b)).To(BeEquivalentTo(1)) 119 | }) 120 | 121 | AfterEach(func() { 122 | Expect(BIO_free(b)).To(Equal(1)) 123 | }) 124 | 125 | It("Should return the correct host", func() { 126 | Expect(BIO_get_conn_hostname(b)).To(Equal(host)) 127 | }) 128 | 129 | It("Should return the correct port", func() { 130 | Expect(BIO_get_conn_port(b)).To(Equal(port)) 131 | }) 132 | 133 | It("Should connect successfully after resetting", func() { 134 | Expect(BIO_reset(b)).To(Equal(0)) 135 | Expect(BIO_do_connect(b)).To(BeEquivalentTo(1)) 136 | }) 137 | 138 | It("Should send a request and receive a response", func() { 139 | Expect(BIO_write(b, request, len(request))).To(Equal(len(request))) 140 | var d UserAgentJSON 141 | buf := make([]byte, 1024) 142 | l := BIO_read(b, buf, len(buf)) 143 | Expect(l).To(BeNumerically(">", 0)) 144 | s := strings.Split(string(buf[:l]), "\r\n\r\n") 145 | e := json.Unmarshal([]byte(s[1]), &d) 146 | Expect(e).To(BeNil()) 147 | Expect(d.UserAgent).To(Equal(ua)) 148 | }) 149 | }) 150 | 151 | Context("Making a connection", func() { 152 | It("Connects successfully", func() { 153 | dest := "www.google.com:http" 154 | bio := BIO_new_connect(dest) 155 | Expect(bio).NotTo(BeNil()) 156 | Expect(BIO_do_connect(bio)).To(BeEquivalentTo(1)) 157 | Expect(BIO_free(bio)).To(Equal(1)) 158 | }) 159 | }) 160 | 161 | Context("File I/O", func() { 162 | var filename, text string 163 | var fbio BIO 164 | BeforeEach(func() { 165 | mode := "w+" 166 | filename = "biotest.out" 167 | text = "To Kill A Mockingbird" 168 | fbio = BIO_new_file(filename, mode) 169 | Expect(fbio).NotTo(BeNil()) 170 | }) 171 | 172 | AfterEach(func() { 173 | /* Assumes only a single BIO in the chain... */ 174 | Expect(BIO_free(fbio)).To(Equal(1)) 175 | }) 176 | 177 | It("Writes to the file, reads using native Go I/O", func() { 178 | Expect(BIO_puts(fbio, text)).To(BeNumerically(">=", 1)) 179 | Expect(BIO_flush(fbio)).To(BeEquivalentTo(1)) 180 | /* For file BIOs, BIO_seek() returns 0 on success */ 181 | Expect(BIO_seek(fbio, 0)).To(BeEquivalentTo(0)) 182 | /* Temp block to check with native go I/O */ 183 | fbuf, _ := ioutil.ReadFile(filename) 184 | s := string(fbuf[:]) 185 | Expect(s).To(Equal(text)) 186 | }) 187 | 188 | It("Writes to the file, reads from the BIO", func() { 189 | Expect(BIO_puts(fbio, text)).To(BeNumerically(">=", 1)) 190 | Expect(BIO_flush(fbio)).To(BeEquivalentTo(1)) 191 | /* For file BIOs, BIO_seek() returns 0 on success */ 192 | Expect(BIO_seek(fbio, 0)).To(BeEquivalentTo(0)) 193 | 194 | rbuf := make([]byte, len(text)) 195 | l := BIO_gets(fbio, rbuf, len(text)+1) 196 | /* Check that we've read enough bytes */ 197 | Expect(l).To(BeNumerically(">=", len(text))) 198 | 199 | /* Check that the contents are what we wrote */ 200 | s := string(rbuf[:]) 201 | Expect(len(s)).To(Equal(len(text))) 202 | Expect(s).To(Equal(text)) 203 | }) 204 | }) 205 | }) 206 | }) 207 | -------------------------------------------------------------------------------- /crypto/crypto.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | // #cgo CFLAGS: -I/usr/local/ssl/include 4 | // #cgo LDFLAGS: -L /usr/local/ssl/lib -lcrypto 5 | import "C" 6 | -------------------------------------------------------------------------------- /crypto/crypto.swig: -------------------------------------------------------------------------------- 1 | /* 2 | * SWIG interface file for libcrypto 3 | * Headers referenced here include: 4 | * openssl/crypto.h - core crypto routines 5 | * openssl/err.h - error routines 6 | * openssl/evp.h - high-level OpenSSL API for cryptography 7 | * openssl/... 8 | */ 9 | %module crypto 10 | %{ 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | /* 19 | * Macros for manipulating argument type to EVP_CIPHER_CTX_ctrl() 20 | */ 21 | 22 | #define SET_TAG_GCM(ctx, type, arg, ptr) EVP_CIPHER_CTX_ctrl(ctx, type, arg, ptr) 23 | #define GET_TAG_GCM(ctx, type, arg, ptr) EVP_CIPHER_CTX_ctrl(ctx, type, arg, ptr) 24 | %} 25 | 26 | // %include "typemaps.i" 27 | %include "../include/ossl_typemaps.i" 28 | %include "../include/evp_typ.i" 29 | 30 | 31 | /* 32 | * From openssl/conf.h 33 | */ 34 | 35 | /* Functions */ 36 | 37 | extern void OPENSSL_config(const char *config_name); 38 | extern void OPENSSL_no_config(void); 39 | 40 | /* 41 | * From openssl/crypto.h 42 | */ 43 | 44 | /* Functions */ 45 | 46 | extern int FIPS_mode_set(int r); 47 | extern int FIPS_mode(void); 48 | 49 | /* 50 | * From openssl/err.h 51 | */ 52 | 53 | /* Functions */ 54 | 55 | extern void ERR_load_crypto_strings(void); 56 | extern void ERR_free_strings(void); 57 | 58 | /* 59 | * From openssl/evp.h 60 | */ 61 | 62 | /* Types */ 63 | typedef struct evp_cipher_st { 64 | int nid; 65 | int block_size; 66 | /* Default value for variable length ciphers */ 67 | int key_len; 68 | int iv_len; 69 | /* Various flags */ 70 | unsigned long flags; 71 | /* init key */ 72 | int (*init) (EVP_CIPHER_CTX *ctx, const unsigned char *key, 73 | const unsigned char *iv, int enc); 74 | /* encrypt/decrypt data */ 75 | int (*do_cipher) (EVP_CIPHER_CTX *ctx, unsigned char *out, 76 | const unsigned char *in, size_t inl); 77 | /* cleanup ctx */ 78 | int (*cleanup) (EVP_CIPHER_CTX *); 79 | /* how big ctx->cipher_data needs to be */ 80 | int ctx_size; 81 | /* Populate a ASN1_TYPE with parameters */ 82 | int (*set_asn1_parameters) (EVP_CIPHER_CTX *, ASN1_TYPE *); 83 | /* Get parameters from a ASN1_TYPE */ 84 | int (*get_asn1_parameters) (EVP_CIPHER_CTX *, ASN1_TYPE *); 85 | /* Miscellaneous operations */ 86 | int (*ctrl) (EVP_CIPHER_CTX *, int type, int arg, void *ptr); 87 | /* Application data */ 88 | void *app_data; 89 | } EVP_CIPHER; 90 | 91 | typedef struct evp_cipher_ctx_st { 92 | const EVP_CIPHER *cipher; 93 | ENGINE *engine; /* functional reference if 'cipher' is 94 | * ENGINE-provided */ 95 | int encrypt; /* encrypt or decrypt */ 96 | int buf_len; /* number we have left */ 97 | unsigned char oiv[EVP_MAX_IV_LENGTH]; /* original iv */ 98 | unsigned char iv[EVP_MAX_IV_LENGTH]; /* working iv */ 99 | unsigned char buf[EVP_MAX_BLOCK_LENGTH]; /* saved partial block */ 100 | int num; /* used by cfb/ofb/ctr mode */ 101 | void *app_data; /* application stuff */ 102 | int key_len; /* May change for variable length cipher */ 103 | unsigned long flags; /* Various flags */ 104 | void *cipher_data; /* per EVP data */ 105 | int final_used; 106 | int block_mask; 107 | unsigned char final[EVP_MAX_BLOCK_LENGTH]; /* possible final block */ 108 | } EVP_CIPHER_CTX; 109 | 110 | 111 | /* Functions */ 112 | 113 | extern void OpenSSL_add_all_algorithms(void); 114 | extern void EVP_cleanup(void); 115 | 116 | /* Allocate and free up a cipher context */ 117 | /* 118 | * A note - I am only including EVP_CIPHER_CTX_new() because ctx := new(EVP_CIPHER_CTX) is 119 | * causing a panic when I then pass ctx into EVP_CIPHER_CTX_init(). If someone can suggest 120 | * how to fix this, we should really remove EVP_CIPHER_CTX_new(), since my understanding is 121 | * that it is now deprecated from the OpenSSL API (it is not included in the manpages, either) 122 | */ 123 | extern EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void); 124 | extern void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *a); 125 | extern int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a); 126 | 127 | extern int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr); 128 | 129 | %apply void *VOIDSTRINGBUF { void *ptr }; 130 | extern int SET_TAG_GCM(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr); 131 | %apply char *CHARBUF { char *ptr }; 132 | extern int GET_TAG_GCM(EVP_CIPHER_CTX *ctx, int type, int arg, char *ptr); 133 | 134 | /* 135 | * Constants for EVP_CIPHER_CTX_ctrl() 136 | */ 137 | #define EVP_CTRL_GCM_GET_TAG 0x10 138 | #define EVP_CTRL_GCM_SET_TAG 0x11 139 | 140 | /* 141 | * As of now, for the standard use of EVP_EncryptInit_ex(), in which you pass null in for the engine, 142 | * you would pass in SwigcptrStruct_SS_engine_st(0) in go. At some point we should make the API cleaner. 143 | * Example: 144 | * r := EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), SwigcptrStruct_SS_engine_st(0), "somekey", "someiv") 145 | */ 146 | extern int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, 147 | ENGINE *impl, unsigned char *key, unsigned char *iv); 148 | 149 | 150 | %apply char *CHARBUF { unsigned char *out }; 151 | %apply int *OUTLEN { int *outl }; 152 | extern int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, 153 | int *outl, unsigned char *in, int inl); 154 | 155 | extern int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl); 156 | 157 | extern int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, 158 | ENGINE *impl, unsigned char *key, unsigned char *iv); 159 | 160 | extern int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, 161 | int *outl, unsigned char *in, int inl); 162 | 163 | extern int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, 164 | int *outl); 165 | 166 | /* Ciphers */ 167 | const EVP_CIPHER *EVP_aes_256_cbc(void); 168 | const EVP_CIPHER *EVP_aes_256_cfb(void); 169 | const EVP_CIPHER *EVP_aes_256_gcm(void); 170 | const EVP_CIPHER *EVP_des_cbc(void); 171 | -------------------------------------------------------------------------------- /crypto/crypto_suite_test.go: -------------------------------------------------------------------------------- 1 | package crypto_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestCrypto(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Crypto Suite") 13 | } 14 | -------------------------------------------------------------------------------- /crypto/crypto_test.go: -------------------------------------------------------------------------------- 1 | package crypto_test 2 | 3 | import ( 4 | . "github.com/IBM-Bluemix/golang-openssl-wrapper/crypto" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | // "strings" 9 | // "unsafe" 10 | "github.com/IBM-Bluemix/golang-openssl-wrapper/rand" 11 | ) 12 | 13 | var _ = Describe("Crypto", func() { 14 | 15 | var ( 16 | // plaintext = "My Fair Lady" 17 | // plaintext = "My super long string to be encrypted" 18 | plaintext = "My super super super super duper long string to be encrypted" 19 | 20 | sLen, eLen int 21 | encrypted, decrypted string 22 | bufEncrypt, bufDecrypt []byte 23 | ctxEncrypt, ctxDecrypt EVP_CIPHER_CTX 24 | ) 25 | 26 | BeforeEach(func() { 27 | /* 28 | * Setup OpenSSL 29 | */ 30 | ERR_load_crypto_strings() 31 | OpenSSL_add_all_algorithms() 32 | OPENSSL_config("") 33 | }) 34 | 35 | Context("Enabling and disabling FIPS mode", func() { 36 | It("should return the current setting", func() { 37 | FIPS_mode_set(1) 38 | Expect(FIPS_mode()).To(Equal(1)) 39 | FIPS_mode_set(0) 40 | Expect(FIPS_mode()).To(Equal(0)) 41 | }) 42 | 43 | It("should disallow the use of a non-approved algorithm in FIPS mode", func() { 44 | FIPS_mode_set(1) 45 | ctxEncrypt = EVP_CIPHER_CTX_new() 46 | EVP_CIPHER_CTX_init(ctxEncrypt) 47 | Expect(EVP_EncryptInit_ex(ctxEncrypt, EVP_des_cbc(), SwigcptrStruct_SS_engine_st(0), "somekey", "someiv")).To(Equal(0)) 48 | EVP_CIPHER_CTX_cleanup(ctxEncrypt) 49 | }) 50 | 51 | It("should allow the use of a non-approved algorithm after disabling FIPS mode", func() { 52 | FIPS_mode_set(0) 53 | ctxEncrypt = EVP_CIPHER_CTX_new() 54 | EVP_CIPHER_CTX_init(ctxEncrypt) 55 | Expect(EVP_EncryptInit_ex(ctxEncrypt, EVP_des_cbc(), SwigcptrStruct_SS_engine_st(0), "somekey", "someiv")).To(Equal(1)) 56 | EVP_CIPHER_CTX_cleanup(ctxEncrypt) 57 | }) 58 | 59 | It("should allow the use of an approved algorithm in FIPS mode", func() { 60 | FIPS_mode_set(1) 61 | EVP_CIPHER_CTX_init(ctxEncrypt) 62 | Expect(EVP_EncryptInit_ex(ctxEncrypt, EVP_aes_256_cfb(), SwigcptrStruct_SS_engine_st(0), "thisisa256bitkeywhichhas32chars", "andwevea128bitiv")).To(Equal(1)) 63 | EVP_CIPHER_CTX_cleanup(ctxEncrypt) 64 | }) 65 | }) // END Context 66 | 67 | Context("Initializing and freeing the context", func() { 68 | It("should return indicating success", func() { 69 | ctxEncrypt = EVP_CIPHER_CTX_new() 70 | EVP_CIPHER_CTX_init(ctxEncrypt) 71 | Expect(EVP_CIPHER_CTX_cleanup(ctxEncrypt)).To(Equal(1)) 72 | }) 73 | }) 74 | 75 | Context("Encrypting in CBC mode with FIPS mode disabled", func() { 76 | BeforeEach(func() { 77 | /* Be sure FIPS mode is disabled */ 78 | FIPS_mode_set(0) 79 | Expect(FIPS_mode()).To(Equal(0)) 80 | 81 | ctxEncrypt = EVP_CIPHER_CTX_new() 82 | EVP_CIPHER_CTX_init(ctxEncrypt) 83 | Expect(ctxEncrypt).NotTo(BeNil()) 84 | }) 85 | 86 | It("should encrypt successfully", func() { 87 | Expect(EVP_EncryptInit_ex(ctxEncrypt, EVP_aes_256_cbc(), SwigcptrStruct_SS_engine_st(0), "somekey", "someiv")).To(Equal(1)) 88 | bufEncrypt = make([]byte, len(plaintext)+ctxEncrypt.GetCipher().GetBlock_size()) 89 | Expect(EVP_EncryptUpdate(ctxEncrypt, bufEncrypt, &sLen, plaintext, len(plaintext))).To(Equal(1)) 90 | encrypted = string(bufEncrypt[:sLen]) 91 | Expect(EVP_EncryptFinal_ex(ctxEncrypt, bufEncrypt, &eLen)).To(Equal(1)) 92 | encrypted += string(bufEncrypt[:eLen]) 93 | }) 94 | 95 | AfterEach(func() { 96 | Expect(EVP_CIPHER_CTX_cleanup(ctxEncrypt)).To(Equal(1)) 97 | }) 98 | }) 99 | 100 | Context("Decrypting a string", func() { 101 | BeforeEach(func() { 102 | FIPS_mode_set(0) 103 | Expect(FIPS_mode()).To(Equal(0)) 104 | 105 | /* Setup an encryption context and create our encrypted string */ 106 | ctxEncrypt = EVP_CIPHER_CTX_new() 107 | EVP_CIPHER_CTX_init(ctxEncrypt) 108 | Expect(ctxEncrypt).NotTo(BeNil()) 109 | 110 | Expect(EVP_EncryptInit_ex(ctxEncrypt, EVP_aes_256_cbc(), SwigcptrStruct_SS_engine_st(0), "somekey", "someiv")).To(Equal(1)) 111 | 112 | bufEncrypt = make([]byte, len(plaintext)+ctxEncrypt.GetCipher().GetBlock_size()) 113 | 114 | Expect(EVP_EncryptUpdate(ctxEncrypt, bufEncrypt, &sLen, plaintext, len(plaintext))).To(Equal(1)) 115 | encrypted = string(bufEncrypt[:sLen]) 116 | 117 | Expect(EVP_EncryptFinal_ex(ctxEncrypt, bufEncrypt, &eLen)).To(Equal(1)) 118 | encrypted += string(bufEncrypt[:eLen]) 119 | 120 | Expect(EVP_CIPHER_CTX_cleanup(ctxEncrypt)).To(Equal(1)) 121 | }) 122 | 123 | It("should decrypt successfully", func() { 124 | ctxDecrypt = EVP_CIPHER_CTX_new() 125 | EVP_CIPHER_CTX_init(ctxDecrypt) 126 | Expect(ctxDecrypt).NotTo(BeNil()) 127 | 128 | Expect(EVP_DecryptInit_ex(ctxDecrypt, EVP_aes_256_cbc(), SwigcptrStruct_SS_engine_st(0), "somekey", "someiv")).To(Equal(1)) 129 | 130 | bufDecrypt = make([]byte, len(encrypted)) 131 | Expect(EVP_DecryptUpdate(ctxDecrypt, bufDecrypt, &sLen, encrypted, len(encrypted))).To(Equal(1)) 132 | 133 | decrypted = string(bufDecrypt[:sLen]) 134 | // bufFinal := make([]byte, len(encrypted)) 135 | Expect(EVP_DecryptFinal_ex(ctxDecrypt, bufDecrypt, &eLen)).To(Equal(1)) 136 | 137 | decrypted += string(bufDecrypt[:eLen]) 138 | Expect(decrypted).To(Equal(plaintext)) 139 | }) 140 | 141 | }) 142 | 143 | Context("Using AEAD (authenticated encryption)", func() { 144 | var ( 145 | key, iv, aad string 146 | ivLen, aLen, tagLen int 147 | tag []byte 148 | ) 149 | 150 | BeforeEach(func() { 151 | /* Create contexts for encryption and decryption */ 152 | ivLen = 12 // Standard length for AEAD 153 | ctxEncrypt = EVP_CIPHER_CTX_new() 154 | EVP_CIPHER_CTX_init(ctxEncrypt) 155 | ctxDecrypt = EVP_CIPHER_CTX_new() 156 | EVP_CIPHER_CTX_init(ctxDecrypt) 157 | 158 | Expect(ctxEncrypt).NotTo(BeNil()) 159 | Expect(ctxDecrypt).NotTo(BeNil()) 160 | key = "somekey" 161 | 162 | /* Setup a random IV */ 163 | buf := make([]byte, ivLen) 164 | l, err := rand.Read(buf) 165 | Expect(err).NotTo(HaveOccurred()) 166 | Expect(l).To(Equal(len(buf))) 167 | iv = string(buf) 168 | 169 | /* Get our aad - in a real world use, this could be almost anything, e.g. a host:ip tuple */ 170 | aad = "additionalAuthenticationData" 171 | 172 | tagLen = 16 173 | tag = make([]byte, tagLen) 174 | 175 | encrypted = "" 176 | decrypted = "" 177 | }) 178 | 179 | It("Encrypts and decrypts a string using a standard 12 byte IV", func() { 180 | Expect(EVP_EncryptInit_ex(ctxEncrypt, EVP_aes_256_gcm(), SwigcptrStruct_SS_engine_st(0), key, iv)).To(Equal(1)) 181 | 182 | /* Provide the aad data */ 183 | Expect(EVP_EncryptUpdate(ctxEncrypt, nil, &aLen, aad, len(aad))).To(Equal(1)) 184 | 185 | /* 186 | * Encrypt 187 | */ 188 | bufEncrypt = make([]byte, len(plaintext)+ctxEncrypt.GetCipher().GetBlock_size()) 189 | Expect(EVP_EncryptUpdate(ctxEncrypt, bufEncrypt, &sLen, plaintext, len(plaintext))).To(Equal(1)) 190 | encrypted += string(bufEncrypt[:sLen]) 191 | Expect(EVP_EncryptFinal_ex(ctxEncrypt, bufEncrypt, &eLen)).To(Equal(1)) 192 | encrypted += string(bufEncrypt[:eLen]) 193 | 194 | /* Get the tag - *must* be done after calling EVP_EncryptFinal_ex() */ 195 | // Expect(EVP_CIPHER_CTX_ctrl(ctxEncrypt, EVP_CTRL_GCM_GET_TAG, tagLen, tag)).To(Equal(1)) 196 | Expect(GET_TAG_GCM(ctxEncrypt, EVP_CTRL_GCM_GET_TAG, tagLen, tag)).To(Equal(1)) 197 | Expect(len(tag)).To(Equal(tagLen)) 198 | 199 | Expect(EVP_CIPHER_CTX_cleanup(ctxEncrypt)).To(Equal(1)) 200 | 201 | /* 202 | * Decrypt 203 | */ 204 | /* Since we're using the default IV length of 12 bytes, we don't need to set it */ 205 | Expect(EVP_DecryptInit_ex(ctxDecrypt, EVP_aes_256_gcm(), SwigcptrStruct_SS_engine_st(0), key, iv)).To(Equal(1)) 206 | 207 | /* Set the tag to what we expect */ 208 | tagString := string(tag) 209 | // Expect(EVP_CIPHER_CTX_ctrl(ctxDecrypt, EVP_CTRL_GCM_SET_TAG, tagLen, tag)).To(Equal(1)) 210 | Expect(SET_TAG_GCM(ctxDecrypt, EVP_CTRL_GCM_SET_TAG, tagLen, tagString)).To(Equal(1)) 211 | 212 | /* Provide the aad data */ 213 | Expect(EVP_DecryptUpdate(ctxDecrypt, nil, &aLen, aad, len(aad))).To(Equal(1)) 214 | 215 | /* Decrypt - tag must be set before we call EVP_DecryptUpdate() */ 216 | bufDecrypt = make([]byte, len(encrypted)) 217 | Expect(EVP_DecryptUpdate(ctxDecrypt, bufDecrypt, &sLen, encrypted, len(encrypted))).To(Equal(1)) 218 | decrypted += string(bufDecrypt[:sLen]) 219 | 220 | Expect(EVP_DecryptFinal_ex(ctxDecrypt, bufDecrypt, &eLen)).To(Equal(1)) 221 | decrypted += string(bufDecrypt[:eLen]) 222 | 223 | Expect(decrypted).To(Equal(plaintext)) 224 | Expect(EVP_CIPHER_CTX_cleanup(ctxDecrypt)).To(Equal(1)) 225 | }) 226 | }) 227 | 228 | }) 229 | -------------------------------------------------------------------------------- /digest/digest.go: -------------------------------------------------------------------------------- 1 | package digest 2 | 3 | // #cgo CFLAGS: -I/usr/local/ssl/include 4 | // #cgo LDFLAGS: -L /usr/local/ssl/lib -lcrypto 5 | import "C" 6 | -------------------------------------------------------------------------------- /digest/digest.swig: -------------------------------------------------------------------------------- 1 | %module digest 2 | 3 | %{ 4 | #include 5 | %} 6 | 7 | %include "cmalloc.i" 8 | %include "../include/ossl_typemaps.i" 9 | %include "../include/evp_typ.i" 10 | %apply char *CHARBUF {unsigned char *outbuf}; 11 | 12 | /* 13 | * Constants 14 | */ 15 | 16 | #define EVP_MAX_MD_SIZE 64 17 | 18 | // %malloc(EVP_MD_CTX) 19 | // %free(EVP_MD_CTX) 20 | %allocators(EVP_MD_CTX) 21 | void EVP_MD_CTX_init(EVP_MD_CTX *ctx); 22 | EVP_MD_CTX *EVP_MD_CTX_create(void); 23 | 24 | int EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx); 25 | /* 26 | * EVP_MD_CTX_destroy() should *only* be used on a context created 27 | * using EVP_MD_CTX_create(). The alternative is to create thus: 28 | * 29 | * ctx := Malloc_EVP_MD_CTX 30 | * EVP_MD_CTX_init(ctx) 31 | * 32 | * You may then deallocate/free thus: 33 | * 34 | * EVP_MD_CTX_cleanup(ctx) 35 | * Free_EVP_MD_CTX(ctx) 36 | */ 37 | void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx); 38 | 39 | int EVP_MD_CTX_copy(EVP_MD_CTX *out, const EVP_MD_CTX *in); 40 | 41 | int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl); 42 | %typemap(gotype) const void *d %{string%} 43 | int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt); 44 | int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *outbuf, unsigned int *s); 45 | 46 | /* 47 | * With these functions, ctx does not need to be initialized, and EVP_DigestFinal 48 | * cleans up the context as well, so you do not need to call _cleanup or _destroy 49 | * on it. 50 | */ 51 | int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type); 52 | int EVP_DigestFinal(EVP_MD_CTX *ctx, unsigned char *outbuf, unsigned int *s); 53 | 54 | /* 55 | * Return digest and block sizes 56 | */ 57 | int EVP_MD_size(const EVP_MD *md); 58 | int EVP_MD_block_size(const EVP_MD *md); 59 | int EVP_MD_CTX_size(EVP_MD_CTX *ctx); 60 | int EVP_MD_CTX_block_size(EVP_MD_CTX *ctx); 61 | 62 | /* FIPS-approved digest/hash algorithms */ 63 | const EVP_MD *EVP_sha(void); 64 | const EVP_MD *EVP_sha1(void); 65 | const EVP_MD *EVP_sha224(void); 66 | const EVP_MD *EVP_sha256(void); 67 | const EVP_MD *EVP_sha384(void); 68 | const EVP_MD *EVP_sha512(void); 69 | 70 | /* Digest/hash algos not allowed under FIPS */ 71 | const EVP_MD *EVP_md5(void); -------------------------------------------------------------------------------- /digest/digest_suite_test.go: -------------------------------------------------------------------------------- 1 | package digest_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestDigest(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Digest Suite") 13 | } 14 | -------------------------------------------------------------------------------- /digest/digest_test.go: -------------------------------------------------------------------------------- 1 | package digest_test 2 | 3 | import ( 4 | "github.com/IBM-Bluemix/golang-openssl-wrapper/crypto" 5 | . "github.com/IBM-Bluemix/golang-openssl-wrapper/digest" 6 | "github.com/IBM-Bluemix/golang-openssl-wrapper/rand" 7 | 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | var _ = Describe("Digest", func() { 13 | Context("Creating and destroying a context", func() { 14 | It("Creates and destroys using the built-in (de)allocation mechanism", func() { 15 | ctx := EVP_MD_CTX_create() 16 | Expect(ctx).NotTo(BeNil()) 17 | EVP_MD_CTX_init(ctx) 18 | EVP_MD_CTX_destroy(ctx) 19 | }) 20 | 21 | It("Creates and destroys using user-controlled (de)allocation", func() { 22 | ctx := Malloc_EVP_MD_CTX() 23 | Expect(ctx).NotTo(BeNil()) 24 | EVP_MD_CTX_init(ctx) 25 | Expect(EVP_MD_CTX_cleanup(ctx)).To(Equal(1)) 26 | Free_EVP_MD_CTX(ctx) 27 | }) 28 | 29 | It("Initializes and deallocates directly", func() { 30 | ctx := Malloc_EVP_MD_CTX() 31 | Expect(EVP_DigestInit(ctx, EVP_sha256())).To(Equal(1)) 32 | buf := make([]byte, 50) 33 | var l uint 34 | Expect(EVP_DigestFinal(ctx, buf, &l)).To(Equal(1)) 35 | }) 36 | }) 37 | 38 | Context("With FIPS mode enabled", func() { 39 | var ctx EVP_MD_CTX 40 | 41 | BeforeEach(func() { 42 | crypto.FIPS_mode_set(1) 43 | Expect(crypto.FIPS_mode()).To(Equal(1)) 44 | ctx = Malloc_EVP_MD_CTX() 45 | Expect(ctx).NotTo(BeNil()) 46 | EVP_MD_CTX_init(ctx) 47 | }) 48 | 49 | AfterEach(func() { 50 | crypto.FIPS_mode_set(0) 51 | Expect(crypto.FIPS_mode()).To(Equal(0)) 52 | EVP_MD_CTX_cleanup(ctx) 53 | Free_EVP_MD_CTX(ctx) 54 | }) 55 | 56 | It("Allows use of SHA* but disallows MD5", func() { 57 | Expect(EVP_DigestInit_ex(ctx, EVP_md5(), SwigcptrStruct_SS_engine_st(0))).To(Equal(0)) 58 | Expect(EVP_DigestInit_ex(ctx, EVP_sha256(), SwigcptrStruct_SS_engine_st(0))).To(Equal(1)) 59 | }) 60 | }) 61 | 62 | Context("Hashing binary data", func() { 63 | Context("Using the OpenSSL digest API", func() { 64 | var ctx EVP_MD_CTX 65 | var data []byte 66 | var buf []byte 67 | var seqlen int 68 | var l uint 69 | 70 | BeforeEach(func() { 71 | ctx = Malloc_EVP_MD_CTX() 72 | Expect(ctx).NotTo(BeNil()) 73 | EVP_MD_CTX_init(ctx) 74 | Expect(EVP_DigestInit_ex(ctx, EVP_sha256(), SwigcptrStruct_SS_engine_st(0))).To(Equal(1)) 75 | 76 | seqlen = 50 77 | data = make([]byte, seqlen) 78 | Expect(rand.RAND_bytes(data, seqlen)).To(Equal(1)) 79 | buf = make([]byte, seqlen) 80 | }) 81 | 82 | AfterEach(func() { 83 | EVP_MD_CTX_cleanup(ctx) 84 | Free_EVP_MD_CTX(ctx) 85 | }) 86 | 87 | It("Returns the correct digest size", func() { 88 | s1 := EVP_MD_CTX_size(ctx) 89 | s2 := EVP_MD_size(EVP_sha256()) 90 | Expect(s1).To(BeNumerically(">", 0)) 91 | Expect(s2).To(Equal(s1)) 92 | }) 93 | 94 | It("Returns the correct block size", func() { 95 | s1 := EVP_MD_CTX_block_size(ctx) 96 | s2 := EVP_MD_block_size(EVP_sha256()) 97 | Expect(s1).To(BeNumerically(">", 0)) 98 | Expect(s2).To(Equal(s1)) 99 | }) 100 | 101 | It("Produces identical hash values from the same binary data", func() { 102 | ctx2 := Malloc_EVP_MD_CTX() 103 | buf2 := make([]byte, seqlen) 104 | var l2 uint 105 | 106 | Expect(EVP_MD_CTX_copy(ctx2, ctx)).To(Equal(1)) 107 | Expect(EVP_DigestUpdate(ctx, string(data), int64(seqlen))).To(Equal(1)) 108 | Expect(EVP_DigestFinal_ex(ctx, buf, &l)).To(Equal(1)) 109 | 110 | Expect(EVP_DigestUpdate(ctx2, string(data), int64(seqlen))).To(Equal(1)) 111 | Expect(EVP_DigestFinal_ex(ctx2, buf2, &l2)).To(Equal(1)) 112 | 113 | h1 := string(buf) 114 | h2 := string(buf2) 115 | 116 | Expect(len(h1)).To(BeNumerically(">", 0)) 117 | Expect(len(h2)).To(BeNumerically(">", 0)) 118 | Expect(h2).To(Equal(h1)) 119 | 120 | EVP_MD_CTX_cleanup(ctx2) 121 | Free_EVP_MD_CTX(ctx2) 122 | }) 123 | }) // END Context for OpenSSL digest 124 | }) 125 | }) 126 | -------------------------------------------------------------------------------- /digest/hash.go: -------------------------------------------------------------------------------- 1 | package digest 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // Digest represents a single message digest. 8 | // It implements the hash.Hash interface. 9 | type Digest struct { 10 | io.Writer 11 | context EVP_MD_CTX 12 | } 13 | 14 | // Write stores the contents of p in the digest. 15 | // p is a slice of bytes representing the message used to calculate the checksum/hash. 16 | // Write returns the number of bytes written and an error (which is always nil). 17 | func (d *Digest) Write(p []byte) (int, error) { 18 | ret := EVP_DigestUpdate(d.context, string(p), int64(len(p))) 19 | if ret != 1 { 20 | return 0, nil 21 | } 22 | return len(p), nil 23 | } 24 | 25 | // Sum returns the current checksum value for the Digest. 26 | // If p is nil, the bare checksum is returned. 27 | // If p is not nil, the checksum is appended to p, which is then returned. 28 | func (d *Digest) Sum(p []byte) []byte { 29 | var ( 30 | l uint 31 | ) 32 | /* 33 | * We make a copy so that the user can still write/sum using the original 34 | */ 35 | ctx := Malloc_EVP_MD_CTX() 36 | EVP_MD_CTX_copy(ctx, d.context) 37 | 38 | buf := make([]byte, EVP_MAX_MD_SIZE) 39 | EVP_DigestFinal_ex(ctx, buf, &l) 40 | 41 | Free_EVP_MD_CTX(ctx) 42 | 43 | if p != nil { 44 | return append(p, buf...) 45 | } 46 | 47 | return buf 48 | } 49 | 50 | // Size returns the the length of the Digest's checksum (what Sum() will return) 51 | func (d *Digest) Size() int { 52 | return EVP_MD_CTX_size(d.context) 53 | } 54 | 55 | // BlockSize returns the underlying block size for the Digest. 56 | func (d *Digest) BlockSize() int { 57 | return EVP_MD_CTX_block_size(d.context) 58 | } 59 | 60 | // NewSHA256 returns a pointer to a Digest configured to use the SHA256 algorithm. 61 | func NewSHA256() *Digest { 62 | ctx := EVP_MD_CTX_create() 63 | 64 | if ctx == nil { 65 | return nil 66 | } 67 | 68 | EVP_MD_CTX_init(ctx) 69 | EVP_DigestInit_ex(ctx, EVP_sha256(), SwigcptrStruct_SS_engine_st(0)) 70 | 71 | return &Digest{ 72 | context: ctx, 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /digest/hash_test.go: -------------------------------------------------------------------------------- 1 | package digest_test 2 | 3 | import ( 4 | . "github.com/IBM-Bluemix/golang-openssl-wrapper/digest" 5 | 6 | "bytes" 7 | "github.com/IBM-Bluemix/golang-openssl-wrapper/rand" 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | "strings" 11 | ) 12 | 13 | var _ = Describe("Hash", func() { 14 | 15 | Context("Emulating the golang native crypto/sha256 API", func() { 16 | var ( 17 | ret int 18 | err error 19 | data, key1 []byte 20 | seqlen int 21 | hasher1 *Digest 22 | ) 23 | 24 | BeforeEach(func() { 25 | seqlen = 50 26 | data = make([]byte, seqlen) 27 | Expect(rand.RAND_bytes(data, seqlen)).To(Equal(1)) 28 | 29 | hasher1 = NewSHA256() 30 | Expect(hasher1).NotTo(BeNil()) 31 | ret, err = hasher1.Write(data) 32 | Expect(ret).To(Equal(len(data))) 33 | Expect(err).NotTo(HaveOccurred()) 34 | key1 = hasher1.Sum(nil) 35 | 36 | Expect(len(key1)).To(BeNumerically(">", 0)) 37 | }) 38 | 39 | It("Returns the correct digest length", func() { 40 | s := EVP_MD_size(EVP_sha256()) 41 | Expect(s).To(BeNumerically(">", 0)) 42 | Expect(hasher1.Size()).To(Equal(s)) 43 | }) 44 | 45 | It("Returns the correct block size", func() { 46 | s := EVP_MD_block_size(EVP_sha256()) 47 | Expect(s).To(BeNumerically(">", 0)) 48 | Expect(hasher1.BlockSize()).To(Equal(s)) 49 | }) 50 | 51 | It("Produces identical hash values from the same binary data", func() { 52 | hasher2 := NewSHA256() 53 | Expect(hasher2).NotTo(BeNil()) 54 | ret, err = hasher2.Write(data) 55 | Expect(ret).To(Equal(len(data))) 56 | Expect(err).NotTo(HaveOccurred()) 57 | key2 := hasher2.Sum(nil) 58 | 59 | Expect(len(key2)).To(BeNumerically(">", 0)) 60 | 61 | Expect(bytes.Equal(key1, key2)).To(BeTrue()) 62 | }) 63 | 64 | It("Produces different hash values from different inputs", func() { 65 | data2 := make([]byte, seqlen) 66 | Expect(rand.RAND_bytes(data2, seqlen)).To(Equal(1)) 67 | 68 | hasher2 := NewSHA256() 69 | Expect(hasher2).NotTo(BeNil()) 70 | ret, err = hasher2.Write(data2) 71 | Expect(ret).To(Equal(len(data2))) 72 | Expect(err).NotTo(HaveOccurred()) 73 | key2 := hasher2.Sum(nil) 74 | 75 | Expect(len(key2)).To(BeNumerically(">", 0)) 76 | 77 | Expect(bytes.Equal(key1, key2)).To(BeFalse()) 78 | }) 79 | 80 | It("Appends to an existing hash key", func() { 81 | newKey := hasher1.Sum(key1) 82 | Expect(strings.HasPrefix(string(newKey), string(key1))).To(BeTrue()) 83 | }) 84 | }) 85 | }) 86 | -------------------------------------------------------------------------------- /examples/crypto.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/IBM-Bluemix/golang-openssl-wrapper/crypto" 6 | "github.com/IBM-Bluemix/golang-openssl-wrapper/rand" 7 | ) 8 | 9 | func main() { 10 | var ( 11 | plaintext = "My super super super super duper long string to be encrypted" 12 | ivLen = 12 13 | 14 | sLen, eLen int 15 | encrypted, decrypted, iv string 16 | bufEncrypt, bufDecrypt []byte 17 | ctxEncrypt, ctxDecrypt crypto.EVP_CIPHER_CTX 18 | ) 19 | 20 | // Setup error strings 21 | crypto.ERR_load_crypto_strings() 22 | 23 | // Add all OpenSSL algorithms 24 | crypto.OpenSSL_add_all_algorithms() 25 | 26 | // Load an OpenSSL config 27 | crypto.OPENSSL_config("") 28 | 29 | // Enable FIPS mode 30 | crypto.FIPS_mode_set(1) 31 | 32 | // Create new EVP_CIPHER_CTX instances 33 | ctxEncrypt, ctxDecrypt = crypto.EVP_CIPHER_CTX_new(), crypto.EVP_CIPHER_CTX_new() 34 | 35 | // Panic if either EVP_CIPHER_CTX fails to create 36 | if ctxEncrypt == nil { 37 | panic("ctxEncrypt is nil") 38 | } 39 | if ctxDecrypt == nil { 40 | panic("ctxDecrypt is nil") 41 | } 42 | 43 | // Initialize the EVP_CIPHER_CTX instances 44 | crypto.EVP_CIPHER_CTX_init(ctxEncrypt) 45 | crypto.EVP_CIPHER_CTX_init(ctxDecrypt) 46 | 47 | // Create random IV for nondeterministic encryption 48 | buf := make([]byte, ivLen) 49 | _, e := rand.Read(buf) 50 | if e != nil { 51 | panic(e) 52 | } 53 | iv = string(buf) 54 | 55 | // Pass the IV into the encrypted string to be used when decoding 56 | encrypted = iv 57 | 58 | // Print plaintext string 59 | fmt.Printf("plaintext: %s\n", plaintext) 60 | 61 | /* 62 | Encrypting a string 63 | */ 64 | // Initialize the ctxEncrypt context for encryption 65 | crypto.EVP_EncryptInit_ex(ctxEncrypt, crypto.EVP_aes_256_cbc(), crypto.SwigcptrStruct_SS_engine_st(0), "somekey", iv) 66 | 67 | // Make a buffer with enough size for the plaintext plus one block 68 | bufEncrypt = make([]byte, len(plaintext)+ctxEncrypt.GetCipher().GetBlock_size()) 69 | 70 | // Update the cipher with some content 71 | crypto.EVP_EncryptUpdate(ctxEncrypt, bufEncrypt, &sLen, plaintext, len(plaintext)) 72 | 73 | // Append encrypted data to encrypted string 74 | encrypted += string(bufEncrypt[:sLen]) 75 | 76 | // Finalize the cipher to flush any remaining data 77 | crypto.EVP_EncryptFinal_ex(ctxEncrypt, bufEncrypt, &eLen) 78 | 79 | // Append any remaining data to the encrypted string 80 | encrypted += string(bufEncrypt[:eLen]) 81 | 82 | // Clean up the EVP_CIPHER_CTX 83 | crypto.EVP_CIPHER_CTX_cleanup(ctxEncrypt) 84 | 85 | /* 86 | Decrypting a string 87 | */ 88 | // Grab the IV from the encrypted string 89 | iv = string([]byte(encrypted)[:ivLen]) 90 | 91 | // Slice the encrypted string to begin after the iv 92 | encrypted = encrypted[ivLen:] 93 | 94 | // Initialize the ctxDecrypt context for decryption 95 | crypto.EVP_DecryptInit_ex(ctxDecrypt, crypto.EVP_aes_256_cbc(), crypto.SwigcptrStruct_SS_engine_st(0), "somekey", iv) 96 | 97 | // Make a buffer the exact size of the encrypted text 98 | bufDecrypt = make([]byte, len(encrypted)) 99 | 100 | // Update the cipher with the encrypted string 101 | crypto.EVP_DecryptUpdate(ctxDecrypt, bufDecrypt, &sLen, encrypted, len(encrypted)) 102 | 103 | // Append decrypted data to decrypted string 104 | decrypted = string(bufDecrypt[:sLen]) 105 | 106 | // Finalize the cipher to flush any remaining data 107 | crypto.EVP_DecryptFinal_ex(ctxDecrypt, bufDecrypt, &eLen) 108 | 109 | // Append any remaining data to decrypted string 110 | decrypted += string(bufDecrypt[:eLen]) 111 | 112 | // Print decoded string 113 | fmt.Printf("decrypted: %s\n", decrypted) 114 | 115 | // Clean up the EVP_CIPHER_CTX 116 | crypto.EVP_CIPHER_CTX_cleanup(ctxDecrypt) 117 | } 118 | -------------------------------------------------------------------------------- /examples/httpsclient.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/IBM-Bluemix/golang-openssl-wrapper/ssl" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | func main() { 11 | // Create a new HTTPS client 12 | client := ssl.NewHTTPSClient() 13 | 14 | // Call HTTP GET on the client 15 | response, err := client.Get("https://httpbin.org/ip") 16 | if err != nil { 17 | panic(err) 18 | } 19 | 20 | // Defer closing the response body so it isn't forgotten. 21 | defer response.Body.Close() 22 | 23 | // Read the entire body 24 | body, _ := ioutil.ReadAll(response.Body) 25 | 26 | // Convert body to string and trim newlines 27 | bstring := strings.Trim(string(body), "\n") 28 | 29 | // Print response 30 | fmt.Printf("https://httpbin.org/ip response:\n%s\n", bstring) 31 | } 32 | -------------------------------------------------------------------------------- /examples/httpstransport.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/IBM-Bluemix/golang-openssl-wrapper/ssl" 6 | "io/ioutil" 7 | "net/http" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | // Create a new HTTPS client 13 | client := http.Client{ 14 | // Use our transport for FIPS compliant OpenSSL 15 | Transport: ssl.NewHTTPSTransport(nil), 16 | } 17 | 18 | // Call HTTP GET on the client 19 | response, err := client.Get("https://httpbin.org/ip") 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | // Defer closing the response body so it isn't forgotten. 25 | defer response.Body.Close() 26 | 27 | // Read the entire body 28 | body, _ := ioutil.ReadAll(response.Body) 29 | 30 | // Convert body to string and trim newlines 31 | bstring := strings.Trim(string(body), "\n") 32 | 33 | // Print response 34 | fmt.Printf("https://httpbin.org/ip response:\n%s\n", bstring) 35 | } 36 | -------------------------------------------------------------------------------- /include/evp_typ.i: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #ifndef HEADER_ENVELOPE_H 4 | #include 5 | #endif 6 | %} 7 | /* 8 | * From openssl/engine.h 9 | */ 10 | 11 | typedef struct engine_st ENGINE; 12 | 13 | /* 14 | * From openssl/evp.h 15 | */ 16 | 17 | typedef struct env_md_st EVP_MD; 18 | typedef struct evp_pkey_ctx_st EVP_PKEY_CTX; 19 | 20 | typedef struct env_md_ctx_st { 21 | const EVP_MD *digest; 22 | ENGINE *engine; /* functional reference if 'digest' is 23 | * ENGINE-provided */ 24 | unsigned long flags; 25 | void *md_data; 26 | /* Public key context for sign/verify */ 27 | EVP_PKEY_CTX *pctx; 28 | /* Update function: usually copied from EVP_MD */ 29 | int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count); 30 | } EVP_MD_CTX; -------------------------------------------------------------------------------- /include/ossl_typemaps.i: -------------------------------------------------------------------------------- 1 | %include "typemaps.i" 2 | 3 | /* 4 | * Use for writeable buffers of type char* passed as arguments 5 | */ 6 | %typemap(gotype) char *CHARBUF %{[]byte%} 7 | %typemap(in) char *CHARBUF { 8 | if ($input.len <= 0 || $input.cap <= 0) $1 = NULL; 9 | else $1 = (char*)malloc($input.cap); 10 | } 11 | %typemap(argout) char *CHARBUF { 12 | if ($1 != NULL) memcpy($input.array, $1, $input.cap); 13 | } 14 | %typemap(freearg) char *CHARBUF { 15 | if ($1 != NULL) free($1); 16 | } 17 | 18 | %typemap(gotype) void *VOIDSTRINGBUF %{string%} 19 | %typemap(in) void *VOIDSTRINGBUF { 20 | $1 = (void*)malloc($input.n); 21 | memcpy($1, $input.p, $input.n); 22 | } 23 | %typemap(argout) void *VOIDSTRINGBUF { 24 | $input.p = $1; 25 | } 26 | %typemap(freearg) void *VOIDSTRINGBUF { 27 | free($1); 28 | } 29 | 30 | %typemap(gotype) int *OUTLEN %{*int%} 31 | -------------------------------------------------------------------------------- /rand/rand.go: -------------------------------------------------------------------------------- 1 | package rand 2 | 3 | // #cgo CFLAGS: -I/usr/local/ssl/include 4 | // #cgo LDFLAGS: -L /usr/local/ssl/lib -lcrypto 5 | import "C" 6 | -------------------------------------------------------------------------------- /rand/rand.swig: -------------------------------------------------------------------------------- 1 | %module rand 2 | 3 | %{ 4 | #include 5 | %} 6 | 7 | %include "../include/ossl_typemaps.i" 8 | 9 | %apply char *CHARBUF { unsigned char *outbuf }; 10 | 11 | // %typemap(gotype) (unsigned char *outbuf, int num) %{[]byte, int%} 12 | // %typemap(gotype) unsigned char *outbuf %{[]byte%} 13 | 14 | // %typemap(in) (unsigned char *outbuf) { 15 | // $1 = (char*)malloc($input.cap); 16 | // } 17 | // %typemap(argout) (unsigned char *outbuf) { 18 | // memcpy($input.array, $1, $input.cap); 19 | // } 20 | // %typemap(freearg) (unsigned char *outbuf) { 21 | // free($1); 22 | // } 23 | int RAND_bytes(unsigned char *outbuf, int num); -------------------------------------------------------------------------------- /rand/rand_suite_test.go: -------------------------------------------------------------------------------- 1 | package rand_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestRand(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Rand Suite") 13 | } 14 | -------------------------------------------------------------------------------- /rand/rand_test.go: -------------------------------------------------------------------------------- 1 | package rand_test 2 | 3 | import ( 4 | . "github.com/IBM-Bluemix/golang-openssl-wrapper/rand" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | var _ = Describe("Rand", func() { 11 | 12 | var seqlen int 13 | BeforeEach(func() { 14 | seqlen = 50 15 | }) 16 | 17 | Context("Using /dev/urandom for PRNG entropy", func() { 18 | It("Returns a valid/random sequence of bytes", func() { 19 | seqlen := 50 20 | buf := make([]byte, seqlen) 21 | Expect(RAND_bytes(buf, seqlen)).To(Equal(1)) 22 | Expect(len(string(buf))).To(Equal(50)) 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /rand/read.go: -------------------------------------------------------------------------------- 1 | package rand 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // Read populates buf with a pseudo-random sequence of bytes read from /dev/random. 8 | // This requires a system with /dev/random. 9 | // Behavior is as for golang's crypto/rand.Read() 10 | func Read(buf []byte) (int, error) { 11 | l := cap(buf) 12 | r := RAND_bytes(buf, l) 13 | if r != 1 { 14 | return 0, errors.New("Error generating random byte sequence") 15 | } 16 | 17 | if len(buf) != l { 18 | return 0, errors.New("Random byte sequence has wrong length") 19 | } 20 | 21 | return len(buf), nil 22 | } 23 | -------------------------------------------------------------------------------- /rand/read_test.go: -------------------------------------------------------------------------------- 1 | package rand_test 2 | 3 | import ( 4 | . "github.com/IBM-Bluemix/golang-openssl-wrapper/rand" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | var _ = Describe("Read", func() { 11 | var seqlen int 12 | BeforeEach(func() { 13 | seqlen = 50 14 | }) 15 | 16 | Context("Emulating the go native API", func() { 17 | var l1, l2 int 18 | var err error 19 | 20 | It("Returns a valid/random sequence of bytes", func() { 21 | buf := make([]byte, seqlen) 22 | l1, err = Read(buf) 23 | Expect(l1).To(Equal(len(buf))) 24 | Expect(err).NotTo(HaveOccurred()) 25 | 26 | newBuf := make([]byte, seqlen) 27 | l2, err = Read(newBuf) 28 | s1 := string(buf) 29 | s2 := string(newBuf) 30 | 31 | Expect(err).NotTo(HaveOccurred()) 32 | Expect(s1).NotTo(Equal(s2)) 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /scripts/install_openssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | UNAME=/bin/uname 4 | MAKE=/usr/bin/make 5 | CURL=/usr/bin/curl 6 | MKDIR=/bin/mkdir 7 | TAR=/bin/tar 8 | RM=/bin/rm 9 | OPENSSL_CMD=/usr/bin/openssl 10 | LN="/bin/ln -sf" 11 | 12 | WORKDIR=/tmp/install_openssl.$$ 13 | LOGFILE=/tmp/install_openssl_log.$$ 14 | 15 | DL_URL=https://www.openssl.org/source/ 16 | CURL_CMD="${CURL} -# -O ${DL_URL}" 17 | OSSL_REL=1.0.2d 18 | FIPS_REL=2.0.10 19 | 20 | OSSL=openssl-${OSSL_REL} 21 | 22 | # Installation paths 23 | if [[ $# -ge 1 ]]; then 24 | I_BASE=$1 25 | else 26 | I_BASE=/usr/local 27 | fi 28 | export FIPSDIR=${I_BASE}/ssl/fips-2.0 29 | 30 | OSSL_PREFIX=${I_BASE}/${OSSL} 31 | OSSL_CONFIG="./config --prefix=${OSSL_PREFIX} fips shared" 32 | 33 | # 34 | # Whoops, bug here 35 | # 36 | 37 | if [[ $1 = "ecp" ]]; then 38 | FIPS=openssl-fips-ecp-${FIPS_REL} 39 | else 40 | FIPS=openssl-fips-${FIPS_REL} 41 | fi 42 | 43 | cleanup() { 44 | local WD=$1 45 | cd /tmp 46 | log "INFO" "Removing working directory ${WD}" 47 | 48 | if [[ $WD != "/tmp/install_openssl*" ]]; then 49 | error "${WD} does not appear to be a valid working directory" 50 | else 51 | $RM -rf $WD && log "INFO" "Working directory ${WD} removed" 52 | fi 53 | } 54 | 55 | cleanup_install() { 56 | local INDIR=$1 57 | if [[ $INDIR != "" ]]; then 58 | 59 | # Installation failed, so remove the install dir, too 60 | if [[ ! -d $INDIR ]]; then 61 | error "${INDIR} not a directory, unable to uninstall" 62 | elif [[ $INDIR != "*/${OSSL}" && $INDIR != "*/${FIPS}" ]]; then 63 | error "${INDIR} does not appear to be a valid OpenSSL installation" 64 | else 65 | $RM -rf $INDIR 66 | log "INFO" "Installation at ${INDIR} removed" 67 | fi 68 | fi 69 | } 70 | 71 | # Return the OS, e.g. "Ubuntu" 72 | get_os() { 73 | local KERNELV=$(${UNAME} -v) 74 | local FLAVOR=${KERNELV#*-} 75 | FLAVOR=${FLAVOR%% *} 76 | printf $FLAVOR 77 | } 78 | 79 | # called if we encounter a fatal error 80 | fatal() { 81 | log "FATAL" "$1" 82 | exit 0 83 | } 84 | 85 | error() { 86 | log "ERROR" "$1" 87 | } 88 | 89 | warn() { 90 | log "WARN" "$1" 91 | } 92 | 93 | log() { 94 | local SEVERITY=$1 95 | local MSG=$2 96 | printf "%(%F %T)T" -1 97 | printf "\t%-7s %s\n" "$SEVERITY" "$MSG" 98 | } 99 | 100 | verify_checksum() { 101 | local PKG=$1 102 | local R=$2 103 | local S=1 104 | log "INFO" "Retrieving checksum with ${CURL} ${DL_URL}${PKG}.sha1" 105 | local SHA1=$(${CURL} ${DL_URL}${PKG}.sha1) 106 | local SHA1_LOCAL=$(${OPENSSL_CMD} sha1 ${PKG}) 107 | SHA1_LOCAL=${SHA1_LOCAL##*= } 108 | 109 | if [[ $SHA1 != $SHA1_LOCAL ]]; then 110 | warn "Downloaded checksum (${PKG}): ${SHA1}" 111 | warn "Calculated checksum (${PKG}): ${SHA1_LOCAL}" 112 | S=0 113 | else 114 | log "INFO" "Downloaded checksum (${PKG}): ${SHA1}" 115 | log "INFO" "Calculated checksum (${PKG}): ${SHA1_LOCAL}" 116 | fi 117 | 118 | eval $R="'$S'" 119 | } 120 | 121 | 122 | # 123 | # Main body 124 | # 125 | 126 | log "INFO" "Using ${LOGFILE} as log file" 127 | log "INFO" "Using ${LOGFILE}.err as stderr log file" 128 | 129 | $MKDIR $WORKDIR 130 | exec 1> $LOGFILE 131 | exec 2> ${LOGFILE}.err 132 | 133 | log "INFO" "Using ${LOGFILE} as log file" 134 | 135 | OS=$(get_os) 136 | 137 | # For now, we only support Ubuntu 138 | if [[ $OS != "Ubuntu" ]]; then 139 | # We'll need something better than this... 140 | # Should be a case/esac when we support other OSes 141 | fatal "Unsupported operating system: ${OS}" 142 | fi 143 | 144 | if [[ ! -x $CURL || ! -x $MAKE || ! -x $OPENSSL_CMD ]]; then 145 | fatal "Missing dependency, unable to proceed" 146 | fi 147 | 148 | cd $WORKDIR || fatal "Unable to cd to working directory ${WORKDIR}" 149 | 150 | log "INFO" "Downloading ${OSSL} source package with ${CURL_CMD}${OSSL}.tar.gz" 151 | ${CURL_CMD}${OSSL}.tar.gz 152 | log "INFO" "Downloading ${FIPS} source package with ${CURL_CMD}${FIPS}.tar.gz" 153 | ${CURL_CMD}${FIPS}.tar.gz 154 | 155 | log "INFO" "Verifying package checksums" 156 | 157 | verify_checksum ${OSSL}.tar.gz RET 158 | if [[ $RET -ne 1 ]]; then 159 | fatal "Checksums do not match for ${OSSL}.tar.gz" 160 | fi 161 | 162 | verify_checksum ${FIPS}.tar.gz RET 163 | if [[ $RET -ne 1 ]]; then 164 | fatal "Checksums do not match for ${FIPS}.tar.gz" 165 | fi 166 | 167 | log "INFO" "Checksums validated, proceeding" 168 | 169 | log "INFO" "Creating installation directories and symlinks" 170 | 171 | if [[ -d ${OSSL_PREFIX} ]]; then 172 | fatal "${OSSL_PREFIX} exists already, exiting" 173 | fi 174 | 175 | $MKDIR -p ${OSSL_PREFIX} || fatal "Unable to create installation directory ${OSSL_PREFIX}" 176 | 177 | if [[ -e ${I_BASE}/ssl && -L ${I_BASE}/ssl ]]; then 178 | warn "Symlink ${I_BASE}/ssl already exists, this installation will modify target" 179 | elif [[ -e ${I_BASE}/ssl ]]; then 180 | fatal "${I_BASE}/ssl exists and is not a symlink, exiting" 181 | fi 182 | 183 | $LN ${OSSL_PREFIX} ${I_BASE}/ssl || fatal "Unable to create symlink ${I_BASE}/ssl" 184 | 185 | log "INFO" "Building OpenSSL FIPS module" 186 | 187 | ${TAR} -xzf ${FIPS}.tar.gz 188 | 189 | if [[ ! -d ${FIPS} ]]; then 190 | fatal "Extraction of ${FIPS} failed" 191 | fi 192 | 193 | cd ${WORKDIR}/${FIPS} || fatal "Unable to cd to ${WORKDIR}/${FIPS}" 194 | 195 | ./config || { 196 | error "${WORKDIR}/${FIPS}/config returned error" 197 | fatal "Exiting" 198 | } 199 | 200 | $MAKE || { 201 | fatal "Error during build of ${FIPS}, exiting" 202 | } 203 | 204 | log "INFO" "Build completed, will attempt to install" 205 | 206 | if [[ -d ${I_BASE}/ssl/fips-2.0 ]]; then 207 | fatal "Previous installation of FIPS module found, unable to install" 208 | fi 209 | 210 | $MAKE install || { 211 | error "${MAKE} install encountered an error" 212 | cleanup_install ${I_BASE}/ssl/fips-2.0 213 | fatal "Exiting" 214 | } 215 | 216 | if [[ ! -d ${I_BASE}/ssl/fips-2.0 ]]; then 217 | fatal "FIPS module installed at unknown location, check build log" 218 | fi 219 | 220 | log "INFO" "Installation of FIPS module complete at ${I_BASE}/ssl/fips-2.0" 221 | 222 | export FIPSDIR=${I_BASE}/ssl/fips-2.0 223 | 224 | log "INFO" "Building FIPS-capable OpenSSL" 225 | 226 | cd ${WORKDIR} 227 | 228 | ${TAR} -xzf ${OSSL}.tar.gz 229 | 230 | if [[ ! -d ${OSSL} ]]; then 231 | fatal "Extraction of ${OSSL} failed" 232 | fi 233 | 234 | cd ${WORKDIR}/${OSSL} || fatal "Unable to cd to ${WORKDIR}/${OSSL}" 235 | 236 | ${OSSL_CONFIG} || fatal "Encountered error running ${OSSL_CONFIG}" 237 | 238 | $MAKE depend || fatal "Encountered error running ${MAKE} depend" 239 | 240 | $MAKE || fatal "Encountered error running $MAKE" 241 | 242 | log "INFO" "Build completed, will attempt to install" 243 | 244 | # if [[ -d ${OSSL_PREFIX} ]]; then 245 | # fatal "Previous installation of ${OSSL} found, unable to install at ${OSSL_PREFIX}" 246 | # fi 247 | 248 | $MAKE install || { 249 | error "Encountered error running ${MAKE} install" 250 | cleanup_install ${OSSL_PREFIX} 251 | fatal "Exiting" 252 | } 253 | 254 | log "INFO" "Installation of FIPS-capable OpenSSL complete at ${OSSL_PREFIX}" 255 | -------------------------------------------------------------------------------- /scripts/install_swig.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Script to install Swig and its prereqs 5 | # 6 | 7 | # DIRNAME=/usr/bin/dirname 8 | # READLINK=/bin/readlink 9 | # READLINKF="${READLINK} -f" 10 | 11 | # DNCMD='${DIRNAME} "$(${READLINKF} \"$0\")"' 12 | # SCRIPT_DIR=$(eval $DNCMD) 13 | 14 | # . ${SCRIPT_DIR}/scripts_profile 15 | UNAME=/bin/uname 16 | MAKE=/usr/bin/make 17 | CURL=/usr/bin/curl 18 | MKDIR=/bin/mkdir 19 | TAR=/bin/tar 20 | RM=/bin/rm 21 | OPENSSL_CMD=/usr/bin/openssl 22 | LN="/bin/ln -sf" 23 | 24 | get_os() { 25 | local KERNELV=$(${UNAME} -v) 26 | local FLAVOR=${KERNELV#*-} 27 | FLAVOR=${FLAVOR%% *} 28 | printf $FLAVOR 29 | } 30 | 31 | # called if we encounter a fatal error 32 | fatal() { 33 | log "FATAL" "$1" 34 | exit 0 35 | } 36 | 37 | error() { 38 | log "ERROR" "$1" 39 | } 40 | 41 | warn() { 42 | log "WARN" "$1" 43 | } 44 | 45 | log() { 46 | local SEVERITY=$1 47 | local MSG=$2 48 | printf "%(%F %T)T" -1 49 | printf "\t%-7s %s\n" "$SEVERITY" "$MSG" 50 | } 51 | 52 | # 53 | # Download PCRE 8.37 (we've had issues with PCRE2 and swig compatibility) 54 | # 55 | 56 | WORKDIR=/tmp/install_swig.$$ 57 | if [[ $# -ge 1 ]]; then 58 | INSTALL_PREFIX=$1 59 | else 60 | INSTALL_PREFIX=/usr/local 61 | fi 62 | 63 | log "INFO" "Installing SWIG and PCRE under $INSTALL_PREFIX" 64 | 65 | ${MKDIR} ${WORKDIR} || fatal "Unable to create ${WORKDIR}" 66 | cd ${WORKDIR} 67 | 68 | TARGZ=.tar.gz 69 | PCRE_URL_BASE=http://downloads.sourceforge.net/project/pcre/pcre/ 70 | PCREREL=8.37 71 | # /pcre-8.37.tar.gz?r=http%3A%2F%2Fsourceforge.net%2Fprojects%2Fpcre%2Ffiles%2Fpcre%2F8.37%2F&ts=1446483191&use_mirror=superb-dca2 72 | PCRE=pcre-${PCREREL} 73 | PCREPKG=${PCRE}${TARGZ} 74 | $CURL -O -L "${PCRE_URL_BASE}${PCREREL}/${PCREPKG}" 75 | 76 | # 77 | # Download SWIG 78 | # 79 | 80 | # SWIG_URL_BASE=http://prdownloads.sourceforge.net/swig/ 81 | SWIG_URL_BASE=http://downloads.sourceforge.net/project/swig/swig/ 82 | SWIGREL=3.0.7 83 | DL_REFERRER="r=http\%3A\%2F\%2Fsourceforge.net\%2Fprojects\%2Fswig\%2Ffiles\%2F\&ts=1446482403\&use_mirror=iweb" 84 | 85 | SWIG=swig-${SWIGREL} 86 | SWIGPKG=${SWIG}${TARGZ} 87 | 88 | 89 | # $CURL -O "${SWIG_URL_BASE}${SWIG}/${SWIGPKG}?${DL_REFERRER}" 90 | $CURL -O -L "${SWIG_URL_BASE}${SWIG}/${SWIGPKG}" 91 | 92 | # 93 | # Build 94 | # 95 | 96 | $TAR -xzf $PCREPKG 97 | cd $PCRE || fatal "$PCREPKG - unable to extract source package" 98 | ./configure --prefix=${INSTALL_PREFIX} 99 | $MAKE 100 | $MAKE install 101 | 102 | if [[ ! -x ${INSTALL_PREFIX}/bin/pcre-config ]]; then 103 | fatal "${INSTALL_PREFIX} not installed correctly, exiting" 104 | fi 105 | 106 | cd ${WORKDIR} 107 | $TAR -xzf $SWIGPKG 108 | cd $SWIG || fatal "$SWIGPKG - unable to extract source package" 109 | ./configure --prefix=${INSTALL_PREFIX} 110 | $MAKE 111 | $MAKE install 112 | if [[ ! -x ${INSTALL_PREFIX}/bin/swig ]]; then 113 | fatal "${INSTALL_PREFIX}/bin/swig not installed correctly, exiting" 114 | fi 115 | 116 | log "INFO" "Installation of ${SWIG} complete" -------------------------------------------------------------------------------- /scripts/scripts_profile: -------------------------------------------------------------------------------- 1 | # 2 | # scripts_profile 3 | # 4 | # Script to setup common variables, etc. for use in scripts 5 | # 6 | 7 | UNAME=/bin/uname 8 | MAKE=/usr/bin/make 9 | CURL=/usr/bin/curl 10 | MKDIR=/bin/mkdir 11 | TAR=/bin/tar 12 | RM=/bin/rm 13 | OPENSSL_CMD=/usr/bin/openssl 14 | LN="/bin/ln -sf" 15 | -------------------------------------------------------------------------------- /ssl/context.go: -------------------------------------------------------------------------------- 1 | package ssl 2 | 3 | import ( 4 | "errors" 5 | "github.com/IBM-Bluemix/golang-openssl-wrapper/crypto" 6 | ) 7 | 8 | func ctxInit(config string, method SSL_METHOD) (SSL_CTX, error) { 9 | SSL_load_error_strings() 10 | if SSL_library_init() != 1 { 11 | return nil, errors.New("Unable to initialize libssl") 12 | } 13 | crypto.OPENSSL_config(config) 14 | 15 | ctx := SSL_CTX_new(method) 16 | if ctx == nil { 17 | return nil, errors.New("Unable to initialize SSL context") 18 | } 19 | 20 | SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nil) 21 | SSL_CTX_set_verify_depth(ctx, 4) 22 | 23 | return ctx, nil 24 | } 25 | -------------------------------------------------------------------------------- /ssl/httpsclient.go: -------------------------------------------------------------------------------- 1 | package ssl 2 | 3 | /* 4 | * Module: httpsclient.go 5 | */ 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "github.com/IBM-Bluemix/golang-openssl-wrapper/bio" 11 | "net" 12 | "net/http" 13 | "net/url" 14 | "strings" 15 | "time" 16 | ) 17 | 18 | var networksAllowed = map[string]bool{ 19 | "tcp": true, 20 | "tcp4": true, 21 | "tcp6": true, 22 | "ip": true, 23 | "ip4": true, 24 | "ip6": true, 25 | } 26 | 27 | // HTTPSConn extends net.Conn to provide HTTPS functions using OpenSSL. 28 | type HTTPSConn struct { 29 | net.Conn 30 | desthost string 31 | connected bool 32 | ctx SSL_CTX 33 | sslInst SSL 34 | sslBio bio.BIO 35 | remoteAddr *net.TCPAddr 36 | localAddr *net.TCPAddr 37 | } 38 | 39 | // Read reads n bytes from the connection into b. 40 | // Read returns the number of bytes read or 0 and an error if the underlying read fails. 41 | func (h HTTPSConn) Read(b []byte) (n int, err error) { 42 | ret := bio.BIO_read(h.sslBio, b, len(b)) 43 | if ret < 0 { 44 | return ret, fmt.Errorf("Possible socket read error - got %d from BIO_read()", ret) 45 | } 46 | return ret, nil 47 | } 48 | 49 | // Write writes n bytes from b onto the connection. 50 | // Write returns the number of bytes written and any error that occurred. 51 | func (h HTTPSConn) Write(b []byte) (n int, err error) { 52 | ret := bio.BIO_write(h.sslBio, string(b), len(b)) 53 | if ret != len(b) { 54 | return ret, fmt.Errorf("SSL socket write failed; only %d bytes written out of %d", ret, len(b)) 55 | } 56 | 57 | return ret, nil 58 | } 59 | 60 | // Close closes the underlying connection. 61 | // Close will return an error if it is invoked on a partially or already closed connection. 62 | func (h HTTPSConn) Close() error { 63 | if (h.ctx != nil) && (h.sslInst != nil) && (h.sslBio != nil) { 64 | SSL_CTX_free(h.ctx) 65 | h.ctx = nil 66 | SSL_free(h.sslInst) 67 | h.sslInst = nil 68 | bio.BIO_free_all(h.sslBio) 69 | h.sslBio = nil 70 | return nil 71 | } 72 | 73 | if (h.ctx != nil) || (h.sslInst != nil) || (h.sslBio != nil) { 74 | return errors.New("HTTPSConn in partially closed state, not all objects freed, unable to close further") 75 | } 76 | 77 | return errors.New("Attempted to close already closed HTTPSConn") 78 | } 79 | 80 | // LocalAddr returns the local address as a net.Addr. 81 | func (h HTTPSConn) LocalAddr() net.Addr { 82 | return h.localAddr 83 | } 84 | 85 | // RemoteAddr returns the remote address for the connection as a net.Addr. 86 | func (h HTTPSConn) RemoteAddr() net.Addr { 87 | return h.remoteAddr 88 | } 89 | 90 | func validateDeadline(t time.Time) error { 91 | now := time.Now() 92 | if t.Equal(now) || t.Before(now) { 93 | return errors.New("Invalid deadline") 94 | } 95 | 96 | if t.After(now.Add(time.Duration(10) * time.Minute)) { 97 | return errors.New("Deadline beyond allowed horizon") 98 | } 99 | 100 | return nil 101 | } 102 | 103 | // TODO: implement Set[{Read,Write}]Deadline 104 | 105 | // // SetDeadLine sets the deadline for both reads and writes. 106 | // // t should be a time.Time representing a relative interval, such as 10 minutes. 107 | // // SetDeadline, SetReadDeadline, and SetWriteDeadLine will all return an error 108 | // // if t equals the current time or is before it. 109 | // // They will also return an error for an interval longer than 10 minutes. 110 | // func (h HTTPSConn) SetDeadLine(t time.Time) error { 111 | // return validateDeadline(t) 112 | // } 113 | 114 | // // SetReadDeadLine sets the deadline for reads. 115 | // func (h HTTPSConn) SetReadDeadLine(t time.Time) error { 116 | // return validateDeadline(t) 117 | // } 118 | 119 | // // SetWriteDeadLine sets the deadline for writes. 120 | // func (h HTTPSConn) SetWriteDeadLine(t time.Time) error { 121 | // return validateDeadline(t) 122 | // } 123 | 124 | /* 125 | * Setup the Transport 126 | */ 127 | 128 | func dial(network, addr string) (net.Conn, error) { 129 | return dialTLS(network, addr) 130 | } 131 | 132 | /* 133 | * dialTLS() Returns an httpsclient.HTTPSConn instance. 134 | * We inject the BIO object for use in I/O. 135 | * We inject the CTX and SSL objects for use in connection 136 | * management. 137 | */ 138 | func dialTLS(network, addr string) (net.Conn, error) { 139 | var err error 140 | var ctx SSL_CTX 141 | var conn bio.BIO 142 | var dest, dhost, dport string 143 | var ra *net.TCPAddr 144 | 145 | if !networksAllowed[network] { 146 | return nil, fmt.Errorf("Invalid network specified: %q", network) 147 | } 148 | 149 | cc := strings.Count(addr, ":") 150 | switch { 151 | case cc == 0: 152 | /* Default is port 443 */ 153 | dhost = addr 154 | dport = "443" 155 | case cc == 1: 156 | dhost, dport, err = net.SplitHostPort(addr) 157 | if err != nil { 158 | return nil, errors.New("Unable to parse address") 159 | } 160 | case cc > 1: 161 | return nil, errors.New("Invalid address specified") 162 | } 163 | dest = net.JoinHostPort(dhost, dport) 164 | 165 | ra, err = net.ResolveTCPAddr(network, dest) 166 | if err != nil { 167 | return nil, fmt.Errorf("Unable to resolve address %s on network %s", dest, network) 168 | } 169 | 170 | ctx, err = ctxInit("", SSLv23_client_method()) 171 | if err != nil { 172 | return nil, err 173 | } 174 | 175 | conn, err = sslInit(ctx, dest) 176 | if err != nil { 177 | return nil, err 178 | } 179 | 180 | err = connect(conn) 181 | if err != nil { 182 | return nil, err 183 | } 184 | 185 | h := HTTPSConn{ 186 | desthost: addr, 187 | ctx: ctx, 188 | sslBio: conn, 189 | remoteAddr: ra, 190 | } 191 | return h, nil 192 | 193 | } 194 | 195 | // NewHTTPSClient returns an http.Client configured to use OpenSSL for TLS. 196 | // The client cannot be used for non-TLS communications - use a regular http.Client instead. 197 | // This is a convenience function wrapping NewHTTPSTransport. 198 | func NewHTTPSClient() http.Client { 199 | return http.Client{ 200 | Transport: NewHTTPSTransport(nil), 201 | } 202 | } 203 | 204 | // NewHTTPSTransport returns an http.Transport configured to use OpenSSL for TLS. 205 | // The transport cannot be used for non-TLS communications - use a regular http.Transport instead. 206 | func NewHTTPSTransport(proxyFunc func(*http.Request) (*url.URL, error)) *http.Transport { 207 | h := &http.Transport{ 208 | Dial: dialTLS, 209 | DialTLS: dialTLS, 210 | Proxy: proxyFunc, 211 | } 212 | return h 213 | } 214 | 215 | func sslInit(ctx SSL_CTX, hostname string) (bio.BIO, error) { 216 | /* Initialize the SSL and connect BIOs */ 217 | conn := bio.BIO_new_ssl_connect(ctx) 218 | if conn == nil { 219 | return nil, errors.New("Unable to setup I/O") 220 | } 221 | 222 | if SSL_CTX_load_verify_locations(ctx, "", "/etc/ssl/certs") != 1 { 223 | return nil, errors.New("Unable to load certificates for verification") 224 | } 225 | if bio.BIO_set_conn_hostname(conn, hostname) != 1 { 226 | return nil, errors.New("Unable to set hostname in BIO object") 227 | } 228 | 229 | /* Setup SSL */ 230 | sslInst := SSL_new(ctx) 231 | if sslInst == nil { 232 | return nil, errors.New("Unable to initialize SSL") 233 | } 234 | 235 | if bio.BIO_get_ssl(conn, sslInst) != 1 { 236 | return nil, errors.New("Unable to configure SSL for I/O") 237 | } 238 | 239 | ciphers := "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4" 240 | if SSL_set_cipher_list(sslInst, ciphers) != 1 { 241 | return nil, errors.New("Unable to configure ciphers") 242 | } 243 | 244 | if SSL_set_tlsext_host_name(sslInst, hostname) != 1 { 245 | return nil, errors.New("Unable to set SSL hostname") 246 | } 247 | 248 | return conn, nil 249 | } 250 | 251 | /* Make the connection */ 252 | func connect(conn bio.BIO) error { 253 | if bio.BIO_do_connect(conn) != 1 { 254 | return errors.New("Unable to connect to SSL destination") 255 | } 256 | 257 | if bio.BIO_do_handshake(conn) != 1 { 258 | return errors.New("Unable to complete SSL handshake") 259 | } 260 | 261 | return nil 262 | } 263 | -------------------------------------------------------------------------------- /ssl/httpsclient_test.go: -------------------------------------------------------------------------------- 1 | package ssl_test 2 | 3 | import ( 4 | . "github.com/IBM-Bluemix/golang-openssl-wrapper/ssl" 5 | 6 | "fmt" 7 | . "github.com/onsi/ginkgo" 8 | . "github.com/onsi/gomega" 9 | "io/ioutil" 10 | "net/http" 11 | // "net/url" 12 | "net" 13 | "strings" 14 | ) 15 | 16 | var _ = Describe("Httpsclient", func() { 17 | 18 | var t *http.Transport 19 | var h HTTPSConn 20 | var host, resource string 21 | var respLen int 22 | var port, ua, dest, requestContent string 23 | // var server *httptest.Server 24 | 25 | BeforeEach(func() { 26 | host = "www.random.org" 27 | respLen = 8 28 | resource = fmt.Sprintf("/strings/?num=1&len=%d&digits=on&upperalpha=on&loweralpha=on&unique=on&format=plain&rnd=new", respLen) 29 | }) 30 | 31 | // AfterEach(func() { 32 | // server.Close() 33 | // }) 34 | 35 | Context("Using the golang http.Client", func() { 36 | It("Should fetch a resource successfully", func() { 37 | client := NewHTTPSClient() 38 | urlPath := "https://" + host + resource 39 | response, err := client.Get(urlPath) 40 | Expect(err).To(BeNil()) 41 | 42 | defer response.Body.Close() 43 | body, _ := ioutil.ReadAll(response.Body) 44 | bstring := strings.Trim(string(body), "\n") 45 | Expect(len(bstring)).To(Equal(respLen)) 46 | }) 47 | }) 48 | 49 | Context("Working directly with the underlying Transport", func() { 50 | BeforeEach(func() { 51 | port = "443" 52 | ua = "https://github.com/IBM-Bluemix/golang-openssl-wrapper" 53 | /* Fetch a single 8 character string in plaintext format */ 54 | requestContent = strings.Join([]string{ 55 | fmt.Sprintf("GET %s HTTP/1.1", resource), 56 | fmt.Sprintf("User-Agent: %s", ua), 57 | fmt.Sprintf("Host: %s", host), 58 | "Accept: */*", 59 | "\r\n", 60 | }, "\r\n") 61 | dest = host + ":" + port 62 | 63 | t = NewHTTPSTransport(nil) 64 | Expect(t).NotTo(BeNil()) 65 | conn, err := t.Dial("tcp", dest) 66 | Expect(err).NotTo(HaveOccurred()) 67 | h = conn.(HTTPSConn) 68 | }) 69 | 70 | Context("Using a bogus hostname and/or IP address", func() { 71 | var ip string 72 | JustBeforeEach(func() { 73 | /* Be sure your internal DNS isn't actually the SOA for ".bogus" */ 74 | host = "bogushost.bogus" 75 | dest = host + ":" + port 76 | /* Change this to an IP which cannot be resolved, which may depend on your specific network configuration */ 77 | ip = "10.100.10.100" 78 | }) 79 | 80 | It("Errors when the hostname is unresolveable", func() { 81 | conn, err := t.Dial("tcp", dest) 82 | Expect(conn).To(BeNil()) 83 | Expect(err).To(HaveOccurred()) 84 | }) 85 | 86 | It("Errors when the hostname is malformed", func() { 87 | baddest := dest + ":" 88 | conn, err := t.Dial("tcp", baddest) 89 | Expect(conn).To(BeNil()) 90 | Expect(err).To(HaveOccurred()) 91 | }) 92 | }) 93 | 94 | It("Should return the correct remote address", func() { 95 | match := false 96 | /* Build a list of the addresses for host */ 97 | addrs, err := net.LookupHost(host) 98 | Expect(len(addrs)).To(BeNumerically(">=", 1)) 99 | Expect(err).NotTo(HaveOccurred()) 100 | 101 | ra, _, _ := net.SplitHostPort(h.RemoteAddr().String()) 102 | 103 | for _, a := range addrs { 104 | if a == ra { 105 | match = true 106 | } 107 | } 108 | Expect(match).To(Equal(true)) 109 | }) 110 | 111 | It("Should error for an invalid network type", func() { 112 | conn, err := t.Dial("bogus", "www.google.com:443") 113 | Expect(conn).To(BeNil()) 114 | Expect(err).To(HaveOccurred()) 115 | }) 116 | 117 | Context("Performing socket I/O", func() { 118 | AfterEach(func() { 119 | h.Close() 120 | }) 121 | 122 | It("Should write to the connection and read the response", func() { 123 | wb := []byte(requestContent) 124 | Expect(h.Write(wb)).To(BeNumerically(">=", len(wb))) 125 | rb := make([]byte, 50) 126 | Expect(h.Read(rb)).To(BeNumerically(">", 0)) 127 | }) 128 | }) 129 | 130 | Context("Connection management", func() { 131 | It("Should not allow closing of an already closed connection", func() { 132 | h.Close() 133 | Expect(h.Close()).NotTo(Succeed()) 134 | }) 135 | 136 | }) 137 | 138 | // TODO: use these tests when Set[{Read,Write}]Deadline is implemented 139 | // Context("Setting deadlines", func() { 140 | // var now time.Time 141 | // BeforeEach(func() { 142 | // now = time.Now() 143 | // }) 144 | 145 | // It("Should not allow setting a deadline equal or or before the current time", func() { 146 | // bogus := now.Add(time.Duration(10) * time.Second * (-1)) 147 | // Expect(h.SetDeadLine(bogus)).NotTo(Succeed()) 148 | // Expect(h.SetReadDeadLine(bogus)).NotTo(Succeed()) 149 | // Expect(h.SetWriteDeadLine(bogus)).NotTo(Succeed()) 150 | // }) 151 | 152 | // It("Should not allow setting a deadline more than ten (10) minutes in the future", func() { 153 | // bogus := now.Add(time.Duration(11) * time.Minute) 154 | // Expect(h.SetDeadLine(bogus)).NotTo(Succeed()) 155 | // Expect(h.SetReadDeadLine(bogus)).NotTo(Succeed()) 156 | // Expect(h.SetWriteDeadLine(bogus)).NotTo(Succeed()) 157 | // }) 158 | 159 | // // TODO: Specs for checking that deadlines, having been set, are observed 160 | // }) 161 | }) 162 | }) 163 | -------------------------------------------------------------------------------- /ssl/httpsserver.go: -------------------------------------------------------------------------------- 1 | package ssl 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fmt" 7 | "log" 8 | "net" 9 | "net/http" 10 | "os" 11 | "path/filepath" 12 | ) 13 | 14 | /* 15 | Handle is shorthand for: 16 | http.DefaultServeMux.Handle(pattern, handler) 17 | */ 18 | func Handle(pattern string, handler http.Handler) { 19 | http.DefaultServeMux.Handle(pattern, handler) 20 | } 21 | 22 | /* 23 | HandleFunc is shorthand for: 24 | http.DefaultServeMux.Handler(pattern, handler) 25 | */ 26 | func HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) { 27 | http.DefaultServeMux.HandleFunc(pattern, handler) 28 | } 29 | 30 | /* 31 | Conn is used for incoming server connections. 32 | */ 33 | type Conn struct { 34 | net.Conn 35 | 36 | ctx SSL 37 | f *os.File 38 | fd int 39 | } 40 | 41 | /* 42 | Close will free any contexts, files, and connections that are 43 | associated with the Conn. 44 | */ 45 | func (c Conn) Close() error { 46 | var e error 47 | 48 | SSL_free(c.ctx) 49 | 50 | if e = c.Conn.Close(); e != nil { 51 | return e 52 | } 53 | if e = c.f.Close(); e != nil { 54 | return e 55 | } 56 | return nil 57 | } 58 | 59 | func (c Conn) getHandshake() error { 60 | if SSL_accept(c.ctx) < 0 { 61 | return errors.New("SSL handshake unsuccessful") 62 | } 63 | 64 | return nil 65 | } 66 | 67 | /* 68 | Read will use SSL_read to read directly from the file descriptor 69 | of the open connection. 70 | */ 71 | func (c Conn) Read(buf []byte) (int, error) { 72 | result := SSL_read(c.ctx, buf, len(buf)) 73 | if result > 0 { 74 | return result, nil 75 | } 76 | return 0, errors.New("Unable to read from connection.") 77 | } 78 | 79 | /* 80 | Write will use SSL_write to write directly to the file descriptor 81 | of the open connection. 82 | */ 83 | func (c Conn) Write(buf []byte) (int, error) { 84 | result := SSL_write(c.ctx, buf, len(buf)) 85 | if result > 0 { 86 | return result, nil 87 | } 88 | return 0, errors.New("Unable to write to connection.") 89 | } 90 | 91 | type response struct { 92 | Conn 93 | 94 | Headers http.Header 95 | 96 | req *http.Request 97 | sentStatus bool 98 | wroteHeaders bool 99 | } 100 | 101 | func (r *response) Close() error { 102 | if e := r.req.Body.Close(); e != nil { 103 | return e 104 | } 105 | return r.Conn.Close() 106 | } 107 | 108 | func (r *response) Header() http.Header { 109 | return r.Headers 110 | } 111 | 112 | func (r *response) Write(buf []byte) (int, error) { 113 | if !r.sentStatus { 114 | r.WriteHeader(http.StatusOK) 115 | } 116 | 117 | if !r.wroteHeaders { 118 | r.wroteHeaders = true 119 | r.Headers.Write(r.Conn) 120 | r.Write([]byte("\r\n")) 121 | } 122 | 123 | return r.Conn.Write(buf) 124 | } 125 | 126 | func (r *response) WriteHeader(s int) { 127 | r.sentStatus = true 128 | r.Conn.Write([]byte(fmt.Sprintf("HTTP/1.1 %d %s\r\n", s, http.StatusText(s)))) 129 | } 130 | 131 | /* 132 | Server mimics the original http.Server. It provides the same methods 133 | as the original http.Server to be a drop-in replacement. 134 | */ 135 | type Server struct { 136 | Addr string 137 | Handler http.Handler 138 | ErrorLog *log.Logger 139 | 140 | ctx SSL_CTX 141 | listener net.Listener 142 | method SSL_METHOD 143 | keepalive bool 144 | } 145 | 146 | /* 147 | ListenAndServeTLS will create a new Server and call ListenAndServeTLS 148 | on the Server instance. 149 | 150 | cf should be an absolute or relative path to the certificate file. 151 | kf should be an absolute or relative path to the key file. 152 | */ 153 | func ListenAndServeTLS(addr, cf, kf string, handler http.Handler) (*Server, error) { 154 | s := &Server{ 155 | Addr: addr, 156 | Handler: handler, 157 | } 158 | 159 | return s, s.ListenAndServeTLS(cf, kf) 160 | } 161 | 162 | /* 163 | ListenAndServe is not supported by this package and will always return 164 | a hardcoded error saying so. We do not support unencrypted traffic in 165 | this module. 166 | */ 167 | func (s *Server) ListenAndServe() error { 168 | return errors.New("You must use ListenAndServeTLS with this package. It does not support unencrypted HTTP traffic.") 169 | } 170 | 171 | /* 172 | ListenAndServeTLS will setup default values, contexts, the certificate, 173 | and key file. It will then listen on the Addr set in the Server instance and 174 | call Server.Serve. 175 | 176 | cf should be an absolute or relative path to the certificate file. 177 | kf should be an absolute or relative path to the key file. 178 | */ 179 | func (s *Server) ListenAndServeTLS(cf, kf string) error { 180 | var ( 181 | e error 182 | ) 183 | 184 | if s.method == nil { 185 | s.method = SSLv23_server_method() 186 | } 187 | 188 | if s.Handler == nil { 189 | s.Handler = http.DefaultServeMux 190 | } 191 | 192 | if s.ErrorLog == nil { 193 | s.ErrorLog = log.New(os.Stdout, "server: ", log.LstdFlags|log.Lshortfile) 194 | } 195 | 196 | cf, e = filepath.Abs(cf) 197 | if e != nil { 198 | return e 199 | } 200 | 201 | kf, e = filepath.Abs(kf) 202 | if e != nil { 203 | return e 204 | } 205 | 206 | ctx, e := ctxInit("", s.method) 207 | if e != nil { 208 | return e 209 | } 210 | 211 | if SSL_CTX_use_certificate_file(ctx, cf, SSL_FILETYPE_PEM) <= 0 { 212 | return errors.New("Could not use certificate file") 213 | } 214 | 215 | if SSL_CTX_use_PrivateKey_file(ctx, kf, SSL_FILETYPE_PEM) <= 0 { 216 | return errors.New("Could not use key file") 217 | } 218 | 219 | if SSL_CTX_check_private_key(ctx) < 1 { 220 | return errors.New("Private key does not match the public certificate") 221 | } 222 | 223 | l, e := net.Listen("tcp", s.Addr) 224 | if e != nil { 225 | return e 226 | } 227 | 228 | s.ctx = ctx 229 | 230 | return s.Serve(l) 231 | } 232 | 233 | /* 234 | Serve will accept connections on the net.Listener provided. It will then call 235 | Server.Handler.ServeHTTP on the resulting connection. 236 | 237 | You should not close the connection in Server.Handler.ServeHTTP. The 238 | connection will be automatically closed once Server.Handler.ServeHTTP 239 | has finished. 240 | */ 241 | func (s *Server) Serve(l net.Listener) error { 242 | var ( 243 | c net.Conn 244 | e error 245 | f *os.File 246 | ) 247 | 248 | check := func(e error) { 249 | if e != nil { 250 | s.ErrorLog.Printf("ERROR: %s\n", e) 251 | } 252 | } 253 | 254 | s.listener = l 255 | 256 | for { 257 | c, e = s.listener.Accept() 258 | check(e) 259 | 260 | switch c.(type) { 261 | case *net.IPConn: 262 | f, e = c.(*net.IPConn).File() 263 | case *net.TCPConn: 264 | f, e = c.(*net.TCPConn).File() 265 | case *net.UDPConn: 266 | f, e = c.(*net.UDPConn).File() 267 | case *net.UnixConn: 268 | f, e = c.(*net.UnixConn).File() 269 | } 270 | 271 | check(e) 272 | 273 | c = Conn{ 274 | Conn: c, 275 | ctx: SSL_new(s.ctx), 276 | f: f, 277 | fd: int(f.Fd()), 278 | } 279 | 280 | oc := c.(Conn) 281 | SSL_set_fd(oc.ctx, oc.fd) 282 | 283 | check(oc.getHandshake()) 284 | 285 | buf := bufio.NewReader(oc) 286 | req, e := http.ReadRequest(buf) 287 | check(e) 288 | 289 | res := &response{ 290 | Conn: oc, 291 | Headers: make(http.Header), 292 | req: req, 293 | } 294 | 295 | s.Handler.ServeHTTP(res, req) 296 | 297 | check(req.Body.Close()) 298 | 299 | check(oc.Close()) 300 | } 301 | 302 | return nil 303 | } 304 | 305 | func (s *Server) SetKeepAlivesEnabled(v bool) { 306 | s.keepalive = v 307 | } 308 | -------------------------------------------------------------------------------- /ssl/httpsserver_test.go: -------------------------------------------------------------------------------- 1 | package ssl_test 2 | 3 | import ( 4 | . "github.com/IBM-Bluemix/golang-openssl-wrapper/ssl" 5 | 6 | "os" 7 | "os/exec" 8 | "time" 9 | 10 | . "github.com/onsi/ginkgo" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | var c *exec.Cmd 15 | var _ = BeforeSuite(compileAndStartServer) 16 | var _ = AfterSuite(cleanup) 17 | 18 | var _ = Describe("Httpsserver", func() { 19 | It("Should get a valid response from the /aloha endpoint", func() { 20 | client := NewHTTPSClient() 21 | url := "https://localhost:8443/aloha" 22 | res, e := client.Get(url) 23 | Expect(e).To(BeNil()) 24 | Expect(res).NotTo(BeNil()) 25 | 26 | body := make([]byte, 200) 27 | i, e := res.Body.Read(body) 28 | Expect(e).To(BeNil()) 29 | Expect(i).To(BeNumerically(">", 0)) 30 | Expect(string(body[:i])).To(Equal("ALOHA!!")) 31 | Expect(res.Body.Close()).To(BeNil()) 32 | }) 33 | 34 | It("Should get a valid response from the /server endpoint", func() { 35 | client := NewHTTPSClient() 36 | url := "https://localhost:8443/server" 37 | res, e := client.Get(url) 38 | Expect(e).To(BeNil()) 39 | Expect(res).NotTo(BeNil()) 40 | 41 | Expect(res.Header.Get("Server")).To(Equal("https://github.com/IBM-Bluemix/golang-openssl-wrapper")) 42 | Expect(res.Body.Close()).To(BeNil()) 43 | }) 44 | 45 | It("Should get a valid response from the /mux endpoint", func() { 46 | client := NewHTTPSClient() 47 | url := "https://localhost:8443/mux" 48 | res, e := client.Get(url) 49 | Expect(e).To(BeNil()) 50 | Expect(res).NotTo(BeNil()) 51 | 52 | body := make([]byte, 200) 53 | i, e := res.Body.Read(body) 54 | Expect(e).To(BeNil()) 55 | Expect(i).To(BeNumerically(">", 0)) 56 | Expect(string(body[:i])).To(Equal("Using gorilla/mux")) 57 | Expect(res.Body.Close()).To(BeNil()) 58 | }) 59 | }) 60 | 61 | func cleanup() { 62 | c.Process.Kill() 63 | } 64 | 65 | func compileAndStartServer() { 66 | check := func(e error) { 67 | if e != nil { 68 | panic(e) 69 | } 70 | } 71 | 72 | check(os.Chdir("tests")) 73 | 74 | // Compile HTTPSServer 75 | c = exec.Command("go", "build", "httpsserver.go") 76 | check(c.Start()) 77 | check(c.Wait()) 78 | 79 | // Run HTTPSServer 80 | c = exec.Command("./httpsserver") 81 | check(c.Start()) 82 | 83 | time.Sleep(2 * time.Second) 84 | } 85 | -------------------------------------------------------------------------------- /ssl/ssl.go: -------------------------------------------------------------------------------- 1 | package ssl 2 | // #cgo CFLAGS: -I/usr/local/ssl/include 3 | // #cgo LDFLAGS: -L /usr/local/ssl/lib -lssl 4 | import "C" 5 | -------------------------------------------------------------------------------- /ssl/ssl.swig: -------------------------------------------------------------------------------- 1 | /* 2 | * SWIG interface file for libssl 3 | */ 4 | %module ssl 5 | %{ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | %} 12 | 13 | %include "../include/ossl_typemaps.i" 14 | 15 | #define SSL_FILETYPE_PEM 1 16 | #define SSL_FILETYPE_ASN1 2 17 | 18 | /* 19 | * Conditional compilation directives for stuff we don't want to support yet 20 | */ 21 | 22 | #define OPENSSL_NO_KRB5 23 | 24 | void SSL_load_error_strings(void); 25 | int SSL_library_init(void); 26 | 27 | SSL_CTX *SSL_CTX_new(const SSL_METHOD *method); 28 | void SSL_CTX_free(SSL_CTX *ctx); 29 | 30 | const SSL_METHOD *SSLv3_method(void); /* SSLv3 */ 31 | const SSL_METHOD *SSLv3_server_method(void); /* SSLv3 */ 32 | const SSL_METHOD *SSLv3_client_method(void); /* SSLv3 */ 33 | 34 | const SSL_METHOD *SSLv23_method(void); /* SSLv3 */ 35 | const SSL_METHOD *SSLv23_server_method(void); /* SSLv3 */ 36 | const SSL_METHOD *SSLv23_client_method(void); /* SSLv3 */ 37 | 38 | // The at https://www.openssl.org/docs/manmaster/ssl/SSL_CTX_new.html say 39 | // that the SSLv23_*method functions are deprecated, use TLS* instead, but 40 | // TLS* is not present in the most recent ssl.h nor in libssl itself. be 41 | // wary of OpenSSL docs... 42 | // const SSL_METHOD *TLS_method(void); 43 | // const SSL_METHOD *TLS_server_method(void); 44 | // const SSL_METHOD *TLS_client_method(void); 45 | 46 | /* Constants for the mode argument to SSL_CTX_set_verify */ 47 | #define SSL_VERIFY_NONE 0x00 48 | #define SSL_VERIFY_PEER 0x01 49 | #define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02 50 | #define SSL_VERIFY_CLIENT_ONCE 0x04 51 | 52 | void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, 53 | int (*callback) (int, X509_STORE_CTX *)); 54 | void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth); 55 | 56 | long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg); 57 | 58 | // long SSL_CTX_set_options(SSL_CTX *ctx, long op); 59 | 60 | 61 | // #define SSL_OP_NO_SSLv2 0x01000000L 62 | // #define SSL_OP_NO_SSLv3 0x02000000L 63 | // #define SSL_OP_NO_COMPRESSION 0x00020000L 64 | 65 | int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, 66 | const char *CApath); 67 | 68 | SSL *SSL_new(SSL_CTX *ctx); 69 | int SSL_accept(SSL *ssl); 70 | 71 | void SSL_free(SSL *ssl); 72 | int SSL_set_cipher_list(SSL *ssl, const char *str); 73 | long SSL_set_tlsext_host_name(SSL *ssl, char *name); 74 | int SSL_connect(SSL *ssl); 75 | int SSL_get_error(SSL *ssl, int ret); 76 | int SSL_set_fd(SSL *ssl, int fd); 77 | int SSL_get_fd(SSL *ssl); 78 | 79 | int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type); 80 | int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type); 81 | int SSL_CTX_check_private_key(const SSL_CTX *ctx); 82 | 83 | %typemap(gotype) const void *buf %{[]byte%} 84 | int SSL_write(SSL *ssl, const void *buf, int num); 85 | 86 | %apply char *CHARBUF { void *buf }; 87 | int SSL_read(SSL *ssl, void *buf, int num); 88 | -------------------------------------------------------------------------------- /ssl/ssl_suite_test.go: -------------------------------------------------------------------------------- 1 | package ssl_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestSsl(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Ssl Suite") 13 | } 14 | -------------------------------------------------------------------------------- /ssl/ssl_test.go: -------------------------------------------------------------------------------- 1 | package ssl_test 2 | 3 | import ( 4 | . "github.com/IBM-Bluemix/golang-openssl-wrapper/ssl" 5 | 6 | "github.com/IBM-Bluemix/golang-openssl-wrapper/bio" 7 | "github.com/IBM-Bluemix/golang-openssl-wrapper/crypto" 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | var _ = Describe("ssl", func() { 13 | Context("Using TLS for connections", func() { 14 | 15 | /* 16 | * Do some basic initialization 17 | */ 18 | BeforeEach(func() { 19 | SSL_load_error_strings() 20 | Expect(SSL_library_init()).To(Equal(1)) 21 | crypto.OPENSSL_config("") 22 | }) 23 | 24 | // AfterEach(func() { 25 | // SSL_free(ssl) 26 | // SSL_CTX_free(ctx) 27 | // }) 28 | 29 | Context("Making a client connection", func() { 30 | var ctx SSL_CTX 31 | var sslInst SSL 32 | var conn bio.BIO 33 | var host, hostport string 34 | 35 | BeforeEach(func() { 36 | ctx = SSL_CTX_new(SSLv23_method()) 37 | Expect(ctx).NotTo(BeNil()) 38 | SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nil) 39 | SSL_CTX_set_verify_depth(ctx, 4) 40 | Expect(SSL_CTX_load_verify_locations(ctx, "", "/etc/ssl/certs")).To(Equal(1)) 41 | sslInst = SSL_new(ctx) 42 | Expect(sslInst).NotTo(BeNil()) 43 | }) 44 | 45 | AfterEach(func() { 46 | bio.BIO_free_all(conn) 47 | SSL_free(sslInst) 48 | SSL_CTX_free(ctx) 49 | }) 50 | 51 | It("Connects to a known site", func() { 52 | host = "www.random.org" 53 | hostport = "www.random.org:443" 54 | 55 | /* Setup the connect BIO, since we're a client */ 56 | conn = bio.BIO_new_ssl_connect(ctx) 57 | Expect(conn).NotTo(BeNil()) 58 | Expect(bio.BIO_set_conn_hostname(conn, hostport)).To(BeEquivalentTo(1)) 59 | Expect(bio.BIO_get_conn_hostname(conn)).To(Equal(hostport)) 60 | 61 | /* Setup SSL */ 62 | Expect(bio.BIO_get_ssl(conn, sslInst)).To(BeEquivalentTo(1)) 63 | ciphers := "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4" 64 | Expect(SSL_set_cipher_list(sslInst, ciphers)).To(Equal(1)) 65 | Expect(SSL_set_tlsext_host_name(sslInst, host)).To(BeEquivalentTo(1)) 66 | /* Make the connection */ 67 | Expect(bio.BIO_do_connect(conn)).To(BeEquivalentTo(1)) 68 | }) 69 | 70 | // Expect(crypto.BIO_do_handshake(conn.(crypto.BIO))).To(BeEquivalentTo(1)) 71 | /*flags := SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION 72 | SSL_CTX_set_options(ctx, flags) 73 | Expect(host).To(Equal(1)) 74 | //port := BIO_set_conn_port(web, 443) 75 | BIO_get_ssl(web, &ssl) 76 | const PREFERRED_CIPHERS = "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MDS:!RC4" 77 | cipher := SSL_set_cipher_list(ssl, PREFERRED_CIPHERS) 78 | Expect(cipher).To(Equal(1)) */ 79 | }) 80 | }) 81 | /* Cannot fail ??? */ 82 | 83 | /* Cannot fail ??? */ 84 | 85 | /* Cannot fail ??? */ 86 | //const long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION; 87 | //SSL_CTX_set_options(ctx, flags); 88 | }) 89 | -------------------------------------------------------------------------------- /ssl/tests/certs/ca/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID8TCCAtmgAwIBAgIJANAWQKweLRm/MA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD 3 | VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExDzANBgNVBAcMBkR1cmhh 4 | bTEMMAoGA1UECgwDSUJNMRAwDgYDVQQLDAdCbHVlbWl4MRQwEgYDVQQDDAtibHVl 5 | bWl4Lm5ldDEfMB0GCSqGSIb3DQEJARYQdHJhc2hAdXMuaWJtLmNvbTAeFw0xNTEw 6 | MTIxNzQ4MDFaFw0xNTExMTExNzQ4MDFaMIGOMQswCQYDVQQGEwJVUzEXMBUGA1UE 7 | CAwOTm9ydGggQ2Fyb2xpbmExDzANBgNVBAcMBkR1cmhhbTEMMAoGA1UECgwDSUJN 8 | MRAwDgYDVQQLDAdCbHVlbWl4MRQwEgYDVQQDDAtibHVlbWl4Lm5ldDEfMB0GCSqG 9 | SIb3DQEJARYQdHJhc2hAdXMuaWJtLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP 10 | ADCCAQoCggEBAM7fj/aXxSvByiD/ipkr/i4I+NAfy1JRotP+2pwPgDVzIGmUmwu6 11 | bJ6EKSYymUZp0mnmuv2BMvjtW0I2U05nAhvBfDQ1b9R3rnKSOWbiwgBDwz2YCjcs 12 | G6DYG22M2Vqeo6iptIhbKpFtlEGTC4mXTgGBGtdZ66XGtStne5SdwokkdLtJiv9S 13 | dgoaCtwcj1mYhBGUO+pw0G5ut4b6l3+f2SWrVlk8ZOiFiZ+1G8O+mCsc1m0D/xJc 14 | uq3UG3dfylph/cbOqpOFLUMRSlayR/xZq0gqo+jHGp9M8ixNH/hn5OgTre52jVFo 15 | x3GqWvi5Gmd6f7Uj1f13HVlij1d9X+alaakCAwEAAaNQME4wHQYDVR0OBBYEFMy7 16 | YvVfIc3PdKpIs5ea8lJV9q+1MB8GA1UdIwQYMBaAFMy7YvVfIc3PdKpIs5ea8lJV 17 | 9q+1MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHoUGI4JACXgFTBL 18 | 34ezuGxkhGeDcOWtZ89xF+7GRL+TaNsZlsJEThGN46UE7a7bjO4c1c/okMlbREcG 19 | rEUq4c++29X1/iXlpvghoGtN3gMU/NlUN0xWyUa827xEPewMJJgZ5LyreuDezslt 20 | cSSo21dhlzzFebnuJOyRBvsmTqOlkxqD0mkLR0T+xGm4sP13ixoSelugPnaRiop0 21 | M7cWh8xspxoe5pqwK8NPQqd1XDdJWFTU4YoQOdOALR15P7U9cZMm2tyddp0vI6zq 22 | HfaoQyegGltIAsQ9RPWBuM574+GBBV6ylUsqxGvOE2hAXeL/GDUADA67fwPt3mmy 23 | bRRp+Dk= 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /ssl/tests/certs/ca/ca.srl: -------------------------------------------------------------------------------- 1 | 04 2 | -------------------------------------------------------------------------------- /ssl/tests/certs/ca/privkey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN ENCRYPTED PRIVATE KEY----- 2 | MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIM2SCVfRwohwCAggA 3 | MBQGCCqGSIb3DQMHBAhWzziFE8WvOwSCBMiQ2IR7Tros8Ikjua0O14TumEkMI1dP 4 | a/qcRdL+POkuc8rItLD7/8B2B43NrLY0GHziNUZBEFQQa0yZXwhY7lgwEZG7Bu3f 5 | S7RuaoB/PJWO021Fvf6tb3SrnbmwQPf8tYIB6sEEXYWff6xUSgKFnm+sY5al+4vd 6 | NfYPWjKZ9+sUVZwV2DujJCfeMqW8cgw5uYC/4OyzUqqGmFaxE6kFe4siREQ6f6yP 7 | rDzKWdFhjgP/Xh13drwcV37rWxT/Gd5xVY70XEEt/YQXwlVmS0lTf0WGHDvAnBRK 8 | zFJoEnw4y4FQwtlsJp6XPNLzhEJ07EvjRM6a3s9mpnB/CpYCIufL+4U9jzm9EBkS 9 | G2OeDn2poV8MVV4ybAG7ju95WZ2aLJbapRmE+G8asYhH1ut3ioxDqV1QXQKej9Mg 10 | J1CHj71CIGwLv/bFmzHKiaxcOgVwjoS7R+Xe952OwtyHlN9diqTqncjGKHIBAbax 11 | R3atrUbqhY5kWE1/ATAG/4s99GJAJetbry5gdGA/cDE7dNZF6WBXsFVaybvSjzTB 12 | r4NlIU206kIS9EWyc3JFzR/AhJwQWXPaylVNAVjm/wMe4GCJ6AyRqrYg7goNHG0d 13 | UoMAcPkUP6pnHCjcC7bHBeXR0m4K6KUDGMta9xkSWzC42HasnnfQmjnZ7edUNQi3 14 | sh5JhDapzsLDJ133aHvQ6kXTqqgV3qVb9b7V/DUzILB8DK09Kk6b1fwCgpRZTH5x 15 | iYqGqZJMC6fcdmS5Z4GjVBu40NlHu1TL/nNVJeUdutfiCmaxEvXSjvDM8+DHRALs 16 | NT4/MeJzO3Y5Xq7RKW4Xfx078tt5L/46/eE87LIfZfCCMamYsl5MkV1q/A6UYzjd 17 | 7/qtLCitMoKl6M3fV413WY3HPfaRQwYkse7vSpz53j51ptUQLt8HR6KbCMgyonhe 18 | i7oAMTweDC8B+/gKJRBD3fD5RjDz6xDe6pf6FQy1R6jT5++/jrC3p4tjO99aQMbu 19 | BH3BGuzCqBeFwrraKdwlBPimmwercgw6RHRGoSiM4XPe+eHGvRVNHlUlnSRHa4cb 20 | ohPSCEP4oCCtK35rVpn9zTRZGdn6RbRuJdonSic7rN2fDfY9Erl3r3Z34huE+oTa 21 | 7aOwnw1f988tzmG1DjkRMjvXxYH8g0yHeY5lz91BEQDsa9DaAxfpgDCg5I+Tr+mR 22 | ZyGSMepITAz8g109RblZ0btSsoFdP3af9rHmcT35MC8ptneOiNo/RpDnvzoefqYC 23 | 6vU95sCNQEQ5fvYTz4y5GmprTvl5o6loWEPSzR0JCKhcioo3Qtsx5fLzE+/b6n6L 24 | f51zy+8Q08MTtwJYAjKyvzVaT3UefQArfbqS2eiqLYcVvuKUliguyXoKwuCSOMWC 25 | o9Hwpv/vhxmsHw7u2FAbkkVABZTnq0/UAgsTP+Qk8OtHX560+cJreaU3cBnBEfb5 26 | GhAKurKKh9wK+6YXWyU33tdl4sf5HGXLMtP63G/KcHuEmFBxBq9cLBB5b5RF9XYP 27 | FmmQucrJcROWRYlSfBcuG6Hn8nu01Tu2QWTfRQ1itLG/89A6IQTFtJusSlhash03 28 | 0I/KZAtpVzdov+HlZIf3BHelnf95HarKm+fHdF3z8ofILn6UX5Z3q5hcFMh6u4Vg 29 | Mn0= 30 | -----END ENCRYPTED PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /ssl/tests/certs/client/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXgIBAAKBgQDbXNREER9m5gYYl4oPsYZSdtAW4/L9qu898Pu+ODL7MU6wgEW9 3 | frKgtpPoS5K39Ea7piMv1QXVkiKKaMshD5TxK+y81PcSdSHay10oVktObfITfwCd 4 | U62edUDI6N6LFZe8N2S8rjNfU5rxhR2g0E4vms/mB6ZoeXDnadXjoPJR3QIDAQAB 5 | AoGBAIrDWR9JuZSM89RAoDw3L2Ig+JdSURb4N2j2dTkqMc3GriFmszcg23xgooWy 6 | sLgfFVOE0ANk/NDSJXlquvxJ8AvHy0hEVUZj5veYTEK8ASoliBj79v2kEcOTCGoZ 7 | 7gIDvkGUMTXVJfzt7jx6Dilg6MkNttrN81E3PwIMhCTvgAF9AkEA7yuEcbQ4Wqjl 8 | mcMfw6rxGQ47vmFLtpt4rZ0nIYxf1K+Am1d6roAuDzOYYOeSrgkTKDHifiUdbWzU 9 | 5KIdHUj7AwJBAOrMft3Asgtr8ArHp5wxNls+HoGLS6RS+DJbW6opXt9dMWMWie4W 10 | +yyPi6CGFnBb4pDQXLZqZRvCzmGeyvf+eZ8CQQCQRXLuV+VKoullWHPXx00KbTCS 11 | xgQzEhO4vry1GwVPudJdYmapzr56AwifI/GyM90vePAEYgUfVfASPsxLNxPbAkBl 12 | /faN9QpiE7ztsdSI8IegEtfFhfCRrMjLdwvWWqovdJfjZwL7PVhV/vS0Agr1O7Sr 13 | Gxdq1cSdzn0/wm6AUzYZAkEAzt/hsPyWNgOp9gbgmI2wQOmQtwJvus+GOjlvYtgS 14 | rho3M1W7uZB4KR80u7c83pqxuDzSTKdcw5aWNkm4IJlHpA== 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /ssl/tests/certs/client/client.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDDjCCAfYCAQQwDQYJKoZIhvcNAQELBQAwgY4xCzAJBgNVBAYTAlVTMRcwFQYD 3 | VQQIDA5Ob3J0aCBDYXJvbGluYTEPMA0GA1UEBwwGRHVyaGFtMQwwCgYDVQQKDANJ 4 | Qk0xEDAOBgNVBAsMB0JsdWVtaXgxFDASBgNVBAMMC2JsdWVtaXgubmV0MR8wHQYJ 5 | KoZIhvcNAQkBFhB0cmFzaEB1cy5pYm0uY29tMB4XDTE1MTAxMjE3NTgwOFoXDTE2 6 | MTAxMTE3NTgwOFowgY4xCzAJBgNVBAYTAlVTMRcwFQYDVQQIDA5Ob3J0aCBDYXJv 7 | bGluYTEPMA0GA1UEBwwGRHVyaGFtMQwwCgYDVQQKDANJQk0xEDAOBgNVBAsMB0Js 8 | dWVtaXgxFDASBgNVBAMMC2JsdWVtaXgubmV0MR8wHQYJKoZIhvcNAQkBFhB0cmFz 9 | aEB1cy5pYm0uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbXNREER9m 10 | 5gYYl4oPsYZSdtAW4/L9qu898Pu+ODL7MU6wgEW9frKgtpPoS5K39Ea7piMv1QXV 11 | kiKKaMshD5TxK+y81PcSdSHay10oVktObfITfwCdU62edUDI6N6LFZe8N2S8rjNf 12 | U5rxhR2g0E4vms/mB6ZoeXDnadXjoPJR3QIDAQABMA0GCSqGSIb3DQEBCwUAA4IB 13 | AQBlqLdzNCui7XCEEpTLN/npKFwy9ylHylrTRKMiexiolSBgjDzDROkoh2OBmSBK 14 | zntCYePhB7RK9nEcUvK008ELLtSB4xrhj+M02LcAPa/aJjphQ78mXUrK51AR1+54 15 | gGZECXUKX6Yy7+5g2WKbQph8Feu4hIEDeezyLeu5dCtKAXLZ044JY+L49B2Ene2U 16 | Z2WI+UUMQmhx47jqLphu8MfMYU5vwQlHm+Ly9ejgyI3C8Lqr4VrhaeOnuDDVb2hZ 17 | q1y6lA+saxDZNH7/es56IvhXktBWJmn1UvPUdnh60rmThV4haCtGc6MdsRrShxYZ 18 | uGGuFTyS+3FF62264i/eJMgQ 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /ssl/tests/certs/client/client.req: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBzzCCATgCAQAwgY4xCzAJBgNVBAYTAlVTMRcwFQYDVQQIDA5Ob3J0aCBDYXJv 3 | bGluYTEPMA0GA1UEBwwGRHVyaGFtMQwwCgYDVQQKDANJQk0xEDAOBgNVBAsMB0Js 4 | dWVtaXgxFDASBgNVBAMMC2JsdWVtaXgubmV0MR8wHQYJKoZIhvcNAQkBFhB0cmFz 5 | aEB1cy5pYm0uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbXNREER9m 6 | 5gYYl4oPsYZSdtAW4/L9qu898Pu+ODL7MU6wgEW9frKgtpPoS5K39Ea7piMv1QXV 7 | kiKKaMshD5TxK+y81PcSdSHay10oVktObfITfwCdU62edUDI6N6LFZe8N2S8rjNf 8 | U5rxhR2g0E4vms/mB6ZoeXDnadXjoPJR3QIDAQABoAAwDQYJKoZIhvcNAQELBQAD 9 | gYEAzpSfBoA/68QGmZt7acfgSfO/K/hDmCnsRh5zyDeiKjpNEYPktWI+o3hRQ7px 10 | G/gTkUy9qDAuWgBByBteTt54jT0QlcTS8ezCZIQNSudqNtMbwMTxnyZfqEwWeCtU 11 | UfmsPgChDg80FgU7mHaICnhj0CimHWtG8/cbjWwjEcE4mAk= 12 | -----END CERTIFICATE REQUEST----- 13 | -------------------------------------------------------------------------------- /ssl/tests/certs/server/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXgIBAAKBgQDM2FH/SjnJU7FlBM9ubA7sA5KyMcjEi7eTBTDrEUOJyHTwmw2P 3 | AAaoqn0euxoJtBIg7wZuOe9B8e7njVY9DsaG6/F/i4qP6gmOVW8VosVHHn57HmMr 4 | NX4AAXOI6gTKoh+WGUhL8eR3mVoCc0aBEAI0XtAMmCovoOgJ3rN42dCg+wIDAQAB 5 | AoGBAIGMYTcp0yfvJSZ1B1aXQkyi6diGQamDjwKM6uYPEf6GZuZvuQByDMQIpn6T 6 | 56CxYDGx2QhG3XiAMA4CxcJxoL07kpfCT6GtkCjzfP40h/jnr2nxhxDuW64t64yN 7 | 421BzeazVxu5+pysvoytsGQXEz+IUvFJRSgNjIqBXZnRarjpAkEA7PJMx7GHalFU 8 | 2953wqxUtwyB9IeUChIRA9ca0/dIEXgfXj6FwjrVhME01s/WAPsTC3C4lpeeZj+Z 9 | glTqSMpAdQJBAN1RMKv5E8hDrP9ObjhPXasbhBygpQ1J32dLMWR9My0fwWKfMnkf 10 | 5Ejqy1+xIXd6RRjHUCA+G2NoQgZamLoSLa8CQQCJjchhDYDNidW3ppjj3ON5OLug 11 | 16xrhD14e0HJ1/5wlcmwDp/cOAeIgBlKs3FLNHbD53ZhhjegqD9X2DACfdeZAkB/ 12 | 0Lj0SNg062jI1anwGV32SODmi5xm7Uf5ZsKyz1+z6sgLxfcnXtS/i+JCdJDoaJPp 13 | OIq85EF7CqY+9ymHJKJ/AkEAmmHPh2/WFYBkMEuWQbMd0JHnRQyKl68dH5xfwYzk 14 | m8rJNwOW8iIL1oH0O/990PtE+pELlkaKoQyBdJDK38h3NQ== 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /ssl/tests/certs/server/server.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDDjCCAfYCAQMwDQYJKoZIhvcNAQELBQAwgY4xCzAJBgNVBAYTAlVTMRcwFQYD 3 | VQQIDA5Ob3J0aCBDYXJvbGluYTEPMA0GA1UEBwwGRHVyaGFtMQwwCgYDVQQKDANJ 4 | Qk0xEDAOBgNVBAsMB0JsdWVtaXgxFDASBgNVBAMMC2JsdWVtaXgubmV0MR8wHQYJ 5 | KoZIhvcNAQkBFhB0cmFzaEB1cy5pYm0uY29tMB4XDTE1MTAxMjE3NTcyMloXDTE2 6 | MTAxMTE3NTcyMlowgY4xCzAJBgNVBAYTAlVTMRcwFQYDVQQIDA5Ob3J0aCBDYXJv 7 | bGluYTEPMA0GA1UEBwwGRHVyaGFtMQwwCgYDVQQKDANJQk0xEDAOBgNVBAsMB0Js 8 | dWVtaXgxFDASBgNVBAMMC2JsdWVtaXgubmV0MR8wHQYJKoZIhvcNAQkBFhB0cmFz 9 | aEB1cy5pYm0uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDM2FH/SjnJ 10 | U7FlBM9ubA7sA5KyMcjEi7eTBTDrEUOJyHTwmw2PAAaoqn0euxoJtBIg7wZuOe9B 11 | 8e7njVY9DsaG6/F/i4qP6gmOVW8VosVHHn57HmMrNX4AAXOI6gTKoh+WGUhL8eR3 12 | mVoCc0aBEAI0XtAMmCovoOgJ3rN42dCg+wIDAQABMA0GCSqGSIb3DQEBCwUAA4IB 13 | AQCzPwv6DytBVBfI7jo+7ykgZjLLXI+RllF8Udw0VmudeQxTKfDom+eLB+Bxx8BO 14 | BE3eyohOgzC/JHXvrkqC2demJrR7WpkyC3UX1TdzthgXB0eWHliJOBO23MivdeuM 15 | p1d69zt2+8K1CrJazfEUOsEcbFHxGdgzX5hD9KKPwAfTVxd5QSv21HHOyuA+zbMy 16 | lWVtRS8YE1k/z1LX2W1jGdeVLjLYv+YR7O+6MYqLyRgEHaDO62vqcXf4EfR2yVHr 17 | T97GA6Yu/uW8P0uDDUS0dpKyMFioSF9NEnuJP3qtx5WPEOCuU5yeic9ZX2cQZ/hd 18 | 8VIN+fSv3CTmOWxqnBbJLvFw 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /ssl/tests/certs/server/server.req: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBzzCCATgCAQAwgY4xCzAJBgNVBAYTAlVTMRcwFQYDVQQIDA5Ob3J0aCBDYXJv 3 | bGluYTEPMA0GA1UEBwwGRHVyaGFtMQwwCgYDVQQKDANJQk0xEDAOBgNVBAsMB0Js 4 | dWVtaXgxFDASBgNVBAMMC2JsdWVtaXgubmV0MR8wHQYJKoZIhvcNAQkBFhB0cmFz 5 | aEB1cy5pYm0uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDM2FH/SjnJ 6 | U7FlBM9ubA7sA5KyMcjEi7eTBTDrEUOJyHTwmw2PAAaoqn0euxoJtBIg7wZuOe9B 7 | 8e7njVY9DsaG6/F/i4qP6gmOVW8VosVHHn57HmMrNX4AAXOI6gTKoh+WGUhL8eR3 8 | mVoCc0aBEAI0XtAMmCovoOgJ3rN42dCg+wIDAQABoAAwDQYJKoZIhvcNAQELBQAD 9 | gYEAAdkFVVgJXAxMcr99+oPvadLwqsOCXFrplKE9HNpVNSo56Ko9CaZpXhtJY/dQ 10 | ObCLoYxDVQgwKdFUQ2kzc/WVN5XrOObSEAWk8BgR8/MYJ3F7C8ZaOIJ7UEUpwBIU 11 | cXdu2/okrRcUXpUGKpqjFFCeXl+34HfF7azCg5XwiNMUta4= 12 | -----END CERTIFICATE REQUEST----- 13 | -------------------------------------------------------------------------------- /ssl/tests/httpsserver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/IBM-Bluemix/golang-openssl-wrapper/ssl" 6 | "github.com/gorilla/mux" 7 | "net/http" 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | func main() { 13 | setup() 14 | 15 | d, _ := filepath.Abs(filepath.Dir(os.Args[0])) 16 | 17 | fmt.Println("Server listening on port 8443") 18 | _, e := ssl.ListenAndServeTLS(":8443", filepath.Join(d, "certs/server/server.pem"), 19 | filepath.Join(d, "certs/server/server.key"), nil) 20 | 21 | if e != nil { 22 | panic(e) 23 | } 24 | } 25 | 26 | func setup() { 27 | ssl.HandleFunc("/aloha", func(res http.ResponseWriter, req *http.Request) { 28 | headers(res) 29 | aloha(res) 30 | }) 31 | 32 | ssl.HandleFunc("/server", func(res http.ResponseWriter, req *http.Request) { 33 | headers(res) 34 | res.Write([]byte("")) 35 | }) 36 | 37 | r := mux.NewRouter() 38 | r.HandleFunc("/mux", func(res http.ResponseWriter, req *http.Request) { 39 | headers(res) 40 | res.Write([]byte("Using gorilla/mux")) 41 | }) 42 | ssl.Handle("/mux", r) 43 | } 44 | 45 | func aloha(res http.ResponseWriter) { 46 | res.Write([]byte("ALOHA!!")) 47 | } 48 | 49 | func headers(res http.ResponseWriter) { 50 | res.Header().Add("Server", "https://github.com/IBM-Bluemix/golang-openssl-wrapper") 51 | } 52 | -------------------------------------------------------------------------------- /x509/helpers_test.go: -------------------------------------------------------------------------------- 1 | package x509_test 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "path" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | CERTDIR = "testcerts" 12 | CERTFILES = make(map[string]string) 13 | CERTS = make(map[string]string) 14 | ) 15 | 16 | func init() { 17 | dfs, e := ioutil.ReadDir(CERTDIR) 18 | if e != nil { 19 | fmt.Printf("Failed to read CERTDIR: %s (%s)\n", CERTDIR, e) 20 | return 21 | } 22 | 23 | for _, f := range dfs { 24 | // Skip directories 25 | if f.IsDir() { 26 | continue 27 | } 28 | 29 | fp := path.Join(CERTDIR, f.Name()) 30 | file, e := ioutil.ReadFile(fp) 31 | // Print and ignore errors 32 | if e != nil { 33 | fmt.Printf("Failed to read file in CERTDIR: %s (%s)\n", fp, e) 34 | continue 35 | } 36 | 37 | certname := strings.Split(f.Name(), ".")[0] 38 | CERTFILES[certname] = fp 39 | CERTS[certname] = string(file) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /x509/testcerts/github.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIHOjCCBiKgAwIBAgIQBH++LkveAITSyvjj7P5wWDANBgkqhkiG9w0BAQUFADBp 3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 4 | d3cuZGlnaWNlcnQuY29tMSgwJgYDVQQDEx9EaWdpQ2VydCBIaWdoIEFzc3VyYW5j 5 | ZSBFViBDQS0xMB4XDTEzMDYxMDAwMDAwMFoXDTE1MDkwMjEyMDAwMFowgfAxHTAb 6 | BgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYBBAGCNzwCAQMTAlVT 7 | MRkwFwYLKwYBBAGCNzwCAQITCERlbGF3YXJlMRAwDgYDVQQFEwc1MTU3NTUwMRcw 8 | FQYDVQQJEw41NDggNHRoIFN0cmVldDEOMAwGA1UEERMFOTQxMDcxCzAJBgNVBAYT 9 | AlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2Nv 10 | MRUwEwYDVQQKEwxHaXRIdWIsIEluYy4xEzARBgNVBAMTCmdpdGh1Yi5jb20wggEi 11 | MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDt04nDXXByCfMzTxpydNm2WpVQ 12 | u2hhn/f7Hxnh2gQxrxV8Gn/5c68d5UMrVgkARWlK6MRb38J3UlEZW9Er2TllNqAy 13 | GRxBc/sysj2fmOyCWws3ZDkstxCDcs3w6iRL+tmULsOFFTmpOvaI2vQniaaVT4Si 14 | N058JXg6yYNtAheVeH1HqFWD7hPIGRqzPPFf/jsC4YX7EWarCV2fTEPwxyReKXIo 15 | ztR1aE8kcimuOSj8341PTYNzdAxvEZun3WLe/+LrF+b/DL/ALTE71lmi8t2HSkh7 16 | bTMRFE00nzI49sgZnfG2PcVG71ELisYz7UhhxB0XG718tmfpOc+lUoAK9OrNAgMB 17 | AAGjggNUMIIDUDAfBgNVHSMEGDAWgBRMWMsl8EFPUvQoyIFDm6aooOaS5TAdBgNV 18 | HQ4EFgQUh9GPGW7kh29TjHeRB1Dfo79VRyAwJQYDVR0RBB4wHIIKZ2l0aHViLmNv 19 | bYIOd3d3LmdpdGh1Yi5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsG 20 | AQUFBwMBBggrBgEFBQcDAjBjBgNVHR8EXDBaMCugKaAnhiVodHRwOi8vY3JsMy5k 21 | aWdpY2VydC5jb20vZXZjYTEtZzIuY3JsMCugKaAnhiVodHRwOi8vY3JsNC5kaWdp 22 | Y2VydC5jb20vZXZjYTEtZzIuY3JsMIIBxAYDVR0gBIIBuzCCAbcwggGzBglghkgB 23 | hv1sAgEwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9z 24 | c2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4A 25 | eQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQA 26 | ZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUA 27 | IABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAA 28 | YQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcA 29 | cgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIA 30 | aQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQA 31 | ZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMH0G 32 | CCsGAQUFBwEBBHEwbzAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu 33 | Y29tMEcGCCsGAQUFBzAChjtodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln 34 | aUNlcnRIaWdoQXNzdXJhbmNlRVZDQS0xLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqG 35 | SIb3DQEBBQUAA4IBAQBfFW1nwzrVo94WnEUzJtU9yRZ0NMqHSBsUkG31q0eGufW4 36 | 4wFFZWjuqRJ1n3Ym7xF8fTjP3fdKGQnxIHKSsE0nuuh/XbQX5DpBJknHdGFoLwY8 37 | xZ9JPI57vgvzLo8+fwHyZp3Vm/o5IYLEQViSo+nlOSUQ8YAVqu6KcsP/e612UiqS 38 | +UMBmgdx9KPDDzZy4MJZC2hbfUoXj9A54mJN8cuEOPyw3c3yKOcq/h48KzVguQXi 39 | SdJbwfqNIbQ9oJM+YzDjzS62+TCtNSNWzWbwABZCmuQxK0oEOSbTmbhxUF7rND3/ 40 | +mx9u8cY//7uAxLWYS5gIZlCbxcf0lkiKSHJB319 41 | -----END CERTIFICATE----- 42 | -------------------------------------------------------------------------------- /x509/testcerts/google.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIGKjCCBZOgAwIBAgIKZAPy8wABAACR3DANBgkqhkiG9w0BAQUFADBGMQswCQYD 3 | VQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzEiMCAGA1UEAxMZR29vZ2xlIElu 4 | dGVybmV0IEF1dGhvcml0eTAeFw0xMzA3MTIwOTAwMzBaFw0xMzEwMzEyMzU5NTla 5 | MGYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N 6 | b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMRUwEwYDVQQDFAwqLmdv 7 | b2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANRk5KDRmXcTdorq 8 | kBFq7Mz17PXY3L1XaypAA/gMju7TcS6cbMmOEhmIXX5Ul+GAxfLwmE2WSGe1K58m 9 | ++B4D153EKIXc+ilrDKt7K/BfcX1cH3qUhk6Zc3IO2PTPL3UYkWA6WiH1Ehuaf39 10 | 89FfB7Vk20Nv2QOvNOHW18qZWgbzAgMBAAGjggP9MIID+TAdBgNVHSUEFjAUBggr 11 | BgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFArP+7JSI4++2qQ6xmNmryABbF8z 12 | MB8GA1UdIwQYMBaAFL/AMOv1QxE+Z7qekfv8atrjaxIkMFsGA1UdHwRUMFIwUKBO 13 | oEyGSmh0dHA6Ly93d3cuZ3N0YXRpYy5jb20vR29vZ2xlSW50ZXJuZXRBdXRob3Jp 14 | dHkvR29vZ2xlSW50ZXJuZXRBdXRob3JpdHkuY3JsMGYGCCsGAQUFBwEBBFowWDBW 15 | BggrBgEFBQcwAoZKaHR0cDovL3d3dy5nc3RhdGljLmNvbS9Hb29nbGVJbnRlcm5l 16 | dEF1dGhvcml0eS9Hb29nbGVJbnRlcm5ldEF1dGhvcml0eS5jcnQwDAYDVR0TAQH/ 17 | BAIwADCCAsMGA1UdEQSCArowggK2ggwqLmdvb2dsZS5jb22CDSouYW5kcm9pZC5j 18 | b22CFiouYXBwZW5naW5lLmdvb2dsZS5jb22CEiouY2xvdWQuZ29vZ2xlLmNvbYIW 19 | Ki5nb29nbGUtYW5hbHl0aWNzLmNvbYILKi5nb29nbGUuY2GCCyouZ29vZ2xlLmNs 20 | gg4qLmdvb2dsZS5jby5pboIOKi5nb29nbGUuY28uanCCDiouZ29vZ2xlLmNvLnVr 21 | gg8qLmdvb2dsZS5jb20uYXKCDyouZ29vZ2xlLmNvbS5hdYIPKi5nb29nbGUuY29t 22 | LmJygg8qLmdvb2dsZS5jb20uY2+CDyouZ29vZ2xlLmNvbS5teIIPKi5nb29nbGUu 23 | Y29tLnRygg8qLmdvb2dsZS5jb20udm6CCyouZ29vZ2xlLmRlggsqLmdvb2dsZS5l 24 | c4ILKi5nb29nbGUuZnKCCyouZ29vZ2xlLmh1ggsqLmdvb2dsZS5pdIILKi5nb29n 25 | bGUubmyCCyouZ29vZ2xlLnBsggsqLmdvb2dsZS5wdIIPKi5nb29nbGVhcGlzLmNu 26 | ghQqLmdvb2dsZWNvbW1lcmNlLmNvbYINKi5nc3RhdGljLmNvbYIMKi51cmNoaW4u 27 | Y29tghAqLnVybC5nb29nbGUuY29tghYqLnlvdXR1YmUtbm9jb29raWUuY29tgg0q 28 | LnlvdXR1YmUuY29tghYqLnlvdXR1YmVlZHVjYXRpb24uY29tggsqLnl0aW1nLmNv 29 | bYILYW5kcm9pZC5jb22CBGcuY2+CBmdvby5nbIIUZ29vZ2xlLWFuYWx5dGljcy5j 30 | b22CCmdvb2dsZS5jb22CEmdvb2dsZWNvbW1lcmNlLmNvbYIKdXJjaGluLmNvbYII 31 | eW91dHUuYmWCC3lvdXR1YmUuY29tghR5b3V0dWJlZWR1Y2F0aW9uLmNvbTANBgkq 32 | hkiG9w0BAQUFAAOBgQBfxSnRtvvjE4rZn/JjpRoC9HrlCXNfMtg3feveIEhGA8Cl 33 | j5aGf2H+8RBfTYzwFmykUvlYNA47de8rHU6rAMcOR7Nq2ePB8BXpsnZ4CH0mCBNz 34 | 76V3NMkECr+o+Vn9l6sAbyjqGwA0dJ3++4SM0Hjd/6jXLXt3ICgggP4eKr09fA== 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /x509/x509.go: -------------------------------------------------------------------------------- 1 | package x509 2 | 3 | // #cgo CFLAGS: -I/usr/local/ssl/include 4 | // #cgo LDFLAGS: -L /usr/local/ssl/lib -lcrypto 5 | import "C" 6 | -------------------------------------------------------------------------------- /x509/x509.swig: -------------------------------------------------------------------------------- 1 | /* SWIG interface file for openssl/x509.h */ 2 | %module x509 3 | %{ 4 | #include 5 | #include 6 | #include 7 | %} 8 | 9 | %include 10 | 11 | /* 12 | * From openssl/x509.h 13 | */ 14 | 15 | // X509 *X509_new(void); 16 | // void X509_free(X509 *a); 17 | 18 | // typedef int pem_password_cb(char *buf, int size, int rwflag, void *userdata); 19 | 20 | // %typemap(gotype) void *u %{string%} 21 | // %typemap(gotype) X509 **x %{*X509%} 22 | 23 | // // TODO[Colton]: Works with nil X509**, doesn't work when supplied existing X509** 24 | // // that was created with X509_new. 25 | // X509 *PEM_read_bio_X509(BIO *b, X509 **x, pem_password_cb *cb, void *u); 26 | -------------------------------------------------------------------------------- /x509/x509_suite_test.go: -------------------------------------------------------------------------------- 1 | package x509_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestX509(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "X509 Suite") 13 | } 14 | -------------------------------------------------------------------------------- /x509/x509_test.go: -------------------------------------------------------------------------------- 1 | package x509_test 2 | 3 | import ( 4 | . "github.com/IBM-Bluemix/golang-openssl-wrapper/x509" 5 | 6 | "github.com/IBM-Bluemix/golang-openssl-wrapper/bio" 7 | . "github.com/onsi/ginkgo" 8 | . "github.com/onsi/gomega" 9 | ) 10 | 11 | var _ = XDescribe("X509", func() { 12 | var x X509 13 | It("Should create a new X509 instance", func() { 14 | x = X509_new() 15 | Expect(x).NotTo(BeNil()) 16 | }) 17 | 18 | It("Should free the X509 instance", func() { 19 | X509_free(x) 20 | }) 21 | 22 | Context("Reading PEM data from a memory BIO", func() { 23 | It("Should read with nil X509 and return new X509", func() { 24 | b := bio.BIO_new(bio.BIO_s_mem()) 25 | cert := CERTS["google"] 26 | x = nil 27 | Expect(bio.BIO_write(b, cert, len(cert))).To(Equal(len(cert))) 28 | result := PEM_read_bio_X509(b, &x, nil, "") 29 | Expect(result).NotTo(BeNil()) 30 | Expect(result).NotTo(BeEquivalentTo(0)) 31 | X509_free(result) 32 | bio.BIO_free(b) 33 | }) 34 | 35 | It("Should read with existing X509 and return new X509", func() { 36 | Skip("This test may contain a known OpenSSL bug") 37 | b := bio.BIO_new(bio.BIO_s_mem()) 38 | cert := CERTS["google"] 39 | x = X509_new() 40 | Expect(bio.BIO_write(b, cert, len(cert))).To(Equal(len(cert))) 41 | // result := PEM_read_bio_X509(b, &x, nil, "") 42 | Expect(PEM_read_bio_X509(b, &x, nil, "")).To(BeEquivalentTo(0)) 43 | // Expect(result).NotTo(BeNil()) 44 | // Expect(result).NotTo(BeEquivalentTo(0)) 45 | // X509_free(result) 46 | X509_free(x) 47 | bio.BIO_free(b) 48 | }) 49 | }) 50 | 51 | Context("Reading PEM data from a file BIO", func() { 52 | It("Should read with nil X509 and return new X509", func() { 53 | b := bio.BIO_new(bio.BIO_s_file()) 54 | Expect(bio.BIO_read_filename(b, CERTFILES["google"])).To(Equal(1)) 55 | x = nil 56 | result := PEM_read_bio_X509(b, &x, nil, "") 57 | Expect(result).NotTo(BeNil()) 58 | Expect(result).NotTo(BeEquivalentTo(0)) 59 | X509_free(result) 60 | bio.BIO_free(b) 61 | }) 62 | }) 63 | }) 64 | --------------------------------------------------------------------------------