From 3945574fd184e2b24f58420c09f9fc829143c8e2 Mon Sep 17 00:00:00 2001 From: Ryan Hileman Date: Wed, 12 Nov 2014 15:55:00 -0800 Subject: [PATCH 1/2] restructure certs/keys; add key/cert generation NID is also now an exposed type --- cert.go | 365 +++++++++++++++++++++++++++++++++++++ cert_test.go | 101 ++++++++++ ciphers.go | 8 +- pem.go => key.go | 77 ++------ pem_test.go => key_test.go | 15 ++ nid.go | 199 ++++++++++++++++++++ 6 files changed, 701 insertions(+), 64 deletions(-) create mode 100644 cert.go create mode 100644 cert_test.go rename pem.go => key.go (82%) rename pem_test.go => key_test.go (93%) create mode 100644 nid.go diff --git a/cert.go b/cert.go new file mode 100644 index 0000000..5c9b00e --- /dev/null +++ b/cert.go @@ -0,0 +1,365 @@ +// Copyright (C) 2014 Space Monkey, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build cgo + +package openssl + +// #include +// #include +// #include +// +// void OPENSSL_free_not_a_macro(void *ref) { OPENSSL_free(ref); } +// +import "C" + +import ( + "errors" + "io/ioutil" + "runtime" + "time" + "unsafe" +) + +type EVP_MD int + +const ( + EVP_NULL EVP_MD = iota + EVP_MD5 EVP_MD = iota + EVP_SHA EVP_MD = iota + EVP_SHA1 EVP_MD = iota + EVP_DSS EVP_MD = iota + EVP_DSS1 EVP_MD = iota + EVP_MDC2 EVP_MD = iota + EVP_RIPEMD160 EVP_MD = iota + EVP_SHA224 EVP_MD = iota + EVP_SHA256 EVP_MD = iota + EVP_SHA384 EVP_MD = iota + EVP_SHA512 EVP_MD = iota +) + +type Certificate struct { + x *C.X509 + Issuer *Certificate + ref interface{} +} + +type CertificateInfo struct { + Serial int + Issued time.Duration + Expires time.Duration + Country string + Organization string + CommonName string +} + +type Name struct { + name *C.X509_NAME +} + +// Allocate and return a new Name object. +func NewName() (*Name, error) { + n := C.X509_NAME_new() + if n == nil { + return nil, errors.New("could not create x509 name") + } + name := &Name{name: n} + runtime.SetFinalizer(name, func(n *Name) { + C.X509_NAME_free(n.name) + }) + return name, nil +} + +// AddTextEntry appends a text entry to an X509 NAME. +func (n *Name) AddTextEntry(field, value string) error { + cfield := C.CString(field) + defer C.free(unsafe.Pointer(cfield)) + cvalue := (*C.uchar)(unsafe.Pointer(C.CString(value))) + defer C.free(unsafe.Pointer(cvalue)) + ret := C.X509_NAME_add_entry_by_txt( + n.name, cfield, C.MBSTRING_ASC, cvalue, -1, -1, 0) + if ret != 1 { + return errors.New("failed to add x509 name text entry") + } + return nil +} + +// AddTextEntries allows adding multiple entries to a name in one call. +func (n *Name) AddTextEntries(entries map[string]string) error { + for f, v := range entries { + if err := n.AddTextEntry(f, v); err != nil { + return err + } + } + return nil +} + +// NewCertificate generates a basic certificate based +// on the provided CertificateInfo struct +func NewCertificate(info *CertificateInfo, key PublicKey) (*Certificate, error) { + c := &Certificate{x: C.X509_new()} + runtime.SetFinalizer(c, func(c *Certificate) { + C.X509_free(c.x) + }) + + name, err := c.GetSubjectName() + if err != nil { + return nil, err + } + err = name.AddTextEntries(map[string]string{ + "C": info.Country, + "O": info.Organization, + "CN": info.CommonName, + }) + if err != nil { + return nil, err + } + // self-issue for now + if err := c.SetIssuerName(name); err != nil { + return nil, err + } + if err := c.SetSerial(info.Serial); err != nil { + return nil, err + } + if err := c.SetIssueDate(info.Issued); err != nil { + return nil, err + } + if err := c.SetExpireDate(info.Expires); err != nil { + return nil, err + } + if err := c.SetPubKey(key); err != nil { + return nil, err + } + return c, nil +} + +func (c *Certificate) GetSubjectName() (*Name, error) { + n := C.X509_get_subject_name(c.x) + if n == nil { + return nil, errors.New("failed to get subject name") + } + return &Name{name: n}, nil +} + +func (c *Certificate) GetIssuerName() (*Name, error) { + n := C.X509_get_issuer_name(c.x) + if n == nil { + return nil, errors.New("failed to get issuer name") + } + return &Name{name: n}, nil +} + +func (c *Certificate) SetSubjectName(name *Name) error { + if C.X509_set_subject_name(c.x, name.name) != 1 { + return errors.New("failed to set subject name") + } + return nil +} + +// SetIssuer updates the stored Issuer cert +// and the internal x509 Issuer Name of a certificate. +// The stored Issuer reference is used when adding extensions. +func (c *Certificate) SetIssuer(issuer *Certificate) error { + name, err := issuer.GetSubjectName() + if err != nil { + return err + } + if err = c.SetIssuerName(name); err != nil { + return err + } + c.Issuer = issuer + return nil +} + +// SetIssuerName populates the issuer name of a certificate. +// Use SetIssuer instead, if possible. +func (c *Certificate) SetIssuerName(name *Name) error { + if C.X509_set_issuer_name(c.x, name.name) != 1 { + return errors.New("failed to set subject name") + } + return nil +} + +// SetSerial sets the serial of a certificate. +func (c *Certificate) SetSerial(serial int) error { + if C.ASN1_INTEGER_set(C.X509_get_serialNumber(c.x), C.long(serial)) != 1 { + return errors.New("failed to set serial") + } + return nil +} + +// SetIssueDate sets the certificate issue date relative to the current time. +func (c *Certificate) SetIssueDate(when time.Duration) error { + offset := C.long(when / time.Second) + result := C.X509_gmtime_adj(c.x.cert_info.validity.notBefore, offset) + if result == nil { + return errors.New("failed to set issue date") + } + return nil +} + +// SetExpireDate sets the certificate issue date relative to the current time. +func (c *Certificate) SetExpireDate(when time.Duration) error { + offset := C.long(when / time.Second) + result := C.X509_gmtime_adj(c.x.cert_info.validity.notAfter, offset) + if result == nil { + return errors.New("failed to set expire date") + } + return nil +} + +// SetPubKey assigns a new public key to a certificate. +func (c *Certificate) SetPubKey(pubKey PublicKey) error { + if C.X509_set_pubkey(c.x, pubKey.evpPKey()) != 1 { + return errors.New("failed to set public key") + } + return nil +} + +// Sign a certificate using a private key and a digest name. +// Accepted digest names are 'sha256', 'sha384', and 'sha512'. +func (c *Certificate) Sign(privKey PrivateKey, digest EVP_MD) error { + switch digest { + case EVP_SHA256: + case EVP_SHA384: + case EVP_SHA512: + default: + return errors.New("Unsupported digest" + + "You're probably looking for 'EVP_SHA256' or 'EVP_SHA512'.") + } + return c.insecureSign(privKey, digest) +} + +func (c *Certificate) insecureSign(privKey PrivateKey, digest EVP_MD) error { + var md *C.EVP_MD + switch digest { + // please don't use these digest functions + case EVP_NULL: + md = C.EVP_md_null() + case EVP_MD5: + md = C.EVP_md5() + case EVP_SHA: + md = C.EVP_sha() + case EVP_SHA1: + md = C.EVP_sha1() + case EVP_DSS: + md = C.EVP_dss() + case EVP_DSS1: + md = C.EVP_dss1() + case EVP_RIPEMD160: + md = C.EVP_ripemd160() + case EVP_SHA224: + md = C.EVP_sha224() + // you actually want one of these + case EVP_SHA256: + md = C.EVP_sha256() + case EVP_SHA384: + md = C.EVP_sha384() + case EVP_SHA512: + md = C.EVP_sha512() + } + if C.X509_sign(c.x, privKey.evpPKey(), md) <= 0 { + return errors.New("failed to sign certificate") + } + return nil +} + +// Add an extension to a certificate. +// Extension constants are NID_* as found in openssl. +func (c *Certificate) AddExtension(nid NID, value string) error { + issuer := c + if c.Issuer != nil { + issuer = c.Issuer + } + var ctx C.X509V3_CTX + C.X509V3_set_ctx(&ctx, c.x, issuer.x, nil, nil, 0) + ex := C.X509V3_EXT_conf_nid(nil, &ctx, C.int(nid), C.CString(value)) + if ex == nil { + return errors.New("failed to create x509v3 extension") + } + defer C.X509_EXTENSION_free(ex) + if C.X509_add_ext(c.x, ex, -1) <= 0 { + return errors.New("failed to add x509v3 extension") + } + return nil +} + +// Wraps AddExtension using a map of NID to text extension. +// Will return without finishing if it encounters an error. +func (c *Certificate) AddExtensions(extensions map[NID]string) error { + for nid, value := range extensions { + if err := c.AddExtension(nid, value); err != nil { + return err + } + } + return nil +} + +// LoadCertificateFromPEM loads an X509 certificate from a PEM-encoded block. +func LoadCertificateFromPEM(pem_block []byte) (*Certificate, error) { + if len(pem_block) == 0 { + return nil, errors.New("empty pem block") + } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]), + C.int(len(pem_block))) + cert := C.PEM_read_bio_X509(bio, nil, nil, nil) + C.BIO_free(bio) + if cert == nil { + return nil, errorFromErrorQueue() + } + x := &Certificate{x: cert} + runtime.SetFinalizer(x, func(x *Certificate) { + C.X509_free(x.x) + }) + return x, nil +} + +// MarshalPEM converts the X509 certificate to PEM-encoded format +func (c *Certificate) MarshalPEM() (pem_block []byte, err error) { + bio := C.BIO_new(C.BIO_s_mem()) + if bio == nil { + return nil, errors.New("failed to allocate memory BIO") + } + defer C.BIO_free(bio) + if int(C.PEM_write_bio_X509(bio, c.x)) != 1 { + return nil, errors.New("failed dumping certificate") + } + return ioutil.ReadAll(asAnyBio(bio)) +} + +// PublicKey returns the public key embedded in the X509 certificate. +func (c *Certificate) PublicKey() (PublicKey, error) { + pkey := C.X509_get_pubkey(c.x) + if pkey == nil { + return nil, errors.New("no public key found") + } + key := &pKey{key: pkey} + runtime.SetFinalizer(key, func(key *pKey) { + C.EVP_PKEY_free(key.key) + }) + return key, nil +} + +// GetSerialNumberHex returns the certificate's serial number in hex format +func (c *Certificate) GetSerialNumberHex() (serial string) { + asn1_i := C.X509_get_serialNumber(c.x) + bignum := C.ASN1_INTEGER_to_BN(asn1_i, nil) + hex := C.BN_bn2hex(bignum) + serial = C.GoString(hex) + C.BN_free(bignum) + C.OPENSSL_free_not_a_macro(unsafe.Pointer(hex)) + return +} diff --git a/cert_test.go b/cert_test.go new file mode 100644 index 0000000..0dfac37 --- /dev/null +++ b/cert_test.go @@ -0,0 +1,101 @@ +// Copyright (C) 2014 Ryan Hileman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package openssl + +import ( + "testing" + "time" +) + +func TestCertGenerate(t *testing.T) { + key, err := GenerateRSAKey(2048) + if err != nil { + t.Fatal(err) + } + info := &CertificateInfo{ + Serial: 1, + Issued: 0, + Expires: 24 * time.Hour, + Country: "US", + Organization: "Test", + CommonName: "localhost", + } + cert, err := NewCertificate(info, key) + if err != nil { + t.Fatal(err) + } + if err := cert.Sign(key, EVP_SHA256); err != nil { + t.Fatal(err) + } +} + +func TestCAGenerate(t *testing.T) { + cakey, err := GenerateRSAKey(2048) + if err != nil { + t.Fatal(err) + } + info := &CertificateInfo{ + Serial: 1, + Issued: 0, + Expires: 24 * time.Hour, + Country: "US", + Organization: "Test CA", + CommonName: "CA", + } + ca, err := NewCertificate(info, cakey) + if err != nil { + t.Fatal(err) + } + if err := ca.AddExtensions(map[NID]string{ + NID_basic_constraints: "critical,CA:TRUE", + NID_key_usage: "critical,keyCertSign,cRLSign", + NID_subject_key_identifier: "hash", + NID_netscape_cert_type: "sslCA", + }); err != nil { + t.Fatal(err) + } + if err := ca.Sign(cakey, EVP_SHA256); err != nil { + t.Fatal(err) + } + key, err := GenerateRSAKey(2048) + if err != nil { + t.Fatal(err) + } + info = &CertificateInfo{ + Serial: 1, + Issued: 0, + Expires: 24 * time.Hour, + Country: "US", + Organization: "Test", + CommonName: "localhost", + } + cert, err := NewCertificate(info, key) + if err != nil { + t.Fatal(err) + } + if err := cert.AddExtensions(map[NID]string{ + NID_basic_constraints: "critical,CA:FALSE", + NID_key_usage: "keyEncipherment", + NID_ext_key_usage: "serverAuth", + }); err != nil { + t.Fatal(err) + } + if err := cert.SetIssuer(ca); err != nil { + t.Fatal(err) + } + if err := cert.Sign(cakey, EVP_SHA256); err != nil { + t.Fatal(err) + } +} diff --git a/ciphers.go b/ciphers.go index 46ea4ce..17d48e8 100644 --- a/ciphers.go +++ b/ciphers.go @@ -73,8 +73,8 @@ type Cipher struct { ptr *C.EVP_CIPHER } -func (c *Cipher) Nid() int { - return int(C.EVP_CIPHER_nid_not_a_macro(c.ptr)) +func (c *Cipher) Nid() NID { + return NID(C.EVP_CIPHER_nid_not_a_macro(c.ptr)) } func (c *Cipher) ShortName() (string, error) { @@ -93,7 +93,7 @@ func (c *Cipher) IVSize() int { return int(C.EVP_CIPHER_iv_length_not_a_macro(c.ptr)) } -func Nid2ShortName(nid int) (string, error) { +func Nid2ShortName(nid NID) (string, error) { sn := C.OBJ_nid2sn(C.int(nid)) if sn == nil { return "", fmt.Errorf("NID %d not found", nid) @@ -112,7 +112,7 @@ func GetCipherByName(name string) (*Cipher, error) { return &Cipher{ptr: p}, nil } -func GetCipherByNid(nid int) (*Cipher, error) { +func GetCipherByNid(nid NID) (*Cipher, error) { sn, err := Nid2ShortName(nid) if err != nil { return nil, err diff --git a/pem.go b/key.go similarity index 82% rename from pem.go rename to key.go index ae8135e..2c4eceb 100644 --- a/pem.go +++ b/key.go @@ -20,8 +20,6 @@ package openssl // #include // #include // -// void OPENSSL_free_not_a_macro(void *ref) { OPENSSL_free(ref); } -// // int EVP_SignInit_not_a_macro(EVP_MD_CTX *ctx, const EVP_MD *type) { // return EVP_SignInit(ctx, type); // } @@ -312,65 +310,24 @@ func LoadPublicKeyFromDER(der_block []byte) (PublicKey, error) { return p, nil } -type Certificate struct { - x *C.X509 - ref interface{} -} - -// LoadCertificateFromPEM loads an X509 certificate from a PEM-encoded block. -func LoadCertificateFromPEM(pem_block []byte) (*Certificate, error) { - if len(pem_block) == 0 { - return nil, errors.New("empty pem block") +// GenerateRSAKey generates a new RSA private key with an exponent of 3. +func GenerateRSAKey(bits int) (PrivateKey, error) { + exponent := 3 + rsa := C.RSA_generate_key(C.int(bits), C.ulong(exponent), nil, nil) + if rsa == nil { + return nil, errors.New("failed to generate RSA key") } - runtime.LockOSThread() - defer runtime.UnlockOSThread() - bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]), - C.int(len(pem_block))) - cert := C.PEM_read_bio_X509(bio, nil, nil, nil) - C.BIO_free(bio) - if cert == nil { - return nil, errorFromErrorQueue() + key := C.EVP_PKEY_new() + if key == nil { + return nil, errors.New("failed to allocate EVP_PKEY") } - x := &Certificate{x: cert} - runtime.SetFinalizer(x, func(x *Certificate) { - C.X509_free(x.x) + if C.EVP_PKEY_assign(key, C.EVP_PKEY_RSA, unsafe.Pointer(rsa)) != 1 { + C.EVP_PKEY_free(key) + return nil, errors.New("failed to assign RSA key") + } + p := &pKey{key: key} + runtime.SetFinalizer(p, func(p *pKey) { + C.EVP_PKEY_free(p.key) }) - return x, nil -} - -// MarshalPEM converts the X509 certificate to PEM-encoded format -func (c *Certificate) MarshalPEM() (pem_block []byte, err error) { - bio := C.BIO_new(C.BIO_s_mem()) - if bio == nil { - return nil, errors.New("failed to allocate memory BIO") - } - defer C.BIO_free(bio) - if int(C.PEM_write_bio_X509(bio, c.x)) != 1 { - return nil, errors.New("failed dumping certificate") - } - return ioutil.ReadAll(asAnyBio(bio)) -} - -// PublicKey returns the public key embedded in the X509 certificate. -func (c *Certificate) PublicKey() (PublicKey, error) { - pkey := C.X509_get_pubkey(c.x) - if pkey == nil { - return nil, errors.New("no public key found") - } - key := &pKey{key: pkey} - runtime.SetFinalizer(key, func(key *pKey) { - C.EVP_PKEY_free(key.key) - }) - return key, nil -} - -// GetSerialNumberHex returns the certificate's serial number in hex format -func (c *Certificate) GetSerialNumberHex() (serial string) { - asn1_i := C.X509_get_serialNumber(c.x) - bignum := C.ASN1_INTEGER_to_BN(asn1_i, nil) - hex := C.BN_bn2hex(bignum) - serial = C.GoString(hex) - C.BN_free(bignum) - C.OPENSSL_free_not_a_macro(unsafe.Pointer(hex)) - return + return p, nil } diff --git a/pem_test.go b/key_test.go similarity index 93% rename from pem_test.go rename to key_test.go index a79dc30..54752d3 100644 --- a/pem_test.go +++ b/key_test.go @@ -132,3 +132,18 @@ func TestMarshal(t *testing.T) { t.Fatal("invalid public key der bytes") } } + +func TestGenerate(t *testing.T) { + key, err := GenerateRSAKey(2048) + if err != nil { + t.Fatal(err) + } + _, err = key.MarshalPKIXPublicKeyPEM() + if err != nil { + t.Fatal(err) + } + _, err = key.MarshalPKCS1PrivateKeyPEM() + if err != nil { + t.Fatal(err) + } +} diff --git a/nid.go b/nid.go new file mode 100644 index 0000000..c80f237 --- /dev/null +++ b/nid.go @@ -0,0 +1,199 @@ +// Copyright (C) 2014 Ryan Hileman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package openssl + +type NID int + +const ( + NID_rsadsi NID = 1 + NID_pkcs NID = 2 + NID_md2 NID = 3 + NID_md5 NID = 4 + NID_rc4 NID = 5 + NID_rsaEncryption NID = 6 + NID_md2WithRSAEncryption NID = 7 + NID_md5WithRSAEncryption NID = 8 + NID_pbeWithMD2AndDES_CBC NID = 9 + NID_pbeWithMD5AndDES_CBC NID = 10 + NID_X500 NID = 11 + NID_X509 NID = 12 + NID_commonName NID = 13 + NID_countryName NID = 14 + NID_localityName NID = 15 + NID_stateOrProvinceName NID = 16 + NID_organizationName NID = 17 + NID_organizationalUnitName NID = 18 + NID_rsa NID = 19 + NID_pkcs7 NID = 20 + NID_pkcs7_data NID = 21 + NID_pkcs7_signed NID = 22 + NID_pkcs7_enveloped NID = 23 + NID_pkcs7_signedAndEnveloped NID = 24 + NID_pkcs7_digest NID = 25 + NID_pkcs7_encrypted NID = 26 + NID_pkcs3 NID = 27 + NID_dhKeyAgreement NID = 28 + NID_des_ecb NID = 29 + NID_des_cfb64 NID = 30 + NID_des_cbc NID = 31 + NID_des_ede NID = 32 + NID_des_ede3 NID = 33 + NID_idea_cbc NID = 34 + NID_idea_cfb64 NID = 35 + NID_idea_ecb NID = 36 + NID_rc2_cbc NID = 37 + NID_rc2_ecb NID = 38 + NID_rc2_cfb64 NID = 39 + NID_rc2_ofb64 NID = 40 + NID_sha NID = 41 + NID_shaWithRSAEncryption NID = 42 + NID_des_ede_cbc NID = 43 + NID_des_ede3_cbc NID = 44 + NID_des_ofb64 NID = 45 + NID_idea_ofb64 NID = 46 + NID_pkcs9 NID = 47 + NID_pkcs9_emailAddress NID = 48 + NID_pkcs9_unstructuredName NID = 49 + NID_pkcs9_contentType NID = 50 + NID_pkcs9_messageDigest NID = 51 + NID_pkcs9_signingTime NID = 52 + NID_pkcs9_countersignature NID = 53 + NID_pkcs9_challengePassword NID = 54 + NID_pkcs9_unstructuredAddress NID = 55 + NID_pkcs9_extCertAttributes NID = 56 + NID_netscape NID = 57 + NID_netscape_cert_extension NID = 58 + NID_netscape_data_type NID = 59 + NID_des_ede_cfb64 NID = 60 + NID_des_ede3_cfb64 NID = 61 + NID_des_ede_ofb64 NID = 62 + NID_des_ede3_ofb64 NID = 63 + NID_sha1 NID = 64 + NID_sha1WithRSAEncryption NID = 65 + NID_dsaWithSHA NID = 66 + NID_dsa_2 NID = 67 + NID_pbeWithSHA1AndRC2_CBC NID = 68 + NID_id_pbkdf2 NID = 69 + NID_dsaWithSHA1_2 NID = 70 + NID_netscape_cert_type NID = 71 + NID_netscape_base_url NID = 72 + NID_netscape_revocation_url NID = 73 + NID_netscape_ca_revocation_url NID = 74 + NID_netscape_renewal_url NID = 75 + NID_netscape_ca_policy_url NID = 76 + NID_netscape_ssl_server_name NID = 77 + NID_netscape_comment NID = 78 + NID_netscape_cert_sequence NID = 79 + NID_desx_cbc NID = 80 + NID_id_ce NID = 81 + NID_subject_key_identifier NID = 82 + NID_key_usage NID = 83 + NID_private_key_usage_period NID = 84 + NID_subject_alt_name NID = 85 + NID_issuer_alt_name NID = 86 + NID_basic_constraints NID = 87 + NID_crl_number NID = 88 + NID_certificate_policies NID = 89 + NID_authority_key_identifier NID = 90 + NID_bf_cbc NID = 91 + NID_bf_ecb NID = 92 + NID_bf_cfb64 NID = 93 + NID_bf_ofb64 NID = 94 + NID_mdc2 NID = 95 + NID_mdc2WithRSA NID = 96 + NID_rc4_40 NID = 97 + NID_rc2_40_cbc NID = 98 + NID_givenName NID = 99 + NID_surname NID = 100 + NID_initials NID = 101 + NID_uniqueIdentifier NID = 102 + NID_crl_distribution_points NID = 103 + NID_md5WithRSA NID = 104 + NID_serialNumber NID = 105 + NID_title NID = 106 + NID_description NID = 107 + NID_cast5_cbc NID = 108 + NID_cast5_ecb NID = 109 + NID_cast5_cfb64 NID = 110 + NID_cast5_ofb64 NID = 111 + NID_pbeWithMD5AndCast5_CBC NID = 112 + NID_dsaWithSHA1 NID = 113 + NID_md5_sha1 NID = 114 + NID_sha1WithRSA NID = 115 + NID_dsa NID = 116 + NID_ripemd160 NID = 117 + NID_ripemd160WithRSA NID = 119 + NID_rc5_cbc NID = 120 + NID_rc5_ecb NID = 121 + NID_rc5_cfb64 NID = 122 + NID_rc5_ofb64 NID = 123 + NID_rle_compression NID = 124 + NID_zlib_compression NID = 125 + NID_ext_key_usage NID = 126 + NID_id_pkix NID = 127 + NID_id_kp NID = 128 + NID_server_auth NID = 129 + NID_client_auth NID = 130 + NID_code_sign NID = 131 + NID_email_protect NID = 132 + NID_time_stamp NID = 133 + NID_ms_code_ind NID = 134 + NID_ms_code_com NID = 135 + NID_ms_ctl_sign NID = 136 + NID_ms_sgc NID = 137 + NID_ms_efs NID = 138 + NID_ns_sgc NID = 139 + NID_delta_crl NID = 140 + NID_crl_reason NID = 141 + NID_invalidity_date NID = 142 + NID_sxnet NID = 143 + NID_pbe_WithSHA1And128BitRC4 NID = 144 + NID_pbe_WithSHA1And40BitRC4 NID = 145 + NID_pbe_WithSHA1And3_Key_TripleDES_CBC NID = 146 + NID_pbe_WithSHA1And2_Key_TripleDES_CBC NID = 147 + NID_pbe_WithSHA1And128BitRC2_CBC NID = 148 + NID_pbe_WithSHA1And40BitRC2_CBC NID = 149 + NID_keyBag NID = 150 + NID_pkcs8ShroudedKeyBag NID = 151 + NID_certBag NID = 152 + NID_crlBag NID = 153 + NID_secretBag NID = 154 + NID_safeContentsBag NID = 155 + NID_friendlyName NID = 156 + NID_localKeyID NID = 157 + NID_x509Certificate NID = 158 + NID_sdsiCertificate NID = 159 + NID_x509Crl NID = 160 + NID_pbes2 NID = 161 + NID_pbmac1 NID = 162 + NID_hmacWithSHA1 NID = 163 + NID_id_qt_cps NID = 164 + NID_id_qt_unotice NID = 165 + NID_rc2_64_cbc NID = 166 + NID_SMIMECapabilities NID = 167 + NID_pbeWithMD2AndRC2_CBC NID = 168 + NID_pbeWithMD5AndRC2_CBC NID = 169 + NID_pbeWithSHA1AndDES_CBC NID = 170 + NID_ms_ext_req NID = 171 + NID_ext_req NID = 172 + NID_name NID = 173 + NID_dnQualifier NID = 174 + NID_id_pe NID = 175 + NID_id_ad NID = 176 + NID_info_access NID = 177 + NID_ad_OCSP NID = 178 + NID_ad_ca_issuers NID = 179 + NID_OCSP_sign NID = 180 +) From 1a2646cde329863648c928bae0f925b47004e1e3 Mon Sep 17 00:00:00 2001 From: Ryan Hileman Date: Thu, 13 Nov 2014 13:34:45 -0800 Subject: [PATCH 2/2] improve GC handling (fixes #10) --- cert.go | 2 ++ ctx.go | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/cert.go b/cert.go index 5c9b00e..8dc82f8 100644 --- a/cert.go +++ b/cert.go @@ -53,6 +53,7 @@ type Certificate struct { x *C.X509 Issuer *Certificate ref interface{} + pubKey PublicKey } type CertificateInfo struct { @@ -221,6 +222,7 @@ func (c *Certificate) SetExpireDate(when time.Duration) error { // SetPubKey assigns a new public key to a certificate. func (c *Certificate) SetPubKey(pubKey PublicKey) error { + c.pubKey = pubKey if C.X509_set_pubkey(c.x, pubKey.evpPKey()) != 1 { return errors.New("failed to set public key") } diff --git a/ctx.go b/ctx.go index 921e9be..538679f 100644 --- a/ctx.go +++ b/ctx.go @@ -101,6 +101,9 @@ var ( type Ctx struct { ctx *C.SSL_CTX + cert *Certificate + chain []*Certificate + key PrivateKey verify_cb VerifyCallback } @@ -244,6 +247,7 @@ func (c *Ctx) SetEllipticCurve(curve EllipticCurve) error { func (c *Ctx) UseCertificate(cert *Certificate) error { runtime.LockOSThread() defer runtime.UnlockOSThread() + c.cert = cert if int(C.SSL_CTX_use_certificate(c.ctx, cert.x)) != 1 { return errorFromErrorQueue() } @@ -255,6 +259,7 @@ func (c *Ctx) UseCertificate(cert *Certificate) error { func (c *Ctx) AddChainCertificate(cert *Certificate) error { runtime.LockOSThread() defer runtime.UnlockOSThread() + c.chain = append(c.chain, cert) if int(C.SSL_CTX_add_extra_chain_cert_not_a_macro(c.ctx, cert.x)) != 1 { return errorFromErrorQueue() } @@ -266,6 +271,7 @@ func (c *Ctx) AddChainCertificate(cert *Certificate) error { func (c *Ctx) UsePrivateKey(key PrivateKey) error { runtime.LockOSThread() defer runtime.UnlockOSThread() + c.key = key if int(C.SSL_CTX_use_PrivateKey(c.ctx, key.evpPKey())) != 1 { return errorFromErrorQueue() } @@ -274,7 +280,9 @@ func (c *Ctx) UsePrivateKey(key PrivateKey) error { type CertificateStore struct { store *C.X509_STORE - ctx *Ctx // for gc + // for GC + ctx *Ctx + certs []*Certificate } // GetCertificateStore returns the context's certificate store that will be @@ -292,6 +300,7 @@ func (c *Ctx) GetCertificateStore() *CertificateStore { func (s *CertificateStore) AddCertificate(cert *Certificate) error { runtime.LockOSThread() defer runtime.UnlockOSThread() + s.certs = append(s.certs, cert) if int(C.X509_STORE_add_cert(s.store, cert.x)) != 1 { return errorFromErrorQueue() }