storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/config/certs.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2015, 2016, 2017 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package config 18 19 import ( 20 "bytes" 21 "crypto" 22 "crypto/ecdsa" 23 "crypto/tls" 24 "crypto/x509" 25 "encoding/pem" 26 "errors" 27 "io/ioutil" 28 29 "storj.io/minio/pkg/env" 30 ) 31 32 // EnvCertPassword is the environment variable which contains the password used 33 // to decrypt the TLS private key. It must be set if the TLS private key is 34 // password protected. 35 const EnvCertPassword = "MINIO_CERT_PASSWD" 36 37 // ParsePublicCertFile - parses public cert into its *x509.Certificate equivalent. 38 func ParsePublicCertFile(certFile string) (x509Certs []*x509.Certificate, err error) { 39 // Read certificate file. 40 var data []byte 41 if data, err = ioutil.ReadFile(certFile); err != nil { 42 return nil, err 43 } 44 45 // Trimming leading and tailing white spaces. 46 data = bytes.TrimSpace(data) 47 48 // Parse all certs in the chain. 49 current := data 50 for len(current) > 0 { 51 var pemBlock *pem.Block 52 if pemBlock, current = pem.Decode(current); pemBlock == nil { 53 return nil, ErrSSLUnexpectedData(nil).Msg("Could not read PEM block from file %s", certFile) 54 } 55 56 var x509Cert *x509.Certificate 57 if x509Cert, err = x509.ParseCertificate(pemBlock.Bytes); err != nil { 58 return nil, ErrSSLUnexpectedData(err) 59 } 60 61 x509Certs = append(x509Certs, x509Cert) 62 } 63 64 if len(x509Certs) == 0 { 65 return nil, ErrSSLUnexpectedData(nil).Msg("Empty public certificate file %s", certFile) 66 } 67 68 return x509Certs, nil 69 } 70 71 // LoadX509KeyPair - load an X509 key pair (private key , certificate) 72 // from the provided paths. The private key may be encrypted and is 73 // decrypted using the ENV_VAR: MINIO_CERT_PASSWD. 74 func LoadX509KeyPair(certFile, keyFile string) (tls.Certificate, error) { 75 certPEMBlock, err := ioutil.ReadFile(certFile) 76 if err != nil { 77 return tls.Certificate{}, ErrSSLUnexpectedError(err) 78 } 79 keyPEMBlock, err := ioutil.ReadFile(keyFile) 80 if err != nil { 81 return tls.Certificate{}, ErrSSLUnexpectedError(err) 82 } 83 key, rest := pem.Decode(keyPEMBlock) 84 if len(rest) > 0 { 85 return tls.Certificate{}, ErrSSLUnexpectedData(nil).Msg("The private key contains additional data") 86 } 87 if x509.IsEncryptedPEMBlock(key) { 88 password := env.Get(EnvCertPassword, "") 89 if len(password) == 0 { 90 return tls.Certificate{}, ErrSSLNoPassword(nil) 91 } 92 decryptedKey, decErr := x509.DecryptPEMBlock(key, []byte(password)) 93 if decErr != nil { 94 return tls.Certificate{}, ErrSSLWrongPassword(decErr) 95 } 96 keyPEMBlock = pem.EncodeToMemory(&pem.Block{Type: key.Type, Bytes: decryptedKey}) 97 } 98 cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock) 99 if err != nil { 100 return tls.Certificate{}, ErrSSLUnexpectedData(nil).Msg(err.Error()) 101 } 102 // Ensure that the private key is not a P-384 or P-521 EC key. 103 // The Go TLS stack does not provide constant-time implementations of P-384 and P-521. 104 if priv, ok := cert.PrivateKey.(crypto.Signer); ok { 105 if pub, ok := priv.Public().(*ecdsa.PublicKey); ok { 106 switch pub.Params().Name { 107 case "P-384": 108 fallthrough 109 case "P-521": 110 // unfortunately there is no cleaner way to check 111 return tls.Certificate{}, ErrSSLUnexpectedData(nil).Msg("tls: the ECDSA curve '%s' is not supported", pub.Params().Name) 112 } 113 } 114 } 115 return cert, nil 116 } 117 118 // EnsureCertAndKey checks if both client certificate and key paths are provided 119 func EnsureCertAndKey(ClientCert, ClientKey string) error { 120 if (ClientCert != "" && ClientKey == "") || 121 (ClientCert == "" && ClientKey != "") { 122 return errors.New("cert and key must be specified as a pair") 123 } 124 return nil 125 }