├── .gitignore ├── LICENSE ├── README.md ├── examples ├── tls-client │ └── main.go └── tls-server │ └── main.go └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .*.swp 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Manlio Perillo (manlio.perillo@gmail.com). 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the 13 | distribution. 14 | * Neither the name of Manlio Perillo nor the names of its 15 | contributors may be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tls-cert command 2 | 3 | The `tls-cert` command simplify the creation of TLS certificates when mutual 4 | authentication is required. The command support both self-signed certificates 5 | and certificates signed by a private CA. 6 | 7 | ## Usage 8 | 9 | `$ tls-cert [flags] Organization CommonName` 10 | 11 | ### Server certificate 12 | 13 | By default `tls-cert` will create a server certificate. `Organization` should 14 | be the name of the software and `CommonName` the primary DNS of the server. 15 | 16 | As an example: 17 | 18 | `$ tls-cert test localhost` 19 | 20 | will create the `test-server.key` and `test-server.crt` files. 21 | 22 | ### Client certificate 23 | 24 | When the `-usage` flag is set to `client`, `tls-cert` will create a client 25 | certificate. `Organization` should be the name of the software (the same one 26 | used for the server certificate) and `CommonName` the user email address. 27 | 28 | As an example: 29 | 30 | `$tls-cert` test manlio.perillo@gmail.com 31 | 32 | will create the `test-client.key` and `test-client.crt` files. 33 | 34 | ### CA certificate 35 | 36 | Using self-signed certificates is simple, but does not scale well when several 37 | clients with different certificates need to connect to a server. Another 38 | problem is that Chromium does not support self-signed certificates. 39 | 40 | `tls-cert` will create a CA certificate when the `-usage` flag is set to `ca`. 41 | `Organization` should be the user full name and `CommonName` the user name or 42 | nickname. 43 | 44 | As an example: 45 | 46 | `$ tls-cert manlio "Manlio Perillo"` 47 | 48 | will create the `manlio-ca.key` and `manlio-ca.crt` files. 49 | 50 | In order to sign a server or client certificate with a CA, set the `-ca` flag 51 | to the CA `CommonName`. 52 | 53 | As an example: 54 | 55 | `$ tls-cert -ca manlio test localhost` 56 | 57 | `$ tls-cert -ca manlio -client test manlio.perillo@gmail.com` 58 | 59 | ## Using client certificate in a browser 60 | 61 | Browsers support certificates in PKCS12 format. Currently this format is not 62 | supported by `tls-cert`, so `openssl` must be used. 63 | 64 | As an example: 65 | 66 | `$ openssl pkcs12 -inkey test-client.key -in test-client.crt -export -out name.p12` 67 | 68 | Add the CA `.crt` file in the list of trusted authorities, and add the `.p12` 69 | file to the list of your certificates. Only tested with Chromium. 70 | 71 | ## Code examples 72 | 73 | A simple HTTPS server and client are available in the `examples/tls-server` and 74 | `examples/tls-client` directories. 75 | 76 | The commands require the `Organization` as argument. By default self-signed 77 | certificates are assumed. Use of a CA can be specified with the `-ca` flag set 78 | to the `CommonName` of the authority. 79 | 80 | As an example: 81 | 82 | `$ tls-server test &` 83 | 84 | `$ tls-client test` 85 | 86 | or: 87 | 88 | `$ tls-server -ca manlio test &` 89 | 90 | `$ tls-client -ca manlio test` 91 | 92 | In order to build the examples, specify the `example` build tag 93 | (e.g. `go build -tags example examples/tls-server`), or run them using the 94 | `go run` command (e.g. `go run examples/tls-server/main`). 95 | 96 | The examples must be executed from the same directory where certificate files 97 | are stored. 98 | -------------------------------------------------------------------------------- /examples/tls-client/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Manlio Perillo. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build example 6 | 7 | // tls-client tests mutual authentication with TLS. 8 | package main 9 | 10 | import ( 11 | "crypto/tls" 12 | "crypto/x509" 13 | "flag" 14 | "fmt" 15 | "io" 16 | "io/ioutil" 17 | "log" 18 | "net/http" 19 | "os" 20 | ) 21 | 22 | var ca = flag.String("ca", "", "name of the CA") 23 | 24 | func main() { 25 | // Setup log. 26 | log.SetFlags(0) 27 | 28 | // Parse command line. 29 | flag.Usage = func() { 30 | fmt.Fprintln(os.Stderr, "Usage: tls-client [flags] Organization") 31 | fmt.Fprintln(os.Stderr, "Flags:") 32 | flag.PrintDefaults() 33 | os.Exit(2) 34 | } 35 | flag.Parse() 36 | if flag.NArg() != 1 { 37 | flag.Usage() 38 | os.Exit(2) 39 | } 40 | O := flag.Arg(0) 41 | 42 | cert, err := LoadClientCert(O) 43 | if err != nil { 44 | log.Fatal(err) 45 | } 46 | 47 | var caCert *x509.CertPool 48 | 49 | if *ca == "" { 50 | caCert, err = LoadServerCert(O) 51 | if err != nil { 52 | log.Fatal(err) 53 | } 54 | } else { 55 | caCert, err = LoadCACert(*ca) 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | } 60 | 61 | // Setup HTTPS client. 62 | config := tls.Config{ 63 | Certificates: []tls.Certificate{*cert}, 64 | RootCAs: caCert, 65 | } 66 | //config.BuildNameToCertificate() 67 | 68 | client := NewClient(&config) 69 | resp, err := client.Get("https://localhost:8080/hello") 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | _, err = io.Copy(os.Stdout, resp.Body) 74 | if err != nil { 75 | log.Fatal(err) 76 | } 77 | } 78 | 79 | func NewClient(config *tls.Config) *http.Client { 80 | transport := http.Transport{TLSClientConfig: config} 81 | 82 | return &http.Client{Transport: &transport} 83 | } 84 | 85 | func LoadClientCert(organization string) (*tls.Certificate, error) { 86 | keyName := fmt.Sprintf("%s-%s.key", organization, "client") 87 | certName := fmt.Sprintf("%s-%s.crt", organization, "client") 88 | 89 | cert, err := tls.LoadX509KeyPair(certName, keyName) 90 | if err != nil { 91 | return nil, fmt.Errorf("loading client certificate: %v", err) 92 | } 93 | 94 | return &cert, nil 95 | } 96 | 97 | func LoadServerCert(organization string) (*x509.CertPool, error) { 98 | certName := fmt.Sprintf("%s-%s.crt", organization, "server") 99 | 100 | return loadCACert(certName) 101 | } 102 | 103 | func LoadCACert(cname string) (*x509.CertPool, error) { 104 | certName := fmt.Sprintf("%s-%s.crt", cname, "ca") 105 | 106 | return loadCACert(certName) 107 | } 108 | 109 | func loadCACert(path string) (*x509.CertPool, error) { 110 | cert, err := ioutil.ReadFile(path) 111 | if err != nil { 112 | return nil, fmt.Errorf("loading CA certificate: %v", err) 113 | } 114 | pool := x509.NewCertPool() 115 | pool.AppendCertsFromPEM(cert) 116 | 117 | return pool, nil 118 | } 119 | -------------------------------------------------------------------------------- /examples/tls-server/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Manlio Perillo. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build example 6 | 7 | // tls-server tests mutual authentication with TLS. 8 | // 9 | // When using self signed certificates, verify authentication with: 10 | // ./tls-server O & 11 | // ./tls-client O 12 | // When using a certificate authority, verify authentication with: 13 | // ./tls-server -ca CN O & 14 | // ./tls-client -ca CN O 15 | // where O is the Organization name and CN is the Common Name of the authority. 16 | package main 17 | 18 | import ( 19 | "crypto/tls" 20 | "crypto/x509" 21 | "flag" 22 | "fmt" 23 | "io" 24 | "io/ioutil" 25 | "log" 26 | "net/http" 27 | "os" 28 | ) 29 | 30 | func HelloServer(w http.ResponseWriter, req *http.Request) { 31 | io.WriteString(w, "hello, world!\n") 32 | } 33 | 34 | func init() { 35 | http.HandleFunc("/hello", HelloServer) 36 | } 37 | 38 | var ca = flag.String("ca", "", "name of the CA") 39 | 40 | func main() { 41 | // Setup log. 42 | log.SetFlags(0) 43 | 44 | // Parse command line. 45 | flag.Usage = func() { 46 | fmt.Fprintln(os.Stderr, "Usage: tls-server [flags] Organization") 47 | fmt.Fprintln(os.Stderr, "Flags:") 48 | flag.PrintDefaults() 49 | os.Exit(2) 50 | } 51 | flag.Parse() 52 | if flag.NArg() != 1 { 53 | flag.Usage() 54 | os.Exit(2) 55 | } 56 | O := flag.Arg(0) 57 | 58 | cert, err := LoadServerCert(O) 59 | if err != nil { 60 | log.Fatal(err) 61 | } 62 | 63 | var caCert *x509.CertPool 64 | 65 | if *ca == "" { 66 | caCert, err = LoadClientCert(O) 67 | if err != nil { 68 | log.Fatal(err) 69 | } 70 | } else { 71 | caCert, err = LoadCACert(*ca) 72 | if err != nil { 73 | log.Fatal(err) 74 | } 75 | } 76 | 77 | // Setup HTTPS server. 78 | config := tls.Config{ 79 | Certificates: []tls.Certificate{*cert}, 80 | ClientCAs: caCert, 81 | ClientAuth: tls.RequireAndVerifyClientCert, 82 | } 83 | //config.BuildNameToCertificate() 84 | 85 | if err := ListenAndServeTLS("localhost:8080", &config); err != nil { 86 | log.Fatal(err) 87 | } 88 | } 89 | 90 | func ListenAndServeTLS(addr string, config *tls.Config) error { 91 | server := &http.Server{ 92 | Addr: ":8080", 93 | TLSConfig: config, 94 | } 95 | 96 | // Certificate and matching private key are already provided via config. 97 | return server.ListenAndServeTLS("", "") 98 | } 99 | 100 | func LoadServerCert(organization string) (*tls.Certificate, error) { 101 | keyName := fmt.Sprintf("%s-%s.key", organization, "server") 102 | certName := fmt.Sprintf("%s-%s.crt", organization, "server") 103 | 104 | cert, err := tls.LoadX509KeyPair(certName, keyName) 105 | if err != nil { 106 | return nil, fmt.Errorf("loading server certificate: %v", err) 107 | } 108 | 109 | return &cert, nil 110 | } 111 | 112 | func LoadClientCert(organization string) (*x509.CertPool, error) { 113 | certName := fmt.Sprintf("%s-%s.crt", organization, "client") 114 | 115 | return loadCACert(certName) 116 | } 117 | func LoadCACert(cname string) (*x509.CertPool, error) { 118 | certName := fmt.Sprintf("%s-%s.crt", cname, "ca") 119 | 120 | return loadCACert(certName) 121 | } 122 | 123 | func loadCACert(path string) (*x509.CertPool, error) { 124 | cert, err := ioutil.ReadFile(path) 125 | if err != nil { 126 | return nil, fmt.Errorf("loading CA certificate: %v", err) 127 | } 128 | pool := x509.NewCertPool() 129 | pool.AppendCertsFromPEM(cert) 130 | 131 | return pool, nil 132 | } 133 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Manlio Perillo. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // tls-cert is a command used to simplify the generation of certificate for 6 | // server and client. The command requires two positional arguments with the 7 | // Organization and Common Name. 8 | // 9 | // When a server certificate is generated, Common Name should be set to the 10 | // server primary DNS name and Organization should be set to the name of the 11 | // software. Additional DNS names and IP addresses are not supported. 12 | // 13 | // When a client certificate is generated, Common Name should be set to the 14 | // user email address and Organization to the name of the software. 15 | // 16 | // When a CA certificate is generated, Common Name should be set to the user 17 | // name and Organization to the user full name. 18 | // 19 | // The generated certificate can be verified using 20 | // openssl x509 -noout -text -in name.crt 21 | // 22 | // A PKCS12 file for use in a browser (for client authentication) can be 23 | // generated with 24 | // openssl pkcs12 -inkey name.key -in name.crt -export -out name.p12 25 | package main 26 | 27 | import ( 28 | "crypto/rand" 29 | "crypto/rsa" 30 | "crypto/x509" 31 | "crypto/x509/pkix" 32 | "encoding/pem" 33 | "flag" 34 | "fmt" 35 | "io/ioutil" 36 | "log" 37 | "math/big" 38 | "os" 39 | "time" 40 | ) 41 | 42 | type Certificate struct { 43 | Certificate *x509.Certificate 44 | PrivateKey *rsa.PrivateKey 45 | } 46 | 47 | // A Config structure is used to generate a TLS certificate. 48 | type Config struct { 49 | Organization string 50 | CommonName string 51 | KeySize int 52 | KeyUsage x509.KeyUsage 53 | ExtKeyUsage x509.ExtKeyUsage 54 | Lifetime time.Duration 55 | } 56 | 57 | const help = ` 58 | When a server certificate is generated, Common Name should be set to the 59 | server primary DNS name and Organization should be set to the name of the 60 | software. 61 | 62 | When a client certificate is generated, Common Name should be set to the user 63 | email address and Organization to the name of the software. 64 | 65 | When a CA certificate is generated, Common Name should be set to the user name 66 | and Organization to the user full name. 67 | ` 68 | 69 | var ( 70 | usage = flag.String("usage", "server", "usage of the certificate") 71 | lifetime = flag.Int("lifetime", 0, "certificate lifetime in days") 72 | caName = flag.String("ca", "", "name of the CA to use for signing") 73 | ) 74 | 75 | var ( 76 | // The default RSA key size. 77 | // CA certificates have a long lifetime so 4096 is used, while server and 78 | // client certificates have a short lifetime and TLS should be as fast as 79 | // possible, so 2048 is used. 80 | defaultKeySize = map[string]int{ 81 | "ca": 4096, 82 | "server": 2048, 83 | "client": 2048, 84 | } 85 | 86 | // The default certificate lifetime, in days. 87 | defaultLifetime = map[string]int{ 88 | "ca": 10 * 365, 89 | "server": 365, 90 | "client": 365, 91 | } 92 | ) 93 | 94 | func main() { 95 | // Setup log. 96 | log.SetFlags(0) 97 | 98 | // Parse command line. 99 | flag.Usage = func() { 100 | fmt.Fprintln(os.Stderr, "Usage: tls-cert [flags] Organization CommonName") 101 | fmt.Fprintln(os.Stderr, "Flags:") 102 | flag.PrintDefaults() 103 | fmt.Fprint(os.Stderr, help) 104 | os.Exit(2) 105 | } 106 | flag.Parse() 107 | if flag.NArg() != 2 { 108 | flag.Usage() 109 | os.Exit(2) 110 | } 111 | if *usage != "ca" && *usage != "server" && *usage != "client" { 112 | flag.Usage() 113 | os.Exit(2) 114 | } 115 | 116 | var keyName string 117 | var certName string 118 | 119 | O := flag.Arg(0) 120 | CN := flag.Arg(1) 121 | if *lifetime == 0 { 122 | *lifetime = defaultLifetime[*usage] 123 | } 124 | config := Config{ 125 | Organization: O, 126 | CommonName: CN, 127 | KeySize: defaultKeySize[*usage], 128 | Lifetime: time.Duration(*lifetime) * 24 * time.Hour, 129 | } 130 | 131 | switch *usage { 132 | case "ca": 133 | config.KeyUsage = x509.KeyUsageCertSign 134 | config.ExtKeyUsage = x509.ExtKeyUsageAny 135 | keyName = fmt.Sprintf("%s-%s.key", CN, "ca") 136 | certName = fmt.Sprintf("%s-%s.crt", CN, "ca") 137 | case "server": 138 | config.KeyUsage = x509.KeyUsageKeyEncipherment 139 | config.ExtKeyUsage = x509.ExtKeyUsageServerAuth 140 | keyName = fmt.Sprintf("%s-%s.key", O, "server") 141 | certName = fmt.Sprintf("%s-%s.crt", O, "server") 142 | case "client": 143 | config.KeyUsage = x509.KeyUsageKeyEncipherment 144 | config.ExtKeyUsage = x509.ExtKeyUsageClientAuth 145 | keyName = fmt.Sprintf("%s-%s.key", O, "client") 146 | certName = fmt.Sprintf("%s-%s.crt", O, "client") 147 | } 148 | 149 | key, err := GenerateKey(config) 150 | if err != nil { 151 | log.Fatal(err) 152 | } 153 | if err := WriteKey(key, keyName); err != nil { 154 | log.Fatal(err) 155 | } 156 | 157 | var cert []byte 158 | 159 | if *caName == "" { 160 | cert, err = CreateSelfSignedCert(key, config) 161 | if err != nil { 162 | log.Fatal(err) 163 | } 164 | } else { 165 | ca, err := LoadCA(*caName) 166 | if err != nil { 167 | log.Fatal(err) 168 | } 169 | cert, err = CreateCert(key, ca, config) 170 | if err != nil { 171 | log.Fatal(err) 172 | } 173 | } 174 | if err := WriteCert(cert, certName); err != nil { 175 | log.Fatal(err) 176 | } 177 | } 178 | 179 | // GenerateKey generates a private key. 180 | func GenerateKey(config Config) (*rsa.PrivateKey, error) { 181 | key, err := rsa.GenerateKey(rand.Reader, config.KeySize) 182 | if err != nil { 183 | return nil, fmt.Errorf("generating private key: %v", err) 184 | } 185 | 186 | return key, nil 187 | } 188 | 189 | // CreateSelfSignedCert creates a self signed certificate using specified 190 | // private key. 191 | func CreateSelfSignedCert(key *rsa.PrivateKey, config Config) ([]byte, error) { 192 | now := time.Now() 193 | 194 | max := new(big.Int).Lsh(big.NewInt(1), 128) 195 | n, err := rand.Int(rand.Reader, max) 196 | if err != nil { 197 | return nil, fmt.Errorf("generating serial number: %v", err) 198 | } 199 | 200 | template := x509.Certificate{ 201 | SerialNumber: n, 202 | Subject: pkix.Name{ 203 | Organization: []string{config.Organization}, 204 | CommonName: config.CommonName, 205 | }, 206 | 207 | NotBefore: now, 208 | NotAfter: now.Add(config.Lifetime), 209 | 210 | KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | config.KeyUsage, 211 | BasicConstraintsValid: true, 212 | 213 | IsCA: true, 214 | } 215 | if config.ExtKeyUsage != x509.ExtKeyUsageAny { 216 | template.ExtKeyUsage = []x509.ExtKeyUsage{config.ExtKeyUsage} 217 | } 218 | 219 | cert, err := x509.CreateCertificate(rand.Reader, &template, &template, 220 | &key.PublicKey, key) 221 | if err != nil { 222 | return nil, fmt.Errorf("creating certificate: %v", err) 223 | } 224 | 225 | return cert, nil 226 | } 227 | 228 | // CreateCert creates a certificate using the specified private key (only the 229 | // public part will be used) and signed using specified CA. 230 | func CreateCert(key *rsa.PrivateKey, ca *Certificate, config Config) ([]byte, error) { 231 | now := time.Now() 232 | 233 | max := new(big.Int).Lsh(big.NewInt(1), 128) 234 | n, err := rand.Int(rand.Reader, max) 235 | if err != nil { 236 | return nil, fmt.Errorf("generating serial number: %v", err) 237 | } 238 | 239 | template := x509.Certificate{ 240 | SerialNumber: n, 241 | Subject: pkix.Name{ 242 | Organization: []string{config.Organization}, 243 | CommonName: config.CommonName, 244 | }, 245 | 246 | NotBefore: now, 247 | NotAfter: now.Add(config.Lifetime), 248 | 249 | KeyUsage: x509.KeyUsageDigitalSignature | config.KeyUsage, 250 | ExtKeyUsage: []x509.ExtKeyUsage{config.ExtKeyUsage}, 251 | } 252 | 253 | cert, err := x509.CreateCertificate(rand.Reader, &template, ca.Certificate, 254 | &key.PublicKey, ca.PrivateKey) 255 | if err != nil { 256 | return nil, fmt.Errorf("creating certificate: %v", err) 257 | } 258 | 259 | return cert, nil 260 | } 261 | 262 | // WriteKey writes the private key to path. The file will be accessible only 263 | // to current user. 264 | func WriteKey(key *rsa.PrivateKey, path string) error { 265 | file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) 266 | if err != nil { 267 | return err 268 | } 269 | defer file.Close() 270 | 271 | b := pem.Block{ 272 | Type: "RSA PRIVATE KEY", 273 | Bytes: x509.MarshalPKCS1PrivateKey(key), 274 | } 275 | if err := pem.Encode(file, &b); err != nil { 276 | // Encode can fail only for I/O errors. 277 | return fmt.Errorf("writing private key to %q: %v", path, err) 278 | } 279 | 280 | return nil 281 | } 282 | 283 | // WriteCert writes the certificate to path. 284 | func WriteCert(cert []byte, path string) error { 285 | file, err := os.Create(path) 286 | if err != nil { 287 | return err 288 | } 289 | defer file.Close() 290 | 291 | b := pem.Block{ 292 | Type: "CERTIFICATE", 293 | Bytes: cert, 294 | } 295 | if err := pem.Encode(file, &b); err != nil { 296 | return fmt.Errorf("writing certificate to %q: %v", path, err) 297 | } 298 | 299 | return nil 300 | } 301 | 302 | // LoadCA loads CA certificate and private key pair. 303 | func LoadCA(name string) (*Certificate, error) { 304 | // NOTE(mperillo): We do not use the tls.LoadX509KeyPair function, since 305 | // the certificate is stored as raw bytes, and not as x509.Certificate. 306 | // It also supports chained certificates, and we don't need them. 307 | certName := fmt.Sprintf("%s-%s.crt", name, "ca") 308 | keyName := fmt.Sprintf("%s-%s.key", name, "ca") 309 | 310 | // Load certificate. 311 | data, err := loadPem(certName) 312 | if err != nil { 313 | return nil, fmt.Errorf("reading CA certificate file: %v", err) 314 | } 315 | cert, err := x509.ParseCertificate(data) 316 | if err != nil { 317 | return nil, fmt.Errorf("parsing CA certificate: %v", err) 318 | } 319 | 320 | // Load private key. 321 | data, err = loadPem(keyName) 322 | if err != nil { 323 | return nil, fmt.Errorf("reading CA key file: %v", err) 324 | } 325 | key, err := x509.ParsePKCS1PrivateKey(data) 326 | if err != nil { 327 | return nil, fmt.Errorf("parsing CA key: %v", err) 328 | } 329 | 330 | ca := Certificate{ 331 | Certificate: cert, 332 | PrivateKey: key, 333 | } 334 | 335 | return &ca, nil 336 | } 337 | 338 | func loadPem(path string) ([]byte, error) { 339 | buf, err := ioutil.ReadFile(path) 340 | if err != nil { 341 | return nil, err 342 | } 343 | 344 | // Ignore errors and additional blocks. 345 | // Any problem will be report by either ParsePKCS1PrivateKey or 346 | // ParseCertificate functions. 347 | data, _ := pem.Decode(buf) 348 | if data == nil { 349 | return []byte(""), nil 350 | } 351 | 352 | return data.Bytes, nil 353 | } 354 | --------------------------------------------------------------------------------