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 }