github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/security/pem.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package security
    12  
    13  import (
    14  	"crypto"
    15  	"crypto/ecdsa"
    16  	"crypto/rsa"
    17  	"crypto/x509"
    18  	"encoding/pem"
    19  	"io"
    20  	"os"
    21  	"strings"
    22  
    23  	"github.com/cockroachdb/errors"
    24  )
    25  
    26  // WritePEMToFile writes an arbitrary number of PEM blocks to a file.
    27  // The file "path" is created with "mode" and WRONLY|CREATE.
    28  // If overwrite is true, the file will be overwritten if it exists.
    29  func WritePEMToFile(path string, mode os.FileMode, overwrite bool, blocks ...*pem.Block) error {
    30  	flags := os.O_WRONLY | os.O_CREATE | os.O_TRUNC
    31  	if !overwrite {
    32  		flags |= os.O_EXCL
    33  	}
    34  	f, err := os.OpenFile(path, flags, mode)
    35  	if err != nil {
    36  		return err
    37  	}
    38  
    39  	for _, p := range blocks {
    40  		if err := pem.Encode(f, p); err != nil {
    41  			return errors.Errorf("could not encode PEM block: %v", err)
    42  		}
    43  	}
    44  
    45  	return f.Close()
    46  }
    47  
    48  // SafeWriteToFile writes the passed-in bytes to a file.
    49  // The file "path" is created with "mode" and WRONLY|CREATE.
    50  // If overwrite is true, the file will be overwritten if it exists.
    51  func SafeWriteToFile(path string, mode os.FileMode, overwrite bool, contents []byte) error {
    52  	flags := os.O_WRONLY | os.O_CREATE | os.O_TRUNC
    53  	if !overwrite {
    54  		flags |= os.O_EXCL
    55  	}
    56  	f, err := os.OpenFile(path, flags, mode)
    57  	if err != nil {
    58  		return err
    59  	}
    60  
    61  	n, err := f.Write(contents)
    62  	if err == nil && n < len(contents) {
    63  		err = io.ErrShortWrite
    64  	}
    65  	if err1 := f.Close(); err == nil {
    66  		err = err1
    67  	}
    68  	return err
    69  }
    70  
    71  // PrivateKeyToPEM generates a PEM block from a private key.
    72  func PrivateKeyToPEM(key crypto.PrivateKey) (*pem.Block, error) {
    73  	switch k := key.(type) {
    74  	case *rsa.PrivateKey:
    75  		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}, nil
    76  	case *ecdsa.PrivateKey:
    77  		bytes, err := x509.MarshalECPrivateKey(k)
    78  		if err != nil {
    79  			return nil, errors.Errorf("error marshaling ECDSA key: %s", err)
    80  		}
    81  		return &pem.Block{Type: "EC PRIVATE KEY", Bytes: bytes}, nil
    82  	default:
    83  		return nil, errors.Errorf("unknown key type: %v", k)
    84  	}
    85  }
    86  
    87  // PrivateKeyToPKCS8 encodes a private key into PKCS#8.
    88  func PrivateKeyToPKCS8(key crypto.PrivateKey) ([]byte, error) {
    89  	return x509.MarshalPKCS8PrivateKey(key)
    90  }
    91  
    92  // PEMToCertificates parses multiple certificate PEM blocks and returns them.
    93  // Each block must be a certificate.
    94  // It is allowed to have zero certificates.
    95  func PEMToCertificates(contents []byte) ([]*pem.Block, error) {
    96  	certs := make([]*pem.Block, 0)
    97  	for {
    98  		var block *pem.Block
    99  		block, contents = pem.Decode(contents)
   100  		if block == nil {
   101  			break
   102  		}
   103  		if block.Type != "CERTIFICATE" {
   104  			return nil, errors.Errorf("block #%d is of type %s, not CERTIFICATE", len(certs), block.Type)
   105  		}
   106  
   107  		certs = append(certs, block)
   108  	}
   109  
   110  	return certs, nil
   111  }
   112  
   113  // PEMToPrivateKey parses a PEM block and returns the private key.
   114  func PEMToPrivateKey(contents []byte) (crypto.PrivateKey, error) {
   115  	keyBlock, remaining := pem.Decode(contents)
   116  	if keyBlock == nil {
   117  		return nil, errors.New("no PEM data found")
   118  	}
   119  	if len(remaining) != 0 {
   120  		return nil, errors.New("more than one PEM block found")
   121  	}
   122  	if keyBlock.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlock.Type, " PRIVATE KEY") {
   123  		return nil, errors.Errorf("PEM block is of type %s", keyBlock.Type)
   124  	}
   125  
   126  	return parsePrivateKey(keyBlock.Bytes)
   127  }
   128  
   129  // Taken straight from: golang.org/src/crypto/tls/tls.go
   130  // Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
   131  // PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
   132  // OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
   133  func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
   134  	if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
   135  		return key, nil
   136  	}
   137  	if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
   138  		switch key := key.(type) {
   139  		case *rsa.PrivateKey, *ecdsa.PrivateKey:
   140  			return key, nil
   141  		default:
   142  			return nil, errors.New("found unknown private key type in PKCS#8 wrapping")
   143  		}
   144  	}
   145  	if key, err := x509.ParseECPrivateKey(der); err == nil {
   146  		return key, nil
   147  	}
   148  
   149  	return nil, errors.New("failed to parse private key")
   150  }