github.com/Axway/agent-sdk@v1.1.101/pkg/config/tlsconfig.go (about) 1 package config 2 3 import ( 4 "crypto/tls" 5 "crypto/x509" 6 "errors" 7 "fmt" 8 "io/ioutil" 9 10 log "github.com/Axway/agent-sdk/pkg/util/log" 11 12 "github.com/Axway/agent-sdk/pkg/util/exception" 13 ) 14 15 // TLSCipherSuite - defined type 16 type TLSCipherSuite uint16 17 18 // Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml 19 var tlsCipherSuites = map[string]TLSCipherSuite{ 20 // ECDHE-ECDSA 21 "ECDHE-ECDSA-AES-128-CBC-SHA": TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA), 22 "ECDHE-ECDSA-AES-128-CBC-SHA256": TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256), 23 "ECDHE-ECDSA-AES-128-GCM-SHA256": TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256), 24 "ECDHE-ECDSA-AES-256-CBC-SHA": TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA), 25 "ECDHE-ECDSA-AES-256-GCM-SHA384": TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384), 26 "ECDHE-ECDSA-CHACHA20-POLY1305": TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305), 27 "ECDHE-ECDSA-RC4-128-SHA": TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA), 28 29 // ECDHE-RSA 30 "ECDHE-RSA-3DES-CBC3-SHA": TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA), 31 "ECDHE-RSA-AES-128-CBC-SHA": TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA), 32 "ECDHE-RSA-AES-128-CBC-SHA256": TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256), 33 "ECDHE-RSA-AES-128-GCM-SHA256": TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256), 34 "ECDHE-RSA-AES-256-CBC-SHA": TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA), 35 "ECDHE-RSA-AES-256-GCM-SHA384": TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384), 36 "ECDHE-RSA-CHACHA20-POLY1305": TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305), 37 "ECDHE-RSA-RC4-128-SHA": TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA), 38 39 // RSA-X 40 "RSA-RC4-128-SHA": TLSCipherSuite(tls.TLS_RSA_WITH_RC4_128_SHA), 41 "RSA-3DES-CBC3-SHA": TLSCipherSuite(tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA), 42 43 // RSA-AES 44 "RSA-AES-128-CBC-SHA": TLSCipherSuite(tls.TLS_RSA_WITH_AES_128_CBC_SHA), 45 "RSA-AES-128-CBC-SHA256": TLSCipherSuite(tls.TLS_RSA_WITH_AES_128_CBC_SHA256), 46 "RSA-AES-128-GCM-SHA256": TLSCipherSuite(tls.TLS_RSA_WITH_AES_128_GCM_SHA256), 47 "RSA-AES-256-CBC-SHA": TLSCipherSuite(tls.TLS_RSA_WITH_AES_256_CBC_SHA), 48 "RSA-AES-256-GCM-SHA384": TLSCipherSuite(tls.TLS_RSA_WITH_AES_256_GCM_SHA384), 49 50 // TLS 1.3 51 "TLS-AES-128-GCM-SHA256": TLSCipherSuite(tls.TLS_AES_128_GCM_SHA256), 52 "TLS-AES-256-GCM-SHA384": TLSCipherSuite(tls.TLS_AES_256_GCM_SHA384), 53 "TLS-CHACHA20-POLY1305-SHA256": TLSCipherSuite(tls.TLS_CHACHA20_POLY1305_SHA256), 54 } 55 56 // TLSDefaultCipherSuitesStringSlice - list of suites to use by default 57 func TLSDefaultCipherSuitesStringSlice() []string { 58 suites := TLSDefaultCipherSuites 59 60 var values []string 61 for _, v := range suites { 62 values = append(values, v.String()) 63 } 64 65 return values 66 } 67 68 // TLSDefaultCipherSuites - list of suites to use by default 69 var TLSDefaultCipherSuites = []TLSCipherSuite{ 70 TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384), 71 TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384), 72 TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305), 73 TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305), 74 TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256), 75 TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256), 76 TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256), 77 TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256), 78 } 79 80 var tlsCipherSuitesInverse = make(map[TLSCipherSuite]string, len(tlsCipherSuites)) 81 82 // Unpack - transforms the string into a constant. 83 func (cs *TLSCipherSuite) Unpack(s string) error { 84 if s == "" { 85 return nil 86 } 87 suite, found := tlsCipherSuites[s] 88 if !found { 89 return fmt.Errorf("invalid tls cipher suite '%v'", s) 90 } 91 92 *cs = suite 93 return nil 94 } 95 96 func (cs *TLSCipherSuite) String() string { 97 if s, found := tlsCipherSuitesInverse[*cs]; found { 98 return s 99 } 100 return "unknown" 101 } 102 103 func cipherAsValue(cs string) TLSCipherSuite { 104 if s, ok := tlsCipherSuites[cs]; ok { 105 return s 106 } 107 return TLSCipherSuite(0) // return bogus value 108 } 109 110 // TLSVersion - define type for version 111 type TLSVersion uint16 112 113 // Define all the possible TLS version. 114 var tlsVersions = map[string]TLSVersion{ 115 "TLS1.0": tls.VersionTLS10, 116 "TLS1.1": tls.VersionTLS11, 117 "TLS1.2": tls.VersionTLS12, 118 "TLS1.3": tls.VersionTLS13, 119 } 120 121 var tlsVersionsInverse = make(map[TLSVersion]string, len(tlsVersions)) 122 123 // Unpack transforms the string into a constant. 124 func (v *TLSVersion) Unpack(s string) error { 125 if s == "" { 126 return nil 127 } 128 version, found := tlsVersions[s] 129 if !found { 130 return fmt.Errorf("invalid tls version '%v'", s) 131 } 132 133 *v = version 134 return nil 135 } 136 137 // TLSDefaultMinVersionString - get the default min version string 138 func TLSDefaultMinVersionString() string { 139 return tlsVersionsInverse[TLSDefaultMinVersion] 140 } 141 142 // TLSDefaultMinVersion - get the default min version 143 var TLSDefaultMinVersion TLSVersion = tls.VersionTLS12 144 145 // TLSVersionAsValue - get the version value 146 func TLSVersionAsValue(cs string) TLSVersion { 147 // value of 0 means to use the default. Leave it alone. 148 if cs == "0" { 149 return TLSVersion(0) 150 } 151 if s, ok := tlsVersions[cs]; ok { 152 return s 153 } 154 return TLSVersion(1) // return a bogus value for validation checking 155 } 156 157 // Init creates a inverse representation of the values mapping. 158 func init() { 159 for cipherName, i := range tlsCipherSuites { 160 tlsCipherSuitesInverse[i] = cipherName 161 } 162 for versionName, i := range tlsVersions { 163 tlsVersionsInverse[i] = versionName 164 } 165 } 166 167 // TLSConfig - interface 168 type TLSConfig interface { 169 GetNextProtos() []string 170 IsInsecureSkipVerify() bool 171 GetCipherSuites() []TLSCipherSuite 172 GetMinVersion() TLSVersion 173 GetMaxVersion() TLSVersion 174 BuildTLSConfig() *tls.Config 175 } 176 177 // TLSConfiguration - A Config structure is used to configure a TLS client or server. 178 // After one has been passed to a TLS function it must not be modified. A Config may be reused; 179 // the tls package will also not modify it. 180 type TLSConfiguration struct { 181 IConfigValidator 182 // NextProtos is a list of supported application level protocols, in order of preference. 183 NextProtos []string `config:"nextProtos,replace" json:"nextProtos,omitempty"` 184 185 // InsecureSkipVerify controls whether a client verifies the server's certificate chain and host name. 186 // If InsecureSkipVerify is true, TLS accepts any certificate presented by the server and any host 187 // name in that certificate. In this mode, TLS is susceptible to man-in-the-middle attacks. 188 // This should be used only for testing. 189 InsecureSkipVerify bool `config:"insecureSkipVerify" json:"insecureSkipVerify,omitempty"` 190 191 // CipherSuites is a list of supported cipher suites for TLS versions up to TLS 1.2. If CipherSuites 192 // is nil, a default list of secure cipher suites is used, with a preference order based on hardware 193 // performance. The default cipher suites might change over Go versions. Note that TLS 1.3 194 // ciphersuites are not configurable. 195 CipherSuites []TLSCipherSuite `config:"cipherSuites,replace" json:"cipherSuites,omitempty"` 196 197 // MinVersion contains the minimum SSL/TLS version that is acceptable. If zero, then TLS 1.0 is taken as the minimum. 198 MinVersion TLSVersion `config:"minVersion" json:"minVersion,omitempty"` 199 200 // MaxVersion contains the maximum SSL/TLS version that is acceptable. If zero, then the maximum 201 // version supported by this package is used, which is currently TLS 1.3. 202 MaxVersion TLSVersion `config:"maxVersion" json:"maxVersion,omitempty"` 203 204 // RootCertificate 205 RootCertificatePath string `config:"rootCACertPath" json:"rootCACertPath,omitempty"` 206 207 // Client Certificate Path 208 ClientCertificatePath string `config:"clientCertPath" json:"clientCertPath,omitempty"` 209 210 // Client Key Path 211 ClientKeyPath string `config:"clientKeyPath" json:"clientKeyPath,omitempty"` 212 } 213 214 // NewTLSConfig - build default config 215 func NewTLSConfig() TLSConfig { 216 return &TLSConfiguration{ 217 InsecureSkipVerify: false, 218 NextProtos: []string{}, 219 CipherSuites: TLSDefaultCipherSuites, 220 MinVersion: TLSDefaultMinVersion, 221 MaxVersion: 0, 222 } 223 } 224 225 // BuildTLSConfig takes the TLSConfiguration and transforms it into a `tls.Config`. 226 func (c *TLSConfiguration) BuildTLSConfig() *tls.Config { 227 if c == nil { 228 // use default TLS settings, if config is empty. 229 return &tls.Config{} 230 } 231 232 ciphers := c.buildUintArrayFromSuites() 233 cfg := &tls.Config{ 234 MinVersion: uint16(c.MinVersion), 235 MaxVersion: uint16(c.MaxVersion), 236 InsecureSkipVerify: c.InsecureSkipVerify, 237 CipherSuites: ciphers, 238 NextProtos: c.NextProtos, 239 } 240 if c.RootCertificatePath != "" { 241 caCert, err := ioutil.ReadFile(c.RootCertificatePath) 242 if err == nil { // config validated in ValidateCfg 243 caCertPool := x509.NewCertPool() 244 caCertPool.AppendCertsFromPEM(caCert) 245 cfg.RootCAs = caCertPool 246 } 247 } 248 249 if c.ClientCertificatePath != "" && c.ClientKeyPath != "" { 250 cert, err := tls.LoadX509KeyPair(c.ClientCertificatePath, c.ClientKeyPath) 251 if err == nil { // config validated in ValidateCfg 252 cfg.Certificates = []tls.Certificate{cert} 253 } 254 } 255 256 return cfg 257 } 258 259 // LoadFrom takes the `tls.Config` and transforms it into a TLSConfiguration. 260 func (c *TLSConfiguration) LoadFrom(tlsCfg *tls.Config) { 261 if tlsCfg == nil { 262 c.InsecureSkipVerify = true 263 return 264 } 265 266 c.InsecureSkipVerify = tlsCfg.InsecureSkipVerify 267 c.MaxVersion = TLSVersion(tlsCfg.MaxVersion) 268 c.MinVersion = TLSVersion(tlsCfg.MinVersion) 269 270 c.CipherSuites = make([]TLSCipherSuite, 0) 271 for _, cipher := range tlsCfg.CipherSuites { 272 c.CipherSuites = append(c.CipherSuites, TLSCipherSuite(cipher)) 273 } 274 } 275 276 // buildUintArrayFromSuites - 277 func (c *TLSConfiguration) buildUintArrayFromSuites() []uint16 { 278 var ciphers []uint16 279 for _, suite := range c.CipherSuites { 280 ciphers = append(ciphers, uint16(suite)) 281 } 282 283 return ciphers 284 } 285 286 // NewCipherArray - create an array of TLSCipherSuite 287 func NewCipherArray(ciphers []string) []TLSCipherSuite { 288 if len(ciphers) == 0 { 289 return nil 290 } 291 292 var result []TLSCipherSuite 293 for _, v := range ciphers { 294 s := cipherAsValue(v) 295 if s == 0 { 296 log.Errorf("Invalid cipher suite value found: %v", v) 297 } 298 result = append(result, s) 299 } 300 return result 301 } 302 303 // GetNextProtos - 304 func (c *TLSConfiguration) GetNextProtos() []string { 305 return c.NextProtos 306 } 307 308 // IsInsecureSkipVerify - 309 func (c *TLSConfiguration) IsInsecureSkipVerify() bool { 310 return c.InsecureSkipVerify 311 } 312 313 // GetCipherSuites - 314 func (c *TLSConfiguration) GetCipherSuites() []TLSCipherSuite { 315 return c.CipherSuites 316 } 317 318 // GetMinVersion - 319 func (c *TLSConfiguration) GetMinVersion() TLSVersion { 320 return c.MinVersion 321 } 322 323 // GetMaxVersion - 324 func (c *TLSConfiguration) GetMaxVersion() TLSVersion { 325 return c.MaxVersion 326 } 327 328 // ValidateCfg - Validates the config, implementing IConfigInterface 329 func (c *TLSConfiguration) ValidateCfg() (err error) { 330 exception.Block{ 331 Try: func() { 332 c.validateConfig() 333 }, 334 Catch: func(e error) { 335 err = e 336 }, 337 }.Do() 338 339 return 340 } 341 342 // Validate - 343 func (c *TLSConfiguration) validateConfig() { 344 if c.MinVersion != 0 && !c.isValidMinVersion() { 345 exception.Throw(errors.New("ssl.minVersion not valid in config")) 346 } 347 348 if c.MaxVersion != 0 && !c.isValidMaxVersion() { 349 exception.Throw(errors.New("ssl.maxVersion not valid in config")) 350 } 351 352 if len(c.CipherSuites) != 0 && !c.isValidCiphers() { 353 exception.Throw(errors.New("ssl.cipherSuites not valid in config")) 354 } 355 if c.RootCertificatePath != "" { 356 _, err := ioutil.ReadFile(c.RootCertificatePath) 357 if err != nil { 358 exception.Throw(errors.New("ssl.rootCertificatePath not valid in config")) 359 } 360 } 361 if c.ClientCertificatePath != "" { 362 _, err := tls.LoadX509KeyPair(c.ClientCertificatePath, c.ClientKeyPath) 363 if err != nil { 364 exception.Throw(errors.New("ssl.clientCertificatePath or sss.clientKeyPath not valid in config")) 365 } 366 } 367 } 368 369 func (c *TLSConfiguration) isValidMinVersion() bool { 370 if c.MinVersion == 0 { 371 return true 372 } 373 374 _, ok := tlsVersionsInverse[c.MinVersion] 375 return ok 376 } 377 378 func (c *TLSConfiguration) isValidMaxVersion() bool { 379 if c.MaxVersion == 0 { 380 return true 381 } 382 383 _, ok := tlsVersionsInverse[c.MaxVersion] 384 return ok 385 } 386 387 func (c *TLSConfiguration) isValidCiphers() bool { 388 for _, v := range c.CipherSuites { 389 if _, ok := tlsCipherSuitesInverse[v]; !ok { 390 return false 391 } 392 } 393 394 return true 395 }