| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of dart.io; |
| |
| /// The object containing the certificates to trust when making |
| /// a secure client connection, and the certificate chain and |
| /// private key to serve from a secure server. |
| /// |
| /// The [SecureSocket] and [SecureServerSocket] classes take a SecurityContext |
| /// as an argument to their connect and bind methods. |
| /// |
| /// Certificates and keys can be added to a SecurityContext from either PEM |
| /// or PKCS12 containers. |
| /// |
| /// iOS note: Some methods to add, remove, and inspect certificates are not yet |
| /// implemented. However, the platform's built-in trusted certificates can |
| /// be used, by way of [SecurityContext.defaultContext]. |
| abstract class SecurityContext { |
| /// Creates a new [SecurityContext]. |
| /// |
| /// By default, the created [SecurityContext] contains no keys or certificates. |
| /// These can be added by calling the methods of this class. |
| /// |
| /// If [withTrustedRoots] is passed as `true`, the [SecurityContext] will be |
| /// seeded by the trusted root certificates provided as explained below. To |
| /// obtain a [SecurityContext] containing trusted root certificates, |
| /// [SecurityContext.defaultContext] is usually sufficient, and should |
| /// be used instead. However, if the [SecurityContext] containing the trusted |
| /// root certificates must be modified per-connection, then [withTrustedRoots] |
| /// should be used. |
| external factory SecurityContext({bool withTrustedRoots = false}); |
| |
| /// The default security context used by most operation requiring one. |
| /// |
| /// Secure networking classes with an optional `context` parameter |
| /// use the [defaultContext] object if the parameter is omitted. |
| /// This object can also be accessed, and modified, directly. |
| /// Each isolate has a different [defaultContext] object. |
| /// The [defaultContext] object uses a list of well-known trusted |
| /// certificate authorities as its trusted roots. On Linux and Windows, this |
| /// list is taken from Mozilla, who maintains it as part of Firefox. On, |
| /// MacOS, iOS, and Android, this list comes from the trusted certificates |
| /// stores built in to the platforms. |
| external static SecurityContext get defaultContext; |
| |
| /// Sets the private key for a server certificate or client certificate. |
| /// |
| /// A secure connection using this SecurityContext will use this key with |
| /// the server or client certificate to sign and decrypt messages. |
| /// [file] is the path to a PEM or PKCS12 file containing an encrypted |
| /// private key, encrypted with [password]. Assuming it is well-formatted, all |
| /// other contents of [file] are ignored. An unencrypted file can be used, |
| /// but this is not usual. |
| /// |
| /// NB: This function calls [File.readAsBytesSync], and will block on file IO. |
| /// Prefer using [usePrivateKeyBytes]. |
| /// |
| /// iOS note: Only PKCS12 data is supported. It should contain both the private |
| /// key and the certificate chain. On iOS one call to [usePrivateKey] with this |
| /// data is used instead of two calls to [useCertificateChain] and |
| /// [usePrivateKey]. |
| void usePrivateKey(String file, {String? password}); |
| |
| /// Sets the private key for a server certificate or client certificate. |
| /// |
| /// Like [usePrivateKey], but takes the contents of the file as a list |
| /// of bytes. |
| void usePrivateKeyBytes(List<int> keyBytes, {String? password}); |
| |
| /// Add a certificate to the set of trusted X509 certificates |
| /// used by [SecureSocket] client connections. |
| /// |
| /// [file] is the path to a PEM or PKCS12 file containing X509 certificates, |
| /// usually root certificates from certificate authorities. For PKCS12 files, |
| /// [password] is the password for the file. For PEM files, [password] is |
| /// ignored. Assuming it is well-formatted, all other contents of [file] are |
| /// ignored. |
| /// |
| /// NB: This function calls [File.readAsBytesSync], and will block on file IO. |
| /// Prefer using [setTrustedCertificatesBytes]. |
| /// |
| /// iOS note: On iOS, this call takes only the bytes for a single DER |
| /// encoded X509 certificate. It may be called multiple times to add |
| /// multiple trusted certificates to the context. A DER encoded certificate |
| /// can be obtained from a PEM encoded certificate by using the openssl tool: |
| /// ```bash |
| /// $ openssl x509 -outform der -in cert.pem -out cert.der |
| /// ``` |
| void setTrustedCertificates(String file, {String? password}); |
| |
| /// Add a certificate to the set of trusted X509 certificates |
| /// used by [SecureSocket] client connections. |
| /// |
| /// Like [setTrustedCertificates] but takes the contents of the file. |
| void setTrustedCertificatesBytes(List<int> certBytes, {String? password}); |
| |
| /// Sets the chain of X509 certificates served by [SecureServerSocket] |
| /// when making secure connections, including the server certificate. |
| /// |
| /// [file] is a PEM or PKCS12 file containing X509 certificates, starting with |
| /// the root authority and intermediate authorities forming the signed |
| /// chain to the server certificate, and ending with the server certificate. |
| /// The private key for the server certificate is set by [usePrivateKey]. For |
| /// PKCS12 files, [password] is the password for the file. For PEM files, |
| /// [password] is ignored. Assuming it is well-formatted, all |
| /// other contents of [file] are ignored. |
| /// |
| /// NB: This function calls [File.readAsBytesSync], and will block on file IO. |
| /// Prefer using [useCertificateChainBytes]. |
| /// |
| /// iOS note: As noted above, [usePrivateKey] does the job of both |
| /// that call and this one. On iOS, this call is a no-op. |
| void useCertificateChain(String file, {String? password}); |
| |
| /// Sets the chain of X509 certificates served by [SecureServerSocket] |
| /// when making secure connections, including the server certificate. |
| /// |
| /// Like [useCertificateChain] but takes the contents of the file. |
| void useCertificateChainBytes(List<int> chainBytes, {String? password}); |
| |
| /// Sets the list of authority names that a [SecureServerSocket] will advertise |
| /// as accepted when requesting a client certificate from a connecting |
| /// client. |
| /// |
| /// The [file] is a PEM or PKCS12 file containing the accepted signing |
| /// authority certificates - the authority names are extracted from the |
| /// certificates. For PKCS12 files, [password] is the password for the file. |
| /// For PEM files, [password] is ignored. Assuming it is well-formatted, all |
| /// other contents of [file] are ignored. |
| /// |
| /// NB: This function calls [File.readAsBytesSync], and will block on file IO. |
| /// Prefer using [setClientAuthoritiesBytes]. |
| /// |
| /// iOS note: This call is not supported. |
| void setClientAuthorities(String file, {String? password}); |
| |
| /// Sets the list of authority names that a [SecureServerSocket] will advertise |
| /// as accepted, when requesting a client certificate from a connecting |
| /// client. |
| /// |
| /// Like [setClientAuthorities] but takes the contents of the file. |
| void setClientAuthoritiesBytes(List<int> authCertBytes, {String? password}); |
| |
| /// Whether the platform supports ALPN. This always returns true and will be |
| /// removed in a future release. |
| @deprecated |
| external static bool get alpnSupported; |
| |
| /// Sets the list of application-level protocols supported by a client |
| /// connection or server connection. The ALPN (application level protocol |
| /// negotiation) extension to TLS allows a client to send a list of |
| /// protocols in the TLS client hello message, and the server to pick |
| /// one and send the selected one back in its server hello message. |
| /// |
| /// Separate lists of protocols can be sent for client connections and |
| /// for server connections, using the same SecurityContext. The [isServer] |
| /// boolean argument specifies whether to set the list for server connections |
| /// or client connections. |
| void setAlpnProtocols(List<String> protocols, bool isServer); |
| |
| /// Encodes a set of supported protocols for ALPN/NPN usage. |
| /// |
| /// The [protocols] list is expected to contain protocols in descending order |
| /// of preference. |
| /// |
| /// See RFC 7301 (https://tools.ietf.org/html/rfc7301) for the encoding of |
| /// `List<String> protocols`: |
| /// ```plaintext |
| /// opaque ProtocolName<1..2^8-1>; |
| /// |
| /// struct { |
| /// ProtocolName protocol_name_list<2..2^16-1> |
| /// } ProtocolNameList; |
| /// ``` |
| /// The encoding of the opaque `ProtocolName<lower..upper>` vector is |
| /// described in RFC 2246: 4.3 Vectors. |
| /// |
| /// Note: Even though this encoding scheme would allow a total |
| /// `ProtocolNameList` length of 65535, this limit cannot be reached. Testing |
| /// showed that more than ~ 2^14 bytes will fail to negotiate a protocol. |
| /// We will be conservative and support only messages up to (1<<13)-1 bytes. |
| static Uint8List _protocolsToLengthEncoding(List<String>? protocols) { |
| if (protocols == null || protocols.length == 0) { |
| return new Uint8List(0); |
| } |
| int protocolsLength = protocols.length; |
| |
| // Calculate the number of bytes we will need if it is ASCII. |
| int expectedLength = protocolsLength; |
| for (int i = 0; i < protocolsLength; i++) { |
| int length = protocols[i].length; |
| if (length > 0 && length <= 255) { |
| expectedLength += length; |
| } else { |
| throw new ArgumentError( |
| 'Length of protocol must be between 1 and 255 (was: $length).'); |
| } |
| } |
| |
| if (expectedLength >= (1 << 13)) { |
| throw new ArgumentError( |
| 'The maximum message length supported is 2^13-1.'); |
| } |
| |
| // Try encoding the `List<String> protocols` array using fast ASCII path. |
| var bytes = new Uint8List(expectedLength); |
| int bytesOffset = 0; |
| for (int i = 0; i < protocolsLength; i++) { |
| String proto = protocols[i]; |
| |
| // Add length byte. |
| bytes[bytesOffset++] = proto.length; |
| int bits = 0; |
| |
| // Add protocol bytes. |
| for (int j = 0; j < proto.length; j++) { |
| var char = proto.codeUnitAt(j); |
| bits |= char; |
| bytes[bytesOffset++] = char & 0xff; |
| } |
| |
| // Go slow case if we have encountered anything non-ascii. |
| if (bits > 0x7f) { |
| return _protocolsToLengthEncodingNonAsciiBailout(protocols); |
| } |
| } |
| return bytes; |
| } |
| |
| static Uint8List _protocolsToLengthEncodingNonAsciiBailout( |
| List<String> protocols) { |
| void addProtocol(List<int> outBytes, String protocol) { |
| var protocolBytes = utf8.encode(protocol); |
| var len = protocolBytes.length; |
| |
| if (len > 255) { |
| throw new ArgumentError( |
| 'Length of protocol must be between 1 and 255 (was: $len)'); |
| } |
| // Add length byte. |
| outBytes.add(len); |
| |
| // Add protocol bytes. |
| outBytes.addAll(protocolBytes); |
| } |
| |
| List<int> bytes = []; |
| for (var i = 0; i < protocols.length; i++) { |
| addProtocol(bytes, protocols[i]); |
| } |
| |
| if (bytes.length >= (1 << 13)) { |
| throw new ArgumentError( |
| 'The maximum message length supported is 2^13-1.'); |
| } |
| |
| return new Uint8List.fromList(bytes); |
| } |
| } |