1 module hunt.net.ssl.SSLParameters; 2 3 4 import hunt.Exceptions; 5 import hunt.collection; 6 7 import std.conv; 8 9 /** 10 * Encapsulates parameters for an SSL/TLS connection. The parameters 11 * are the list of ciphersuites to be accepted in an SSL/TLS handshake, 12 * the list of protocols to be allowed, the endpoint identification 13 * algorithm during SSL/TLS handshaking, the Server Name Indication (SNI), 14 * the algorithm constraints and whether SSL/TLS servers should request 15 * or require client authentication, etc. 16 * <p> 17 * SSLParameters can be created via the constructors in this class. 18 * Objects can also be obtained using the <code>getSSLParameters()</code> 19 * methods in 20 * {@link SSLSocket#getSSLParameters SSLSocket} and 21 * {@link SSLServerSocket#getSSLParameters SSLServerSocket} and 22 * {@link SSLEngine#getSSLParameters SSLEngine} or the 23 * {@link SSLContext#getDefaultSSLParameters getDefaultSSLParameters()} and 24 * {@link SSLContext#getSupportedSSLParameters getSupportedSSLParameters()} 25 * methods in <code>SSLContext</code>. 26 * <p> 27 * SSLParameters can be applied to a connection via the methods 28 * {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and 29 * {@link SSLServerSocket#setSSLParameters SSLServerSocket.setSSLParameters()} 30 * and {@link SSLEngine#setSSLParameters SSLEngine.setSSLParameters()}. 31 * 32 * @see SSLSocket 33 * @see SSLEngine 34 * @see SSLContext 35 * 36 */ 37 class SSLParameters { 38 39 private string[] cipherSuites; 40 private string[] protocols; 41 private bool wantClientAuth; 42 private bool needClientAuth; 43 private string identificationAlgorithm; 44 // private Map!(int, SNIServerName) sniNames = null; 45 // private Map!(int, SNIMatcher) sniMatchers = null; 46 private bool preferLocalCipherSuites; 47 48 /** 49 * Constructs SSLParameters. 50 * <p> 51 * The values of cipherSuites, protocols, cryptographic algorithm 52 * constraints, endpoint identification algorithm, server names and 53 * server name matchers are set to <code>null</code>, useCipherSuitesOrder, 54 * wantClientAuth and needClientAuth are set to <code>false</code>. 55 */ 56 this() { 57 // empty 58 } 59 60 /** 61 * Constructs SSLParameters from the specified array of ciphersuites. 62 * <p> 63 * Calling this constructor is equivalent to calling the no-args 64 * constructor followed by 65 * <code>setCipherSuites(cipherSuites);</code>. 66 * 67 * @param cipherSuites the array of ciphersuites (or null) 68 */ 69 this(string[] cipherSuites) { 70 setCipherSuites(cipherSuites); 71 } 72 73 /** 74 * Constructs SSLParameters from the specified array of ciphersuites 75 * and protocols. 76 * <p> 77 * Calling this constructor is equivalent to calling the no-args 78 * constructor followed by 79 * <code>setCipherSuites(cipherSuites); setProtocols(protocols);</code>. 80 * 81 * @param cipherSuites the array of ciphersuites (or null) 82 * @param protocols the array of protocols (or null) 83 */ 84 this(string[] cipherSuites, string[] protocols) { 85 setCipherSuites(cipherSuites); 86 setProtocols(protocols); 87 } 88 89 private static string[] clone(string[] s) { 90 return (s is null) ? null : s.dup; 91 } 92 93 /** 94 * Returns a copy of the array of ciphersuites or null if none 95 * have been set. 96 * 97 * @return a copy of the array of ciphersuites or null if none 98 * have been set. 99 */ 100 string[] getCipherSuites() { 101 return clone(cipherSuites); 102 } 103 104 /** 105 * Sets the array of ciphersuites. 106 * 107 * @param cipherSuites the array of ciphersuites (or null) 108 */ 109 void setCipherSuites(string[] cipherSuites) { 110 this.cipherSuites = clone(cipherSuites); 111 } 112 113 /** 114 * Returns a copy of the array of protocols or null if none 115 * have been set. 116 * 117 * @return a copy of the array of protocols or null if none 118 * have been set. 119 */ 120 string[] getProtocols() { 121 return clone(protocols); 122 } 123 124 /** 125 * Sets the array of protocols. 126 * 127 * @param protocols the array of protocols (or null) 128 */ 129 void setProtocols(string[] protocols) { 130 this.protocols = clone(protocols); 131 } 132 133 /** 134 * Returns whether client authentication should be requested. 135 * 136 * @return whether client authentication should be requested. 137 */ 138 bool getWantClientAuth() { 139 return wantClientAuth; 140 } 141 142 /** 143 * Sets whether client authentication should be requested. Calling 144 * this method clears the <code>needClientAuth</code> flag. 145 * 146 * @param wantClientAuth whether client authentication should be requested 147 */ 148 void setWantClientAuth(bool wantClientAuth) { 149 this.wantClientAuth = wantClientAuth; 150 this.needClientAuth = false; 151 } 152 153 /** 154 * Returns whether client authentication should be required. 155 * 156 * @return whether client authentication should be required. 157 */ 158 bool getNeedClientAuth() { 159 return needClientAuth; 160 } 161 162 /** 163 * Sets whether client authentication should be required. Calling 164 * this method clears the <code>wantClientAuth</code> flag. 165 * 166 * @param needClientAuth whether client authentication should be required 167 */ 168 void setNeedClientAuth(bool needClientAuth) { 169 this.wantClientAuth = false; 170 this.needClientAuth = needClientAuth; 171 } 172 173 /** 174 * Returns the cryptographic algorithm constraints. 175 * 176 * @return the cryptographic algorithm constraints, or null if the 177 * constraints have not been set 178 * 179 * @see #setAlgorithmConstraints(AlgorithmConstraints) 180 * 181 */ 182 // AlgorithmConstraints getAlgorithmConstraints() { 183 // return algorithmConstraints; 184 // } 185 186 /** 187 * Sets the cryptographic algorithm constraints, which will be used 188 * in addition to any configured by the runtime environment. 189 * <p> 190 * If the <code>constraints</code> parameter is non-null, every 191 * cryptographic algorithm, key and algorithm parameters used in the 192 * SSL/TLS handshake must be permitted by the constraints. 193 * 194 * @param constraints the algorithm constraints (or null) 195 * 196 */ 197 // void setAlgorithmConstraints(AlgorithmConstraints constraints) { 198 // // the constraints object is immutable 199 // this.algorithmConstraints = constraints; 200 // } 201 202 /** 203 * Gets the endpoint identification algorithm. 204 * 205 * @return the endpoint identification algorithm, or null if none 206 * has been set. 207 * 208 * @see X509ExtendedTrustManager 209 * @see #setEndpointIdentificationAlgorithm(string) 210 * 211 */ 212 string getEndpointIdentificationAlgorithm() { 213 return identificationAlgorithm; 214 } 215 216 /** 217 * Sets the endpoint identification algorithm. 218 * <p> 219 * If the <code>algorithm</code> parameter is non-null or non-empty, the 220 * endpoint identification/verification procedures must be handled during 221 * SSL/TLS handshaking. This is to prevent man-in-the-middle attacks. 222 * 223 * @param algorithm The standard string name of the endpoint 224 * identification algorithm (or null). See Appendix A in the <a href= 225 * "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA"> 226 * Java Cryptography Architecture API Specification & Reference </a> 227 * for information about standard algorithm names. 228 * 229 * @see X509ExtendedTrustManager 230 * 231 */ 232 void setEndpointIdentificationAlgorithm(string algorithm) { 233 this.identificationAlgorithm = algorithm; 234 } 235 236 /** 237 * Sets the desired {@link SNIServerName}s of the Server Name 238 * Indication (SNI) parameter. 239 * <P> 240 * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s 241 * operating in client mode. 242 * <P> 243 * Note that the {@code serverNames} list is cloned 244 * to protect against subsequent modification. 245 * 246 * @param serverNames 247 * the list of desired {@link SNIServerName}s (or null) 248 * 249 * @throws NullPointerException if the {@code serverNames} 250 * contains {@code null} element 251 * @throws IllegalArgumentException if the {@code serverNames} 252 * contains more than one name of the same name type 253 * 254 * @see SNIServerName 255 * @see #getServerNames() 256 * 257 */ 258 // void setServerNames(List!SNIServerName serverNames) { 259 // if (serverNames !is null) { 260 // if (!serverNames.isEmpty()) { 261 // sniNames = new HashMap!(int, SNIServerName)(serverNames.size()); // LinkedHashMap<>(serverNames.size()); 262 // foreach (SNIServerName serverName ; serverNames) { 263 // if (sniNames.put(serverName.getType(), 264 // serverName) !is null) { 265 // throw new IllegalArgumentException( 266 // "Duplicated server name of type " ~ 267 // serverName.getType().to!string()); 268 // } 269 // } 270 // } else { 271 // sniNames = Collections.emptyMap!(int, SNIServerName)(); 272 // } 273 // } else { 274 // sniNames = null; 275 // } 276 // } 277 278 /** 279 * Returns a {@link List} containing all {@link SNIServerName}s of the 280 * Server Name Indication (SNI) parameter, or null if none has been set. 281 * <P> 282 * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s 283 * operating in client mode. 284 * <P> 285 * For SSL/TLS connections, the underlying SSL/TLS provider 286 * may specify a default value for a certain server name type. In 287 * client mode, it is recommended that, by default, providers should 288 * include the server name indication whenever the server can be located 289 * by a supported server name type. 290 * <P> 291 * It is recommended that providers initialize default Server Name 292 * Indications when creating {@code SSLSocket}/{@code SSLEngine}s. 293 * In the following examples, the server name could be represented by an 294 * instance of {@link SNIHostName} which has been initialized with the 295 * hostname "www.example.com" and type 296 * {@link StandardConstants#SNI_HOST_NAME}. 297 * 298 * <pre> 299 * Socket socket = 300 * sslSocketFactory.createSocket("www.example.com", 443); 301 * </pre> 302 * or 303 * <pre> 304 * SSLEngine engine = 305 * sslContext.createSSLEngine("www.example.com", 443); 306 * </pre> 307 * <P> 308 * 309 * @return null or an immutable list of non-null {@link SNIServerName}s 310 * 311 * @see List 312 * @see #setServerNames(List) 313 * 314 */ 315 // List!SNIServerName getServerNames() { 316 // if (sniNames !is null) { 317 // if (!sniNames.isEmpty()) { 318 // return new ArrayList!(SNIServerName)(sniNames.values()); 319 // } else { 320 // return new EmptyList!(SNIServerName)(); 321 // } 322 // } 323 324 // return null; 325 // } 326 327 /** 328 * Sets the {@link SNIMatcher}s of the Server Name Indication (SNI) 329 * parameter. 330 * <P> 331 * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s 332 * operating in server mode. 333 * <P> 334 * Note that the {@code matchers} collection is cloned to protect 335 * against subsequent modification. 336 * 337 * @param matchers 338 * the collection of {@link SNIMatcher}s (or null) 339 * 340 * @throws NullPointerException if the {@code matchers} 341 * contains {@code null} element 342 * @throws IllegalArgumentException if the {@code matchers} 343 * contains more than one name of the same name type 344 * 345 * @see Collection 346 * @see SNIMatcher 347 * @see #getSNIMatchers() 348 * 349 */ 350 // void setSNIMatchers(Collection!SNIMatcher matchers) { 351 // if (matchers !is null) { 352 // if (!matchers.isEmpty()) { 353 // sniMatchers = new HashMap!(int, SNIMatcher)(matchers.size()); 354 // foreach (SNIMatcher matcher ; matchers) { 355 // if (sniMatchers.put(matcher.getType(), 356 // matcher) !is null) { 357 // throw new IllegalArgumentException( 358 // "Duplicated server name of type " ~ 359 // matcher.getType().to!string()); 360 // } 361 // } 362 // } else { 363 // sniMatchers = Collections.emptyMap!(int, SNIMatcher)(); 364 // } 365 // } else { 366 // sniMatchers = null; 367 // } 368 // } 369 370 /** 371 * Returns a {@link Collection} containing all {@link SNIMatcher}s of the 372 * Server Name Indication (SNI) parameter, or null if none has been set. 373 * <P> 374 * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s 375 * operating in server mode. 376 * <P> 377 * For better interoperability, providers generally will not define 378 * default matchers so that by default servers will ignore the SNI 379 * extension and continue the handshake. 380 * 381 * @return null or an immutable collection of non-null {@link SNIMatcher}s 382 * 383 * @see SNIMatcher 384 * @see #setSNIMatchers(Collection) 385 * 386 */ 387 // Collection!SNIMatcher getSNIMatchers() { 388 // if (sniMatchers !is null) { 389 // if (!sniMatchers.isEmpty()) { 390 // return new ArrayList!(SNIMatcher)(sniMatchers.values()); 391 // } else { 392 // return new EmptyList!(SNIMatcher)(); 393 // } 394 // } 395 396 // return null; 397 // } 398 399 /** 400 * Sets whether the local cipher suites preference should be honored. 401 * 402 * @param honorOrder whether local cipher suites order in 403 * {@code #getCipherSuites} should be honored during 404 * SSL/TLS handshaking. 405 * 406 * @see #getUseCipherSuitesOrder() 407 * 408 */ 409 void setUseCipherSuitesOrder(bool honorOrder) { 410 this.preferLocalCipherSuites = honorOrder; 411 } 412 413 /** 414 * Returns whether the local cipher suites preference should be honored. 415 * 416 * @return whether local cipher suites order in {@code #getCipherSuites} 417 * should be honored during SSL/TLS handshaking. 418 * 419 * @see #setUseCipherSuitesOrder(bool) 420 * 421 */ 422 bool getUseCipherSuitesOrder() { 423 return preferLocalCipherSuites; 424 } 425 }