├── .github └── workflows │ ├── build.yml │ └── release.yml ├── CHANGELOG ├── Dockerfile ├── INSTALL ├── LICENSE.txt ├── Makefile.am ├── README.md ├── _config.yml ├── bootstrap-arm64.sh ├── bootstrap.sh ├── build-arm64.sh ├── build-x86-64.sh ├── ca-bundle.crt ├── cert-parser.c ├── common.c ├── configure.ac ├── gnutls13.c ├── include ├── cert-parser.h ├── common.h └── proto-adapters.h ├── main.c ├── man └── tls-scan.1 ├── proto-adapters.c └── tests ├── README.md ├── dsa-scan.out ├── dsa-test.crt ├── dsa-test.key ├── ecdsa-scan.out ├── ecdsa-test.crt ├── ecdsa-test.key ├── rsa-scan.out ├── rsa-test.crt └── rsa-test.key /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | os: 14 | - ubuntu-20.04 15 | - macos-latest 16 | runs-on: ${{ matrix.os }} 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Setup build dependencies 20 | if: matrix.os == 'ubuntu-20.04' 21 | run: sudo apt install -y autoconf automake libtool pkg-config gcc unzip 22 | - name: Setup MacOS build dependencies 23 | if: matrix.os == 'macos-latest' 24 | run: brew install autoconf automake libtool 25 | - name: build-x86-64 26 | run: ./build-x86-64.sh 27 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | build-release: 10 | strategy: 11 | matrix: 12 | os: 13 | - ubuntu-20.04 14 | - macos-latest 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | fetch-depth: 0 20 | 21 | - name: Setup build dependencies 22 | if: matrix.os == 'ubuntu-20.04' 23 | run: | 24 | echo "TLSSCAN_OS=linux" >> $GITHUB_ENV 25 | sudo apt install -y autoconf automake libtool pkg-config gcc unzip 26 | 27 | - name: Setup MacOS build dependencies 28 | if: matrix.os == 'macos-latest' 29 | run: | 30 | echo "TLSSCAN_OS=darwin" >> $GITHUB_ENV 31 | brew install autoconf automake libtool 32 | 33 | - name: build-x86-64 34 | run: | 35 | ./build-x86-64.sh 36 | grep Version -m 1 $GITHUB_WORKSPACE/CHANGELOG | awk '{printf "TLSSCAN_VERSION=%s", $2}' >> $GITHUB_ENV 37 | 38 | - name: build-package 39 | run: | 40 | rm $GITHUB_WORKSPACE/tls-scan 41 | mkdir -p $GITHUB_WORKSPACE/tls-scan/man/man1 42 | cp $GITHUB_WORKSPACE/build-root/bin/tls-scan $GITHUB_WORKSPACE/tls-scan/tls-scan 43 | cp $GITHUB_WORKSPACE/build-root/share/man/man1/tls-scan.1 $GITHUB_WORKSPACE/tls-scan/man/man1/tls-scan.1 44 | cp $GITHUB_WORKSPACE/build-root/etc/tls-scan/ca-bundle.crt $GITHUB_WORKSPACE/tls-scan/ca-bundle.crt 45 | tar -zcvf tls-scan-${{env.TLSSCAN_VERSION}}-${{env.TLSSCAN_OS}}-amd64.tar.gz ./tls-scan/ 46 | 47 | - name: GH Release 48 | uses: softprops/action-gh-release@v0.1.14 49 | if: startsWith(github.ref, 'refs/tags/') 50 | with: 51 | files: | 52 | tls-scan-${{env.TLSSCAN_VERSION}}-${{env.TLSSCAN_OS}}-amd64.tar.gz 53 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | tls-scan -- History of changes. 2 | Bug numbers referenced in this log correspond to bug numbers at our issue tracker, 3 | 4 | Version 1.6.0 (2023-09-25) 5 | +----------------------------------- 6 | * New feature: ALPN protocol id enumeration. A new field `alpn` 7 | is added to the JSON output to indicate ALPN protocol id 8 | selected by the server. Besides alpn, `sni` is also added 9 | as a new field to indicate SNI value set by the client. 10 | 11 | Version 1.5.2 (2023-09-24) 12 | +----------------------------------- 13 | * Build bug fix: https://github.com/prbinu/tls-scan/issues/59 14 | * Updated gzip and libevent package versions 15 | * Add build-from-source support for Linux ARM architecture 16 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.5.2 17 | 18 | Version 1.5.1 (2023-04-23) 19 | +----------------------------------- 20 | * Critial bug fix: https://github.com/prbinu/tls-scan/issues/54 21 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.5.1 22 | 23 | Version 1.5.0 (2023-01-04) 24 | ----------------------------------- 25 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.5.0 26 | 27 | * Moved from Ubuntu 18.04 to 20.04 (https://github.com/prbinu/tls-scan/pull/48) 28 | * Updated zlib version to zlib-1.2.13 (https://github.com/prbinu/tls-scan/pull/50) 29 | 30 | Version 1.4.8 (2022-01-08) 31 | ----------------------------------- 32 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.4.8 33 | 34 | * Fixed duplicate field (ocspStapled) in the json output 35 | * Migrated build from Travis to GitHub Action/Build system 36 | 37 | Version 1.4.7 (2022-01-08) 38 | ----------------------------------- 39 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.4.7_2 40 | 41 | * Malformed json output for some domains (#43) 42 | 43 | Version 1.4.6 (2020-09-16) 44 | ----------------------------------- 45 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.4.6 46 | 47 | * Fix JSON outout encode bug - escape all control chars (rfc4627) (#38) 48 | 49 | Version 1.4.5 (2020-09-09) 50 | Version 1.4.4 (2020-09-08) 51 | ----------------------------------- 52 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.4.5 53 | 54 | * Fix JSON outout encode bug (#38) 55 | 56 | Version 1.4.3 (2020-06-09) 57 | ----------------------------------- 58 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.4.3 59 | 60 | * Fix the TLS version enum bug 61 | * Support TLSv1.2 CHACHA cipher scans 62 | 63 | Version 1.4.2 (2020-05-17) 64 | ----------------------------------- 65 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.4.2 66 | 67 | * Add run-time stats for tracking progress and performance 68 | New command-line param: `--stats-outfile` 69 | 70 | Version 1.4.1 (2020-01-05) 71 | ----------------------------------- 72 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.4.1 73 | 74 | * Build - moving closer to autotools style builds 75 | 76 | Version 1.4.0 (2019-12-30) 77 | ----------------------------------- 78 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.4.0 79 | 80 | * Add StartTLS RDP protocol support 81 | 82 | * Bug fix - starttls error stats counter 83 | 84 | * Add version info to tls-scan 85 | 86 | Version 1.3.0 (2019-12-28) 87 | ----------------------------------- 88 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.3.0 89 | 90 | * Add StartTLS protocol support: IMAP, POP3, FTPS, SIEVE, NNTP, XMPP, LDAP, POSTGRES 91 | 92 | * New test/ directory with scan output for reference 93 | 94 | Version 1.2.0 (2019-12-07) 95 | ----------------------------------- 96 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.2.0 97 | 98 | * Extend async/non-blocking support to TLS 1.3 version/cipher enum scans 99 | 100 | * TLS 1.3 scan support for STARTTLS protocols 101 | 102 | Version 1.0.1 (2019-12-01) 103 | ----------------------------------- 104 | * Tag: https://github.com/prbinu/tls-scan/releases/tag/1.0.1 105 | 106 | * Added TLS 1.3 scan support 107 | 108 | * GnuTLS library for 1.3+ scanning - to support both old and new ciphers, we 109 | link both GnuTLS and Old OpenSSL version (statically linked). 110 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 AS builder 2 | ARG DEBIAN_FRONTEND=noninteractive 3 | ENV TZ=Etc/UTC 4 | RUN set -xeu; \ 5 | apt-get update; \ 6 | apt-get install -y \ 7 | build-essential \ 8 | autoconf \ 9 | automake \ 10 | pkg-config \ 11 | curl \ 12 | zip \ 13 | libtool 14 | 15 | COPY . /usr/local/src/tls-scan 16 | RUN set -xeu; \ 17 | cd /usr/local/src/tls-scan; \ 18 | ./build-x86-64.sh 19 | 20 | 21 | FROM ubuntu:20.04 22 | 23 | RUN useradd -rU tls-scan 24 | USER tls-scan 25 | 26 | WORKDIR /usr/local/share/tls-scan/ 27 | COPY --from=builder /usr/local/src/tls-scan/build-root/bin/tls-scan /usr/local/bin/tls-scan 28 | ADD --chown=tls-scan:tls-scan https://curl.haxx.se/ca/cacert.pem ./ca-bundle.crt 29 | 30 | ENTRYPOINT ["tls-scan"] 31 | CMD ["--help"] 32 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | TLS-SCAN INSTALLATION 2 | --------------------- 3 | 4 | This document describes installation on Linux and Mac OS/X (Darwin) 5 | operating systems. 6 | 7 | Pre-requisites 8 | -------------- 9 | 10 | To build and install 'tls-scan', you will need: 11 | 12 | autoconf 13 | automake 14 | libtool 15 | pkg-config 16 | gcc 17 | git 18 | 19 | Build (direct) dependencies: 20 | 21 | libevent 2x 22 | openssl 1.0.2 23 | gnutls 24 | 25 | The above dependent packages also pulls few other packages as well. 26 | To reduce the complexy of building dependent pckages, we have created 27 | a script 'bootstrap.sh'. This script download and build all tls-scan 28 | dependent packages and finally executes 'autoreconf'. 29 | 30 | Build and install from source 31 | ----------------------------- 32 | 33 | On Linux and Mac OS/X: 34 | 35 | $ ./bootstrap.sh 36 | $ ./configure 37 | $ make 38 | $ make install 39 | 40 | The 'build-x86-64.sh' is a utility script that wraps the above commands. 41 | 42 | 43 | Installation from binary 44 | ------------------------ 45 | 46 | Steps: 47 | 1. Download the latest tarball: 48 | https://github.com/prbinu/tls-scan/releases/latest 49 | 2. Un-tar the file creates a tls-scan directory locally that contains 50 | tls-scan binary, ca-bundle.crt and the man page 51 | 52 | 53 | Source distribution 54 | ------------------- 55 | 56 | The following command will create a source distribution. 57 | 58 | $ make dist 59 | 60 | Note: While building from a source tarball, make sure ./bootstrap.sh 61 | is executed prior to ./configure. 62 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016, Yahoo Inc. 2 | 3 | Licensed under the terms of the New BSD license. See below for terms. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = tls-scan 2 | tls_scan_SOURCES = main.c common.c cert-parser.c gnutls13.c proto-adapters.c 3 | 4 | TS_VERSION = $(shell grep -m 1 Version $(top_srcdir)/CHANGELOG | awk '{print $$2}') 5 | TS_BUILD_DATE = $(shell date '+%Y-%m-%d') 6 | TS_OS = $(shell uname -s) 7 | TS_ARCH = $(shell uname -p) 8 | 9 | AM_CPPFLAGS = -I$(top_srcdir)/include -I build-root/include -I build-root/include -Wall -Wundef -Wshadow -Wunreachable-code -Wswitch-default -Wcast-align -pedantic -g -std=c99 -D_GNU_SOURCE -DTS_VERSION=\"$(TS_VERSION)\" -DTS_BUILD_DATE=\"$(TS_BUILD_DATE)\" -DTS_OS=\"$(TS_OS)\" -DTS_ARCH=\"$(TS_ARCH)\" 10 | 11 | if LINUX_AMD64 12 | LDADD = build-root/lib/libssl.a build-root/lib/libcrypto.a build-root/lib/libevent.a build-root/lib/libevent_openssl.a build-root/lib/libgnutls.a build-root/lib64/libhogweed.a build-root/lib64/libnettle.a build-root/lib/libz.a -ldl -lrt 13 | endif 14 | 15 | if LINUX_ARM64 16 | LDADD = build-root/lib/libssl.a build-root/lib/libcrypto.a build-root/lib/libevent.a build-root/lib/libevent_openssl.a build-root/lib/libgnutls.a build-root/lib/libhogweed.a build-root/lib/libnettle.a build-root/lib/libz.a -ldl -lrt 17 | endif 18 | 19 | if DARWIN_AMD64 20 | LDADD = build-root/lib/libssl.a build-root/lib/libcrypto.a build-root/lib/libevent.a build-root/lib/libevent_openssl.a build-root/lib/libgnutls.a build-root/lib/libhogweed.a build-root/lib/libnettle.a -ldl 21 | AM_LDFLAGS = -lz -framework Security -framework CoreFoundation 22 | endif 23 | 24 | myconfdir = $(sysconfdir)/tls-scan 25 | myconf_DATA = ca-bundle.crt 26 | man1_MANS = man/tls-scan.1 27 | EXTRA_DIST = build-x86-64.sh 28 | 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Build Status](https://github.com/prbinu/tls-scan/actions/workflows/build.yml/badge.svg) 2 | ![Release Status](https://github.com/prbinu/tls-scan/actions/workflows/release.yml/badge.svg) 3 | 4 | # tls-scan 5 | 6 | A program to scan TLS based servers and collect X.509 certificates, ciphers and related information. It produces results in JSON format. `tls-scan` is a single threaded asynchronous/event-based program (powered by libevent) capable of concurrently scan thousands of TLS servers. It can be combined with other tools such as GNU parallel to vertically scale in multi-core machines. 7 | 8 | `tls-scan` helps developers and security engineers to track/test/debug certificates and TLS configurations of servers within their organization. 9 | 10 | ## Features 11 | 12 | * Support for TLSv1.3 13 | * TLS and StartTLS protocol support: SMTP, IMAP, POP3, FTPS, SIEVE, NNTP, XMPP, LDAP, RDP, POSTGRES, MYSQL 14 | * Blazing fast - Can operate at scale with the ability to concurrently scan large number of servers (say scan IoT devices at scale) 15 | * Detect SSLv2, SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3 versions and ciphers 16 | * ALPN protocol id enumeration 17 | * Cipher and TLS version enumeration 18 | * Extract X.509 certificate fields from the target server and print it in JSON format 19 | * Certificate and host name verification checks 20 | * TLS compression checks 21 | * Session reuse tests 22 | * Certificate revocation checks with stapled OCSP response 23 | * Script friendly output - Can be combined with other tools to analyze the scan results 24 | * Detailed run time stats for tracking progress and performance/charts 25 | 26 | This tool is primarily for collecting TLS cipher and X.509 certificate data. The scan output can be easily combined with related tools to identify TLS misconfigurations. 27 | 28 | ## Installation 29 | 30 | You may either use pre-built binary package or build from the source. 31 | 32 | ### Pre-built Binary (x86_64) 33 | 34 | Linux and OSX: [https://github.com/prbinu/tls-scan/releases/latest](https://github.com/prbinu/tls-scan/releases/latest) 35 | 36 | ### Build From Source 37 | 38 | All you need is [`build-x86-64.sh`](https://github.com/prbinu/tls-scan/blob/master/build-x86-64.sh) (or `build-arm64.sh` for Linux Arm arch). This script pulls dependent packages - PeterMosmans [`openssl`](https://github.com/PeterMosmans/openssl), [`libevent`](https://github.com/libevent/libevent) and [GnuTLS](https://gitlab.com/gnutls/gnutls/), and build those from the scratch. Since the openssl we use is different from stock openssl, it is linked statically to tls-scan program. The build can take approximately twenty minutes to complete. 39 | 40 | *Build Pre-requisites* : 41 | 42 | * [autoconf](https://ftpmirror.gnu.org/autoconf) 43 | * [automake](https://ftpmirror.gnu.org/automake) 44 | * [libtool](http://ftpmirror.gnu.org/libtool) 45 | * [pkg-config](https://pkg-config.freedesktop.org/releases/?C=M;O=D) 46 | * [gcc](http://railsapps.github.io/xcode-command-line-tools.html) 47 | * make 48 | 49 | On Ubuntu: 50 | 51 | ```sh 52 | sudo apt-get update 53 | sudo apt-get install make autoconf automake libtool pkg-config gcc unzip -y 54 | ``` 55 | 56 | ### Linux 57 | 58 | ```sh 59 | git clone https://github.com/prbinu/tls-scan.git 60 | cd tls-scan 61 | ``` 62 | 63 | *x84_64* 64 | ```sh 65 | ./build-x86-64.sh 66 | ``` 67 | The newly built tls-scan binary can be found at `./build-root/bin`. build-x86-64.sh is a wrapper script that calls `./bootstrap.sh` to build all dependent packages. bootstrap.sh also executes the `autoreconf -i` command to generate `configure` file. Subsequently it calles the standard `./configure`, `make && make install`. 68 | 69 | *arm64* 70 | ```sh 71 | ./build-arm64.sh 72 | ``` 73 | 74 | *Test* : 75 | 76 | ```sh 77 | cd build-root/bin 78 | ./tls-scan --connect=yahoo.com --cacert=../etc/tls-scan/ca-bundle.crt --pretty 79 | ``` 80 | 81 | ### OSX 82 | If you do not have the pre-requisite packages, you can easily install those packages by following the links below: 83 | 84 | * [xcode-command-line-tools](http://railsapps.github.io/xcode-command-line-tools.html) 85 | * [how-to-install-autoconf-automake-and-related-tools-on-mac-os-x-from-source](http://superuser.com/questions/383580/how-to-install-autoconf-automake-and-related-tools-on-mac-os-x-from-source) 86 | 87 | ```sh 88 | git clone https://github.com/prbinu/tls-scan.git 89 | cd tls-scan 90 | ./build-x86-64.sh 91 | ``` 92 | 93 | The tls-scan binary can be found at `./build-root/bin`. Another (easy) option is to use our Docker image to build and run `tls-scan` on OSX. 94 | 95 | **Running `tls-scan` on Mac Apple Silicon (Arm/M1/M2)**: 96 | 97 | Currently no native build support, however you may run `tls-scan` binary using [Rosetta2](https://support.apple.com/en-us/HT211861) 98 | 99 | ### Docker 100 | 101 | *Pre-requisite* : [Docker](https://docs.docker.com/engine/installation/) 102 | 103 | *Build* : 104 | Copy the [Dockerfile](https://github.com/prbinu/tls-scan/blob/master/Dockerfile) to your machine, and run it: 105 | 106 | ```sh 107 | docker build -t tls-scan . 108 | ``` 109 | 110 | *Test* : 111 | 112 | ```sh 113 | docker run --rm tls-scan --connect=example.com:443 --all --pretty 114 | ``` 115 | 116 | ## Example 117 | 118 | ```sh 119 | ./tls-scan -c search.yahoo.com --all --pretty 120 | ``` 121 | 122 | ```json 123 | { 124 | "host": "search.yahoo.com", 125 | "ip": "208.71.45.12", 126 | "port": 443, 127 | "elapsedTime": 1600, 128 | "tlsVersion": "TLSv1.2", 129 | "cipher": "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD", 130 | "tempPublicKeyAlg": "ECDH prime256v1", 131 | "tempPublicKeySize": 256, 132 | "secureRenego": true, 133 | "compression": "NONE", 134 | "expansion": "NONE", 135 | "sessionLifetimeHint": 100800, 136 | "tlsVersions": [ 137 | "TLSv1", 138 | "TLSv1_1", 139 | "TLSv1_2" 140 | ], 141 | "x509ChainDepth": 2, 142 | "verifyCertResult": true, 143 | "verifyHostResult": true, 144 | "ocspStapled": true, 145 | "verifyOcspResult": true, 146 | "certificateChain": [ 147 | { 148 | "version": 3, 149 | "subject": "CN=*.search.yahoo.com; O=Yahoo! Inc.; L=Sunnyvale; ST=CA; C=US", 150 | "issuer": "CN=DigiCert SHA2 High Assurance Server CA; OU=www.digicert.com; O=DigiCert Inc; C=US", 151 | "subjectCN": "*.search.yahoo.com", 152 | "subjectAltName": "DNS:*.search.yahoo.com, DNS:search.yahoo.com, DNS:search.yahoo.net, ...", 153 | "signatureAlg": "sha256WithRSAEncryption", 154 | "notBefore": "Dec 9 00:00:00 2016 GMT", 155 | "notAfter": "Apr 30 12:00:00 2017 GMT", 156 | "expired": false, 157 | "serialNo": "0F:45:73:E3:F5:7A:7D:5B:43:57:64:2A:6C:46:F2:1C", 158 | "keyUsage": "Digital Signature, Key Encipherment critical", 159 | "extKeyUsage": "TLS Web Server Authentication, TLS Web Client Authentication", 160 | "publicKeyAlg": "RSA", 161 | "publicKeySize": 2048, 162 | "basicConstraints": "CA:FALSE critical", 163 | "subjectKeyIdentifier": "63:0F:82:DB:F9:B0:64:78:90:C9:16:69:95:84:24:F1:4B:04:6F:E4", 164 | "sha1Fingerprint": "F7:35:E5:C9:A3:60:62:07:CB:55:74:7E:0F:09:AD:2A:F3:F3:53:F3" 165 | }, { 166 | "version": 3, 167 | "subject": "CN=DigiCert SHA2 High Assurance Server CA; OU=www.digicert.com; O=DigiCert Inc; C=US", 168 | "issuer": "CN=DigiCert High Assurance EV Root CA; OU=www.digicert.com; O=DigiCert Inc; C=US", 169 | "subjectCN": "DigiCert SHA2 High Assurance Server CA", 170 | "signatureAlg": "sha256WithRSAEncryption", 171 | "notBefore": "Oct 22 12:00:00 2013 GMT", 172 | "notAfter": "Oct 22 12:00:00 2028 GMT", 173 | "expired": false, 174 | "serialNo": "04:E1:E7:A4:DC:5C:F2:F3:6D:C0:2B:42:B8:5D:15:9F", 175 | "keyUsage": "Digital Signature, Certificate Sign, CRL Sign critical", 176 | "extKeyUsage": "TLS Web Server Authentication, TLS Web Client Authentication", 177 | "publicKeyAlg": "RSA", 178 | "publicKeySize": 2048, 179 | "basicConstraints": "CA:TRUE, pathlen:0 critical", 180 | "subjectKeyIdentifier": "51:68:FF:90:AF:02:07:75:3C:CC:D9:65:64:62:A2:12:B8:59:72:3B", 181 | "sha1Fingerprint": "A0:31:C4:67:82:E6:E6:C6:62:C2:C8:7C:76:DA:9A:A6:2C:CA:BD:8E" 182 | } ] 183 | } 184 | 185 | ``` 186 | 187 | | Useful Tip: Use `--concurrency=` option if you want to scan multiple target servers in parallel. | 188 | |----------------| 189 | 190 | ## Usage 191 | 192 | The scan output can be shoved into tools like [Splunk](http://www.splunk.com/) or [ELK](http://elastic.co/) for analysis. 193 | 194 | ### Command-line Query & Filter 195 | 196 | By passing `tls-scan` output to JSON command-line parser like [`jq`](https://stedolan.github.io/jq), you can do realtime filtering on the scan results. 197 | 198 | **Examples**: 199 | 200 | *Command to filter out hosts that passed certificate and host name verifications*: 201 | 202 | ```sh 203 | cat input.txt | tls-scan --port=443 2>/dev/null | \ 204 | jq-linux64 -r 'select(.verifyHostResult == true and .verifyCertResult == true) | [.host, .ip, .verifyHost, .verifyCert] | @tsv' 205 | 206 | ``` 207 | 208 | *Command to find hosts with expired certificates* : 209 | 210 | ```sh 211 | cat input.txt | tls-scan --port=443 --concurrency=500 --timeout=5 2>/dev/null | \ 212 | jq-linux64 -r 'select(.certificateChain[].expired == true) | [.host, .ip] | @tsv' 213 | 214 | ``` 215 | 216 | *Command to find weak RSA keys* : 217 | 218 | ```sh 219 | cat tlscerts.out | \ 220 | jq-linux64 -r 'select(.certificateChain[0].publicKeyAlg == "RSA" and .certificateChain[0].publicKeySize < 2048) | [.host, .ip]' 221 | 222 | ``` 223 | 224 | *Command to find hosts that support SSLv2 or SSLv3* : 225 | 226 | ```sh 227 | tls-scan --infile=domains.txt --port=443 --version-enum --concurrency=250 --timeout=3 2>/dev/null | \ 228 | jq-linux64 -r 'if (.tlsVersions[] | contains("SSL")) == true then [.host, .ip, .tlsVersions[]] else empty end | @tsv' 229 | 230 | ``` 231 | 232 | **NOTE**: Avoid frequent scan + filter; instead save the scan output to a file and use it to run queries. 233 | 234 | ## Help 235 | 236 | | Option | Description | 237 | |----------------|-------------| 238 | -H --help | Print a usage message briefly summarizing these command-line options and the bug-reporting address, then exit. 239 | -c --connect=\ | `target[:port]` to scan. target = {hostname, IPv4, [IPv6] }. IPv6 example: [::1]:443 (default port 443). 240 | --starttls=\ | Supported protocols: `smtp`, `imap`, `pop3`, `ftp`, `sieve`, `nntp`, `xmpp`, `ldap`, `rdp`, `postgres`, `mysql`, `tls` (default) 241 | -c --cacert=\ | Root CA file for certificate validation. By default the program attempts to load `ca-bundle.crt` file from current directory. 242 | -C --ciphers=\ | Ciphers to use; try `openssl ciphers` to see all ciphers. Note that this option will be overwritten by `--ssl2`, `--ssl3`, `--tls1`, `--tls1_1`, `--tls1_2` options, if provided. Example: `"ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384"` 243 | -e --cipher-enum | Enumerate supported ciphers. Currently use `--tls-old` ciphers. Try `--meta-info` to find predefined cipher suite options. 244 | --show-unsupported-ciphers | Include unsupported ciphers in the cipher list to JSON output. 245 | -V --version-enum | Enumerate supported TLS versions. 246 | -v --version | Print tls-scan version and build information. 247 | -r --session-reuse | Enable ssl session reuse. 248 | -u --session-print | Print SSL session in PEM format to stderr. This is currently not included in the JSON output, but print seperately. This flag woould be useful if you wanted to pass SSL session to `--session-file` to test session reuse. 249 | -T --session-file=\ | File that contains SSL session in PEM format. 250 | -a --all | Shortcut for `--version-enum`, `--cipher-enum` and `--session-reuse` options. This scan can take longer time to complete. Also note if the server employs some form of rate-limiting, your scan may fail. 251 | -s --sni=\ | Set TLS extension servername in `ClientHello`. Defaults to input hostname and applied to TLSv1+ only. 252 | -b --concurrency=\ | Number of concurrent requests. The default is 1. This option specify the number of worker objects. Concurrency should be set based on your system capacity (memory, cpu, network) etc. Default: 1. 253 | -t --timeout=\ | Timeout per connection (in seconds). Note that is is per connection and for cipher scans, `tls-scan` makes several connections to the same server. Default: 10. 254 | -S --sleep=\ | Add milliseconds delay between the connection. Only for `--cipher-enum` and `--version-enum` options. Useful to manage server rate-limits. The max sleep value is 60000 (1 minute). Default: 0. 255 | -f --infile=\ | Input file with domains or IPs. This is optional and by default the program accepts input from standard input (`stdin`). 256 | -o --outfile=\ | Output file where the result in JSON format is stored. The default is standard output (`stdout`). 257 | -n --pretty | Pretty print; add newline (`\n`) between record fields. 258 | -N --nameserver=\ | DNS resolver IPs to use and is an optional field. Multiple Namespace IP address can be passed. Format: `-N -N -N ..` In practice, DNS servers may have tight rate-limit in place. 259 | --ssl2 | Use only SSLv2 ciphers. 260 | --ssl3 | Use only SSLv3 ciphers. 261 | --tls1 | Use only TLSv1 ciphers. 262 | --tls1_1 | Use only TLSv1_1 ciphers. 263 | --tls1_2 | Use only TLSv1_2 ciphers. 264 | --tls-modern | Mozilla's modern cipher list. See: https://wiki.mozilla.org/Security/Server_Side_TLS. 265 | --tls-interm | Mozilla's intermediate cipher list. 266 | --tls-old | Mozilla's old (backward compatible cipher list). 267 | --no-parallel-enum |Disable parallel cipher and tls version enumeration. Parallel scan is performed only with '--connect' option. 268 | --meta-info | Print program meta information and exit. Useful if you wanted to see predefined cipher options. 269 | --stats-outfile=\ | Enable run-time scan stats and save it to a file 270 | 271 | ## Caveats 272 | 273 | * The following ciphers are currently disabled: ```SRP:PSK``` 274 | 275 | ## TLS 1.3 Support 276 | To support old, insecure cipher scans, we are using an old openssl version that doesn't have support for TLS 1.3. So to support TLS 1.3, we need a newer openssl version (v1.1.1+). Since linking two openssl libraries to the same process space doesn't work out of box (duplicate symbols), we chose to use [GnuTLS](https://gitlab.com/gnutls/gnutls/) library for TLS 1.3+ support. In short, openssl is used for scanning SSLv2, SSLv3, TLSv1, TLSv1.1 and TLSv1.2 and GnuTLS is used for TLSv1.3. 277 | 278 | ## Contributions 279 | Collaborators and pull requests are welcome! 280 | 281 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman 2 | show_downloads: true 3 | releases_url: https://github.com/prbinu/tls-scan/releases/latest 4 | gems: 5 | - jekyll-sitemap 6 | -------------------------------------------------------------------------------- /bootstrap-arm64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Environment variables 3 | # TS_BUILDROOT : Build root directory. Default to current working directory 4 | # TS_INSTALLDIR : Installation directory. Default to ${TS_BUILDROOT} 5 | # 6 | echo " " 7 | echo " IMPORTANT NOTICE" 8 | echo " Build Pre-requisites :" 9 | echo " " 10 | echo " gcc" 11 | echo " autoconf" 12 | echo " automake" 13 | echo " libtool" 14 | echo " pkg-config" 15 | echo "Make sure you have these packages installed before you proceed with tls-scan build" 16 | echo "continue in a moment ..." 17 | sleep 10 18 | 19 | set -e 20 | CD=`pwd` 21 | OS=`uname` 22 | 23 | if [ "${OS}" != "Linux" ]; then 24 | echo "Error: ${OS} is not a currently supported platform for ARM64 builds." 25 | exit 1 26 | fi 27 | 28 | [[ -z "${TS_BUILDROOT}" ]] && BUILDDIR="${CD}" || BUILDDIR="${TS_BUILDROOT}" 29 | 30 | echo ">>> Build DIR: ${BUILDDIR}" 31 | BUILDDIR=${BUILDDIR}/build-root 32 | 33 | # remove build dirs 34 | test -d ${BUILDDIR}/build && rm -rf ${BUILDDIR}/build/* 35 | 36 | 37 | test -z ${BUILDDIR} || /bin/mkdir -p ${BUILDDIR} 38 | test -z ${BUILDDIR}/downloads || /bin/mkdir -p ${BUILDDIR}/downloads 39 | test -z ${BUILDDIR}/build || /bin/mkdir -p ${BUILDDIR}/build 40 | 41 | [[ -z "${TS_INSTALLDIR}" ]] && OUTDIR="${BUILDDIR}" || OUTDIR="${TS_INSTALLDIR}" 42 | 43 | echo ">>> Install DIR: ${OUTDIR}" 44 | export PKG_CONFIG_PATH=${OUTDIR}/lib/pkgconfig 45 | 46 | OPENSSL_VERSION="1.0.2-chacha" 47 | LIBEVENT_VERSION="2.1.10-stable" 48 | ZLIB_VERSION="zlib-1.3" 49 | 50 | FILE="${BUILDDIR}/downloads/${OPENSSL_VERSION}.zip" 51 | if [ ! -f $FILE ]; then 52 | echo "Downloading $FILE.." 53 | cd ${BUILDDIR}/downloads 54 | curl -OL https://github.com/prbinu/PeterMosmans-openssl/archive/refs/heads/${OPENSSL_VERSION}.zip 55 | fi 56 | 57 | cd ${BUILDDIR}/build 58 | unzip ${BUILDDIR}/downloads/${OPENSSL_VERSION}.zip 59 | mv PeterMosmans-openssl-${OPENSSL_VERSION} openssl-arm64 60 | 61 | cd openssl-arm64 62 | 63 | cd ${BUILDDIR}/downloads 64 | curl -OL https://www.zlib.net/${ZLIB_VERSION}.tar.gz 65 | 66 | cd ${BUILDDIR}/build 67 | tar -zxvf ${BUILDDIR}/downloads/${ZLIB_VERSION}.tar.gz 68 | mv ${ZLIB_VERSION} zlib-arm64 69 | cd zlib-arm64 70 | 71 | ./configure --prefix=${OUTDIR} --static 72 | make 73 | make install 74 | 75 | echo ">>> ZLIB complete" 76 | cd ${BUILDDIR}/build/openssl-arm64 77 | ./config enable-static-engine enable-ec_nistp_64_gcc_128 enable-gost enable-idea enable-md2 enable-rc2 enable-rc5 enable-rfc3779 enable-ssl-trace enable-ssl2 enable-ssl3 enable-zlib experimental-jpake --prefix=${OUTDIR} --openssldir=${OUTDIR}/ssl -I${OUTDIR}/include -L${OUTDIR}/lib --with-zlib-lib=${OUTDIR}/lib --with-zlib-include=${OUTDIR}/include 78 | 79 | 80 | make 81 | make install prefix=${OUTDIR} 82 | 83 | FILE="${BUILDDIR}/downloads/libevent-${LIBEVENT_VERSION}.tar.gz" 84 | if [ ! -f $FILE ]; then 85 | echo "Downloading $FILE.." 86 | cd ${BUILDDIR}/downloads 87 | curl -OL https://github.com/libevent/libevent/releases/download/release-${LIBEVENT_VERSION}/libevent-${LIBEVENT_VERSION}.tar.gz 88 | fi 89 | 90 | cd ${BUILDDIR}/build 91 | tar -zxvf ${BUILDDIR}/downloads/libevent-${LIBEVENT_VERSION}.tar.gz 92 | mv libevent-${LIBEVENT_VERSION} libevent-arm64 93 | 94 | cd libevent-arm64 95 | ./autogen.sh 96 | 97 | ./configure --enable-shared=no OPENSSL_CFLAGS=-I${OUTDIR}/include OPENSSL_LIBS="-L${OUTDIR}/lib -lssl -L${OUTDIR}/lib -lcrypto" CFLAGS="-I${OUTDIR}/include" LIBS="-L${OUTDIR}/lib -ldl -lz" 98 | 99 | make 100 | make install prefix=${OUTDIR} 101 | 102 | echo "Downloading nettle_3.5.1." 103 | cd ${BUILDDIR}/downloads 104 | curl -OL https://ftp.gnu.org/gnu/nettle/nettle-3.5.1.tar.gz 105 | 106 | cd ${BUILDDIR}/build 107 | tar -zxvf ${BUILDDIR}/downloads/nettle-3.5.1.tar.gz 108 | cd nettle-3.5.1 109 | 110 | ./.bootstrap 111 | ./configure --enable-static --enable-mini-gmp --disable-openssl --disable-shared --disable-documentation LDFLAGS="-L${OUTDIR}/lib" 112 | make && make install prefix=${OUTDIR} 113 | 114 | echo "Downloading gnutls_3.6.10." 115 | cd ${BUILDDIR}/downloads 116 | curl -OL https://www.gnupg.org/ftp/gcrypt/gnutls/v3.6/gnutls-3.6.10.tar.xz 117 | 118 | cd ${BUILDDIR}/build 119 | tar -xvf ${BUILDDIR}/downloads/gnutls-3.6.10.tar.xz 120 | cd gnutls-3.6.10 121 | 122 | ./configure --enable-static --disable-openssl-compatibility --disable-libdane --without-p11-kit --without-tpm --without-idn --disable-tests --disable-doc --disable-full-test-suite --disable-libdane --disable-nls --enable-shared=no --with-included-libtasn1 --with-included-unistring --with-nettle-mini --enable-guile=no --prefix=$OUTDIR LDFLAGS="-L${OUTDIR}/lib" NETTLE_CFLAGS="-I${OUTDIR}/include" NETTLE_LIBS="-L${OUTDIR}/lib -lnettle" HOGWEED_CFLAGS="-I${OUTDIR}/include" HOGWEED_LIBS="-L${OUTDIR}/lib -lhogweed" LIBS="${OUTDIR}/lib/libhogweed.a ${OUTDIR}/lib/libnettle.a" 123 | 124 | make && make install prefix=${OUTDIR} 125 | 126 | echo ">>> Running autoreconf -i" 127 | cd ${CD} 128 | autoreconf -i 129 | 130 | echo ">>> Bootstrap complete" 131 | 132 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Environment variables 3 | # TS_BUILDROOT : Build root directory. Default to current working directory 4 | # TS_INSTALLDIR : Installation directory. Default to ${TS_BUILDROOT} 5 | # 6 | echo " " 7 | echo " IMPORTANT NOTICE" 8 | echo " Build Pre-requisites :" 9 | echo " " 10 | echo " gcc" 11 | echo " autoconf" 12 | echo " automake" 13 | echo " libtool" 14 | echo " pkg-config" 15 | echo "Make sure you have these packages installed before you proceed with tls-scan build" 16 | echo "continue in a moment ..." 17 | sleep 10 18 | 19 | set -e 20 | CD=`pwd` 21 | OS=`uname` 22 | 23 | if [ "${OS}" != "Darwin" ] && [ "${OS}" != "Linux" ]; then 24 | echo "Error: ${OS} is not a currently supported platform." 25 | exit 1 26 | fi 27 | 28 | [[ -z "${TS_BUILDROOT}" ]] && BUILDDIR="${CD}" || BUILDDIR="${TS_BUILDROOT}" 29 | 30 | echo ">>> Build DIR: ${BUILDDIR}" 31 | BUILDDIR=${BUILDDIR}/build-root 32 | 33 | # remove build dirs 34 | test -d ${BUILDDIR}/build && rm -rf ${BUILDDIR}/build/* 35 | 36 | 37 | test -z ${BUILDDIR} || /bin/mkdir -p ${BUILDDIR} 38 | test -z ${BUILDDIR}/downloads || /bin/mkdir -p ${BUILDDIR}/downloads 39 | test -z ${BUILDDIR}/build || /bin/mkdir -p ${BUILDDIR}/build 40 | 41 | [[ -z "${TS_INSTALLDIR}" ]] && OUTDIR="${BUILDDIR}" || OUTDIR="${TS_INSTALLDIR}" 42 | 43 | echo ">>> Install DIR: ${OUTDIR}" 44 | export PKG_CONFIG_PATH=${OUTDIR}/lib/pkgconfig 45 | 46 | OPENSSL_VERSION="1.0.2-chacha" 47 | LIBEVENT_VERSION="2.1.10-stable" 48 | ZLIB_VERSION="zlib-1.3.1" 49 | 50 | FILE="${BUILDDIR}/downloads/${OPENSSL_VERSION}.zip" 51 | if [ ! -f $FILE ]; then 52 | echo "Downloading $FILE.." 53 | cd ${BUILDDIR}/downloads 54 | curl -OL https://github.com/PeterMosmans/openssl/archive/${OPENSSL_VERSION}.zip 55 | fi 56 | 57 | cd ${BUILDDIR}/build 58 | unzip ${BUILDDIR}/downloads/${OPENSSL_VERSION}.zip 59 | mv openssl-${OPENSSL_VERSION} openssl-x86_64 60 | 61 | cd openssl-x86_64 62 | 63 | if [ "${OS}" == "Darwin" ]; then 64 | ./Configure darwin64-x86_64-cc enable-static-engine enable-ec_nistp_64_gcc_128 enable-gost enable-idea enable-md2 enable-rc2 enable-rc5 enable-rfc3779 enable-ssl-trace enable-ssl2 enable-ssl3 enable-zlib experimental-jpake --prefix=${OUTDIR} --openssldir=${OUTDIR}/ssl 65 | else 66 | cd ${BUILDDIR}/downloads 67 | curl -OL https://www.zlib.net/${ZLIB_VERSION}.tar.gz 68 | 69 | cd ${BUILDDIR}/build 70 | tar -zxvf ${BUILDDIR}/downloads/${ZLIB_VERSION}.tar.gz 71 | mv ${ZLIB_VERSION} zlib-x86_64 72 | cd zlib-x86_64 73 | 74 | ./configure --prefix=${OUTDIR} --static 75 | make 76 | make install 77 | 78 | echo ">>> ZLIB complete" 79 | cd ${BUILDDIR}/build/openssl-x86_64 80 | ./config enable-static-engine enable-ec_nistp_64_gcc_128 enable-gost enable-idea enable-md2 enable-rc2 enable-rc5 enable-rfc3779 enable-ssl-trace enable-ssl2 enable-ssl3 enable-zlib experimental-jpake --prefix=${OUTDIR} --openssldir=${OUTDIR}/ssl -I${OUTDIR}/include -L${OUTDIR}/lib --with-zlib-lib=${OUTDIR}/lib --with-zlib-include=${OUTDIR}/include 81 | fi 82 | 83 | make 84 | make install prefix=${OUTDIR} 85 | 86 | FILE="${BUILDDIR}/downloads/libevent-${LIBEVENT_VERSION}.tar.gz" 87 | if [ ! -f $FILE ]; then 88 | echo "Downloading $FILE.." 89 | cd ${BUILDDIR}/downloads 90 | curl -OL https://github.com/libevent/libevent/releases/download/release-${LIBEVENT_VERSION}/libevent-${LIBEVENT_VERSION}.tar.gz 91 | fi 92 | 93 | cd ${BUILDDIR}/build 94 | tar -zxvf ${BUILDDIR}/downloads/libevent-${LIBEVENT_VERSION}.tar.gz 95 | mv libevent-${LIBEVENT_VERSION} libevent-x86_64 96 | 97 | cd libevent-x86_64 98 | ./autogen.sh 99 | 100 | if [ "${OS}" == "Darwin" ]; then 101 | ./configure --enable-shared=no --enable-static CFLAGS="-I${OUTDIR}/include -arch x86_64" LIBS="-L${OUTDIR}/lib -lssl -L${OUTDIR}/lib -lcrypto -ldl -L${OUTDIR}/lib -lz" 102 | else 103 | ./configure --enable-shared=no OPENSSL_CFLAGS=-I${OUTDIR}/include OPENSSL_LIBS="-L${OUTDIR}/lib -lssl -L${OUTDIR}/lib -lcrypto" CFLAGS="-I${OUTDIR}/include" LIBS="-L${OUTDIR}/lib -ldl -lz" 104 | fi 105 | 106 | make 107 | make install prefix=${OUTDIR} 108 | 109 | echo "Downloading nettle_3.5.1." 110 | cd ${BUILDDIR}/downloads 111 | curl -OL https://ftp.gnu.org/gnu/nettle/nettle-3.5.1.tar.gz 112 | 113 | cd ${BUILDDIR}/build 114 | tar -zxvf ${BUILDDIR}/downloads/nettle-3.5.1.tar.gz 115 | cd nettle-3.5.1 116 | 117 | ./.bootstrap 118 | ./configure --enable-static --enable-mini-gmp --disable-openssl --disable-shared --disable-documentation LDFLAGS="-L${OUTDIR}/lib" 119 | make && make install prefix=${OUTDIR} 120 | 121 | echo "Downloading gnutls_3.6.10." 122 | cd ${BUILDDIR}/downloads 123 | curl -OL https://www.gnupg.org/ftp/gcrypt/gnutls/v3.6/gnutls-3.6.10.tar.xz 124 | 125 | cd ${BUILDDIR}/build 126 | tar -xvf ${BUILDDIR}/downloads/gnutls-3.6.10.tar.xz 127 | cd gnutls-3.6.10 128 | 129 | if [ "${OS}" == "Darwin" ]; then 130 | ./configure --enable-static --disable-openssl-compatibility --disable-libdane --without-p11-kit --without-tpm --without-idn --disable-tests --disable-doc --disable-full-test-suite --disable-libdane --disable-nls --enable-shared=no --with-included-libtasn1 --with-included-unistring --with-nettle-mini --enable-guile=no --prefix=$OUTDIR PKG_CONFIG_PATH=${OUTDIR}/lib/pkgconfig LDFLAGS="-L${OUTDIR}/lib" NETTLE_CFLAGS="-I${OUTDIR}/include -arch x86_64" NETTLE_LIBS="-L${OUTDIR}/lib -lnettle" HOGWEED_CFLAGS="-I${OUTDIR}/include -arch x86_64 " HOGWEED_LIBS="-L${OUTDIR}/lib -lhogweed" 131 | else 132 | ./configure --enable-static --disable-openssl-compatibility --disable-libdane --without-p11-kit --without-tpm --without-idn --disable-tests --disable-doc --disable-full-test-suite --disable-libdane --disable-nls --enable-shared=no --with-included-libtasn1 --with-included-unistring --with-nettle-mini --enable-guile=no --prefix=$OUTDIR LDFLAGS="-L${OUTDIR}/lib64" NETTLE_CFLAGS="-I${OUTDIR}/include" NETTLE_LIBS="-L${OUTDIR}/lib64 -lnettle" HOGWEED_CFLAGS="-I${OUTDIR}/include" HOGWEED_LIBS="-L${OUTDIR}/lib64 -lhogweed" LIBS="${OUTDIR}/lib64/libhogweed.a ${OUTDIR}/lib64/libnettle.a" 133 | fi 134 | 135 | make && make install prefix=${OUTDIR} 136 | 137 | echo ">>> Running autoreconf -i" 138 | cd ${CD} 139 | autoreconf -i 140 | 141 | echo ">>> Bootstrap complete" 142 | -------------------------------------------------------------------------------- /build-arm64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # download and build all dependent packages 4 | ./bootstrap-arm64.sh 5 | 6 | # configure tls-scan 7 | ./configure --prefix=${PWD}/build-root 8 | 9 | # make 10 | make 11 | make install 12 | 13 | echo '>>> Complete' 14 | -------------------------------------------------------------------------------- /build-x86-64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # download and build all dependent packages 4 | ./bootstrap.sh 5 | 6 | # configure tls-scan 7 | ./configure --prefix=${PWD}/build-root 8 | 9 | # make 10 | make 11 | make install 12 | 13 | echo '>>> Complete' 14 | 15 | -------------------------------------------------------------------------------- /cert-parser.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Yahoo Inc. 2 | // Copyrights licensed under the New BSD License. See the 3 | // accompanying LICENSE.txt file for terms. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | // References 15 | // https://github.com/openssl/openssl/blob/master/apps/s_client.c 16 | // https://zakird.com/2013/10/13/certificate-parsing-with-openssl 17 | 18 | 19 | static BIO *get_cname(X509_NAME *name) 20 | { 21 | int idx = -1; 22 | if (!name) { 23 | return NULL;; 24 | } 25 | 26 | idx = X509_NAME_get_index_by_NID(name, NID_commonName, -1); 27 | if (!(idx > -1)) { 28 | return NULL; 29 | } 30 | 31 | X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, idx); 32 | if (!entry) { 33 | return NULL; 34 | } 35 | 36 | ASN1_STRING *data = X509_NAME_ENTRY_get_data(entry); 37 | if (!data) { 38 | return NULL; 39 | } 40 | 41 | BIO *bio = BIO_new(BIO_s_mem()); 42 | //int l = ASN1_STRING_print_ex(bio, data, ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_ESC_CTRL); 43 | if (!ASN1_STRING_print(bio, data)) { 44 | BIO_free(bio); 45 | return NULL; 46 | } 47 | 48 | if (BIO_flush(bio)) {} 49 | 50 | return bio; 51 | } 52 | 53 | 54 | static BIO *get_x509v3_ext(X509 *cert, int nid) 55 | { 56 | int loc = 0; 57 | 58 | loc = X509_get_ext_by_NID(cert, nid, -1); 59 | X509_EXTENSION *ex = X509_get_ext(cert, loc); 60 | 61 | if (!ex) { 62 | return NULL; 63 | } 64 | 65 | BIO *bio = BIO_new(BIO_s_mem()); 66 | if (!X509V3_EXT_print(bio, ex, 0, 0)) { 67 | BIO_free(bio); 68 | return NULL; 69 | } 70 | 71 | BIO_printf(bio, "%s", X509_EXTENSION_get_critical(ex) ? " critical" : ""); 72 | if (BIO_flush(bio)) {} 73 | 74 | return bio; 75 | } 76 | 77 | static char *get_serial_no(X509 *const cert, char *out) 78 | { 79 | strcpy(out, "NULL"); 80 | ASN1_INTEGER *serial = X509_get_serialNumber(cert); 81 | int t = 0; 82 | for (int i = 0; i < serial->length; i++) { 83 | 84 | if (t+2 >= TS_SERIALNO_LEN) break; 85 | 86 | if (i + 1 == serial->length) { 87 | sprintf(out + t, "%02X", serial->data[i]); 88 | } else { 89 | t += sprintf(out + t, "%02X:", serial->data[i]); 90 | } 91 | } 92 | 93 | return out; 94 | } 95 | 96 | static char *get_x509_fingerprint(const X509 *cert, 97 | const EVP_MD *digest, char *out) 98 | { 99 | strcpy(out, "NULL"); 100 | unsigned char buf[SHA1LEN]; 101 | unsigned len; 102 | 103 | int rc = X509_digest(cert, digest, buf, &len); 104 | if (rc == 0 || len != SHA1LEN) { 105 | return out; 106 | } 107 | 108 | int t = 0; 109 | for (int i = 0; i < SHA1LEN; i++) { 110 | 111 | if (i + 1 == SHA1LEN) { 112 | sprintf(out + t, "%02X", buf[i]); 113 | } else { 114 | t += sprintf(out + t, "%02X:", buf[i]); 115 | } 116 | } 117 | 118 | return out; 119 | } 120 | 121 | static const char *get_signature_algorithm(const X509 *cert) 122 | { 123 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 124 | int sig_nid = OBJ_obj2nid((cert)->sig_alg->algorithm); 125 | #else 126 | int sig_nid = X509_get_signature_nid(cert); 127 | #endif 128 | return OBJ_nid2ln(sig_nid); 129 | } 130 | 131 | static int convert_ASN1TIME(ASN1_TIME *t, char *out, size_t len) 132 | { 133 | strcpy(out, "NULL"); 134 | int rc; 135 | BIO *b = BIO_new(BIO_s_mem()); 136 | 137 | rc = ASN1_TIME_print(b, t); 138 | if (rc <= 0) { 139 | BIO_free(b); 140 | return EXIT_FAILURE; 141 | } 142 | 143 | rc = BIO_gets(b, out, len); 144 | if (rc <= 0) { 145 | BIO_free(b); 146 | return EXIT_FAILURE; 147 | } 148 | 149 | BIO_free(b); 150 | return EXIT_SUCCESS; 151 | } 152 | 153 | static int get_public_keyalg_and_keylen(EVP_PKEY *key, char *out_alg, bool tmp_key) 154 | { 155 | out_alg[0] = 0; 156 | int keylen = 0; 157 | 158 | switch (EVP_PKEY_id(key)) { 159 | case EVP_PKEY_RSA: 160 | strcpy(out_alg, "RSA"); 161 | keylen = EVP_PKEY_bits(key); 162 | break; 163 | 164 | case EVP_PKEY_DSA: 165 | strcpy(out_alg, "DSA"); 166 | keylen = EVP_PKEY_bits(key); 167 | break; 168 | 169 | case EVP_PKEY_DH: 170 | strcpy(out_alg, "DH"); 171 | keylen = EVP_PKEY_bits(key); 172 | break; 173 | 174 | case EVP_PKEY_EC: 175 | { 176 | EC_KEY *ec = EVP_PKEY_get1_EC_KEY(key); 177 | int nid = 0; 178 | const char *cname = NULL; 179 | 180 | nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); 181 | EC_KEY_free(ec); 182 | //cname = EC_curve_nid2nist(nid); 183 | cname = OBJ_nid2sn(nid); 184 | 185 | if (tmp_key) { 186 | sprintf(out_alg, "ECDH %s", cname); 187 | } else { 188 | sprintf(out_alg, "ECC %s", cname); 189 | } 190 | 191 | keylen = EVP_PKEY_bits(key); 192 | } 193 | break; 194 | 195 | default: 196 | //strcpy(out_alg, OBJ_nid2sn(EVP_PKEY_id(key))); 197 | snprintf(out_alg, PUBKEY_ALGSTR_LEN, "%s", OBJ_nid2sn(EVP_PKEY_id(key))); 198 | keylen = EVP_PKEY_bits(key); 199 | } 200 | 201 | return keylen; 202 | } 203 | 204 | static BIO *get_X509_name(X509_NAME *name) 205 | { 206 | BIO *bio = BIO_new(BIO_s_mem()); 207 | X509_NAME_print_ex(bio, name, 0, XN_FLAG_DN_REV | XN_FLAG_SEP_SPLUS_SPC | 208 | !XN_FLAG_SPC_EQ | XN_FLAG_FN_SN | 209 | ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_ESC_CTRL); 210 | if (BIO_flush(bio)) {} 211 | return bio; 212 | } 213 | 214 | static void X509_parse(X509 *cert, struct x509_cert *x509_cert) 215 | { 216 | /* For error codes, see http://www.openssl.org/docs/apps/verify.html */ 217 | x509_cert->issuer = get_X509_name(X509_get_issuer_name(cert)); 218 | 219 | x509_cert->subject = get_X509_name(X509_get_subject_name(cert)); 220 | 221 | x509_cert->subject_cname = get_cname(X509_get_subject_name(cert)); 222 | 223 | get_x509_fingerprint(cert, EVP_sha1(), x509_cert->sha1_fingerprint); 224 | 225 | x509_cert->x509_ver = X509_get_version(cert) + 1; 226 | 227 | get_serial_no(cert, x509_cert->serialno); 228 | 229 | snprintf(x509_cert->sig_alg, SIG_ALGSTR_LEN, "%s", 230 | get_signature_algorithm(cert)); 231 | 232 | EVP_PKEY *pkey = X509_get_pubkey(cert); 233 | if (pkey) { 234 | x509_cert->pubkey_size = 235 | get_public_keyalg_and_keylen(pkey, x509_cert->pubkey_alg, false); 236 | EVP_PKEY_free(pkey); 237 | } 238 | 239 | x509_cert->subject_keyid = get_x509v3_ext(cert, NID_subject_key_identifier); 240 | 241 | x509_cert->key_usage = get_x509v3_ext(cert, NID_key_usage); 242 | 243 | x509_cert->ext_key_usage = get_x509v3_ext(cert, NID_ext_key_usage); 244 | 245 | x509_cert->basic_constr = get_x509v3_ext(cert, NID_basic_constraints); 246 | 247 | x509_cert->name_constr = get_x509v3_ext(cert, NID_name_constraints); 248 | 249 | ASN1_TIME *not_before = X509_get_notBefore(cert); 250 | ASN1_TIME *not_after = X509_get_notAfter(cert); 251 | 252 | convert_ASN1TIME(not_after, x509_cert->not_after, TS_DATE_LEN); 253 | convert_ASN1TIME(not_before, x509_cert->not_before, TS_DATE_LEN); 254 | time_t ctime; 255 | ctime = time(&ctime); 256 | ASN1_TIME *curr_time = ASN1_TIME_set(NULL, ctime); 257 | 258 | int pday; 259 | int psec; 260 | ASN1_TIME_diff(&pday, &psec, curr_time, not_after); 261 | 262 | if (pday > 0 || psec > 0) { 263 | x509_cert->cert_expired = false; 264 | } else { 265 | x509_cert->cert_expired = true; 266 | } 267 | 268 | ASN1_STRING_free(curr_time); 269 | return; 270 | } 271 | 272 | options_t *ts_get_global_option_obj(); 273 | 274 | static const char *bool_to_str(bool b) 275 | { 276 | if (b) { 277 | return "true"; 278 | } else { 279 | return "false"; 280 | } 281 | } 282 | 283 | const char *get_ssl_version_str(int index) 284 | { 285 | switch (index) { 286 | case SSLv2: 287 | return "SSLv2"; 288 | case SSLv3: 289 | return "SSLv3"; 290 | case TLSv1: 291 | return "TLSv1"; 292 | case TLSv1_1: 293 | return "TLSv1_1"; 294 | case TLSv1_2: 295 | return "TLSv1_2"; 296 | case TLSv1_3: 297 | return "TLSv1_3"; 298 | default: 299 | return "UNKNOWN"; 300 | } 301 | } 302 | 303 | static bool verify_ocsp(SSL *ssl, OCSP_RESPONSE *ocsp_resp, 304 | STACK_OF(X509) *cert_stack) 305 | { 306 | bool ret = false; 307 | OCSP_BASICRESP *bresp = NULL; 308 | int st = -1; 309 | 310 | bresp = OCSP_response_get1_basic(ocsp_resp); 311 | if (!bresp) { 312 | return false; 313 | } 314 | 315 | X509_STORE *x509_store = SSL_CTX_get_cert_store(SSL_get_SSL_CTX(ssl)); 316 | assert(x509_store != NULL); 317 | 318 | st = OCSP_basic_verify(bresp, cert_stack, x509_store, 0); 319 | if (st <= 0) { 320 | //ERR_print_errors_fp(stderr); 321 | ret = false; 322 | } else { 323 | ret = true; 324 | } 325 | 326 | OCSP_BASICRESP_free(bresp); 327 | return ret; 328 | } 329 | 330 | void ts_tls_cert_parse(SSL *ssl, struct tls_cert *tls_cert, 331 | FILE * fp, bool pretty) 332 | { 333 | STACK_OF(X509) * cert_stack; 334 | X509 *cert = NULL; 335 | int i; 336 | const COMP_METHOD *comp = NULL, *expansion = NULL; 337 | 338 | SSL_CIPHER_description(SSL_get_current_cipher(ssl), tls_cert->cipher, 128); 339 | // remove newline chars 340 | tls_cert->cipher[strlen(tls_cert->cipher) - 1] = '\0'; 341 | 342 | EVP_PKEY *key = NULL; 343 | if (SSL_get_server_tmp_key(ssl, &key)) { 344 | tls_cert->temp_pubkey_size = 345 | get_public_keyalg_and_keylen(key, tls_cert->temp_pubkey_alg, true); 346 | EVP_PKEY_free(key); 347 | } 348 | 349 | tls_cert->sni[0] = 0; 350 | const char *sni = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); 351 | if (sni != NULL) { 352 | strcpy(tls_cert->sni, sni); 353 | } 354 | 355 | const unsigned char *data = NULL; 356 | unsigned int len = 0; 357 | SSL_get0_alpn_selected(ssl, &data, &len); 358 | tls_cert->alpn[0] = 0; 359 | if (len > 0) { 360 | strncpy(tls_cert->alpn, (char*)data, len); 361 | tls_cert->alpn[len] = 0; 362 | } 363 | 364 | tls_cert->secure_renego = 365 | SSL_get_secure_renegotiation_support(ssl) ? true : false; 366 | 367 | comp = SSL_get_current_compression(ssl); 368 | snprintf(tls_cert->compression, sizeof(tls_cert->compression), "%s", 369 | comp ? SSL_COMP_get_name(comp) : "NONE"); 370 | 371 | expansion = SSL_get_current_expansion(ssl); 372 | snprintf(tls_cert->expansion, sizeof(tls_cert->expansion), "%s", 373 | comp ? SSL_COMP_get_name(expansion) : "NONE"); 374 | 375 | snprintf(tls_cert->tls_version, sizeof(tls_cert->tls_version), "%s", 376 | SSL_get_version(ssl)); 377 | 378 | SSL_SESSION *session = SSL_get_session(ssl); 379 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 380 | if (session) { 381 | tls_cert->session_lifetime_hint = session->tlsext_tick_lifetime_hint; 382 | } 383 | #else 384 | if (session && SSL_SESSION_has_ticket(session)) { 385 | tls_cert->session_lifetime_hint = SSL_SESSION_get_ticket_lifetime_hint(session); 386 | } 387 | #endif 388 | 389 | // https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_SSL_SESSION.html 390 | if (!tls_cert->session) { 391 | tls_cert->session = SSL_get1_session(ssl); 392 | } 393 | 394 | cert_stack = SSL_get_peer_cert_chain(ssl); 395 | tls_cert->x509_chain_depth = sk_X509_num(cert_stack); 396 | 397 | int res = SSL_get_verify_result(ssl); 398 | if (X509_V_OK == res) { 399 | tls_cert->verify_cert = true; 400 | } else { 401 | tls_cert->verify_cert = false; 402 | } 403 | 404 | snprintf(tls_cert->verify_cert_errmsg, sizeof(tls_cert->verify_cert_errmsg), 405 | "%s", X509_verify_cert_error_string(res)); 406 | 407 | const unsigned char *resp; 408 | long ocsp_len = SSL_get_tlsext_status_ocsp_resp(ssl, &resp); 409 | 410 | if (ocsp_len == -1) { 411 | tls_cert->ocsp_stapling_response = false; 412 | } else { 413 | tls_cert->ocsp_stapling_response = true; 414 | 415 | OCSP_RESPONSE *ocsp_resp = d2i_OCSP_RESPONSE(NULL, &resp, ocsp_len); 416 | if (ocsp_resp) { 417 | tls_cert->verify_ocsp_basic = verify_ocsp(ssl, ocsp_resp, cert_stack); 418 | OCSP_RESPONSE_free(ocsp_resp); 419 | } else { 420 | tls_cert->verify_ocsp_basic = false; 421 | } 422 | } 423 | 424 | for (i = 0; i < tls_cert->x509_chain_depth; i++) { 425 | 426 | if (i == CERT_CHAIN_MAXLEN) { 427 | break; 428 | } 429 | 430 | cert = sk_X509_value(cert_stack, i); 431 | X509_parse(cert, &tls_cert->x509[i]); 432 | 433 | if (i == 0) { 434 | tls_cert->san = get_x509v3_ext(cert, NID_subject_alt_name); 435 | 436 | int ret = X509_check_host(cert, tls_cert->host, 0, 0, NULL); 437 | if (ret == 1) { 438 | tls_cert->verify_host = true; 439 | } else { 440 | tls_cert->verify_host = false; 441 | } 442 | } 443 | } 444 | } 445 | 446 | void ts_tls_cert_BIO_free(struct tls_cert *tls_cert) 447 | { 448 | if (!tls_cert) return; 449 | 450 | if (tls_cert->session) { 451 | SSL_SESSION_free(tls_cert->session); 452 | tls_cert->session = NULL; 453 | } 454 | 455 | for (int i = 0; i < tls_cert->x509_chain_depth; i++) { 456 | 457 | if (i == CERT_CHAIN_MAXLEN) { 458 | break; 459 | } 460 | 461 | if (0 == i) { 462 | if (tls_cert->san) { 463 | BIO_free(tls_cert->san); 464 | tls_cert->san = NULL; 465 | } 466 | } 467 | 468 | if (tls_cert->x509[i].issuer) { 469 | BIO_free(tls_cert->x509[i].issuer); 470 | tls_cert->x509[i].issuer = NULL; 471 | } 472 | 473 | if (tls_cert->x509[i].subject) { 474 | BIO_free(tls_cert->x509[i].subject); 475 | tls_cert->x509[i].subject = NULL; 476 | } 477 | 478 | if (tls_cert->x509[i].subject_cname) { 479 | BIO_free(tls_cert->x509[i].subject_cname); 480 | tls_cert->x509[i].subject_cname = NULL; 481 | } 482 | 483 | if (tls_cert->x509[i].key_usage) { 484 | BIO_free(tls_cert->x509[i].key_usage); 485 | tls_cert->x509[i].key_usage = NULL; 486 | } 487 | 488 | if (tls_cert->x509[i].ext_key_usage) { 489 | BIO_free(tls_cert->x509[i].ext_key_usage); 490 | tls_cert->x509[i].ext_key_usage = NULL; 491 | } 492 | 493 | if (tls_cert->x509[i].basic_constr) { 494 | BIO_free(tls_cert->x509[i].basic_constr); 495 | tls_cert->x509[i].basic_constr = NULL; 496 | } 497 | 498 | if (tls_cert->x509[i].name_constr) { 499 | BIO_free(tls_cert->x509[i].name_constr); 500 | tls_cert->x509[i].name_constr = NULL; 501 | } 502 | 503 | if (tls_cert->x509[i].subject_keyid) { 504 | BIO_free(tls_cert->x509[i].subject_keyid); 505 | tls_cert->x509[i].subject_keyid = NULL; 506 | } 507 | } 508 | } 509 | 510 | void ts_tls_cert_reset(struct tls_cert* tls_cert) 511 | { 512 | if (!tls_cert) return; 513 | 514 | bool *cipher_suite_support = tls_cert->cipher_suite_support; 515 | memset(tls_cert, 0, sizeof(struct tls_cert)); 516 | 517 | const options_t *op = ts_get_global_option_obj(); 518 | if ((cipher_suite_support) && (op->cipher_enum_count > 0)) { 519 | memset(cipher_suite_support, 0, op->cipher_enum_count); 520 | tls_cert->cipher_suite_support= cipher_suite_support; 521 | } 522 | 523 | tls_cert->cipher_suite_support= cipher_suite_support; 524 | } 525 | 526 | const SSL_METHOD *ts_tls_get_method(int index) 527 | { 528 | switch (index) { 529 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 530 | case 0: 531 | return SSLv2_client_method(); 532 | #endif 533 | case 1: 534 | return SSLv3_client_method(); 535 | case 2: 536 | return TLSv1_client_method(); 537 | case 3: 538 | return TLSv1_1_client_method(); 539 | case 4: 540 | return TLSv1_2_client_method(); 541 | case 5: 542 | #if OPENSSL_VERSION_NUMBER > 0x10100000L 543 | return tlsv1_3_client_method(); //TLSv1_3_client_method(); 544 | #endif 545 | default: 546 | return SSLv23_client_method(); 547 | } 548 | } 549 | 550 | long ts_tls_get_options(int index) 551 | { 552 | switch (index) { 553 | case 1: 554 | return ~SSL_OP_NO_SSLv3; 555 | case 2: 556 | return ~SSL_OP_NO_TLSv1; 557 | case 3: 558 | return ~SSL_OP_NO_TLSv1_1; 559 | case 4: 560 | return ~SSL_OP_NO_TLSv1_2; 561 | default: 562 | return SSL_OP_ALL; 563 | } 564 | } 565 | 566 | char json_control_char_map[33][7] = { 567 | "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", 568 | "\\u0008", "\\u0009", "\\u000A", "\\u000B", "\\u000C", "\\u000D", "\\u000E", "\\u000F", 569 | "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017", 570 | "\\u0018", "\\u0019", "\\u001A", "\\u001B", "\\u001C", "\\u001D", "\\u001E", "\\u001F", 571 | }; 572 | 573 | size_t ts_json_escape(char *data, size_t length, char *outbuffer, 574 | size_t outbuffer_length) 575 | { 576 | int i, j=0; 577 | for (i = 0; i < length; i++) { 578 | 579 | if (j >= outbuffer_length-6) { 580 | outbuffer[j] = 0; 581 | return j; 582 | } 583 | 584 | // escape control charactors; ref: https://www.ietf.org/rfc/rfc4627.txt 585 | if (((data[i] > 0) && (data[i] < 8)) || (data[i] == 11) || 586 | ((data[i] > 13) && (data[i] < 32))) { 587 | strncpy(&outbuffer[j], json_control_char_map[(int)data[i]], 6); 588 | j += 6; 589 | continue; 590 | } 591 | 592 | // escape DEL (0x7F) 593 | if (data[i] == 127) { 594 | strncpy(&outbuffer[j], "\\u007F", 6); 595 | j += 6; 596 | continue; 597 | } 598 | 599 | switch (data[i]) { 600 | case '\\': 601 | outbuffer[j++] = '\\'; 602 | outbuffer[j++] = '\\'; 603 | break; 604 | 605 | case '/': 606 | outbuffer[j++] = '\\'; 607 | outbuffer[j++] = '/'; 608 | break; 609 | 610 | case '\n': 611 | outbuffer[j++] = '\\'; 612 | outbuffer[j++] = 'n'; 613 | break; 614 | 615 | case '"': 616 | outbuffer[j++] = '\\'; 617 | outbuffer[j++] = '"'; 618 | break; 619 | 620 | case '\t': 621 | outbuffer[j++] = '\\'; 622 | outbuffer[j++] = 't'; 623 | break; 624 | 625 | case '\r': 626 | outbuffer[j++] = '\\'; 627 | outbuffer[j++] = 'r'; 628 | break; 629 | 630 | case '\f': 631 | outbuffer[j++] = '\\'; 632 | outbuffer[j++] = 'f'; 633 | break; 634 | 635 | case '\b': 636 | outbuffer[j++] = '\\'; 637 | outbuffer[j++] = 'b'; 638 | break; 639 | 640 | default: 641 | outbuffer[j++] = data[i]; 642 | break; 643 | } 644 | } 645 | 646 | outbuffer[j] = 0; 647 | return j; 648 | } 649 | 650 | #define FMT_INDENT(n) (n) * sp_flag, sp 651 | 652 | void ts_tls_print_json(struct tls_cert *tls_cert, FILE *fp, bool pretty) 653 | { 654 | BUF_MEM *bptr = NULL; 655 | char fmt = ' '; 656 | char sp[] = " "; // indent space 657 | char sp_flag = 0; 658 | 659 | if (pretty) { 660 | fmt = '\n'; 661 | sp_flag = 1; 662 | } 663 | 664 | const options_t *op = ts_get_global_option_obj(); 665 | 666 | fprintf(fp, "{%c", fmt); 667 | 668 | if (tls_cert->host[0] != 0) { 669 | fprintf(fp, "%.*s\"host\": \"%s\",%c", FMT_INDENT(2), tls_cert->host, fmt); 670 | } 671 | 672 | if (tls_cert->ip[0] != 0) { 673 | fprintf(fp, "%.*s\"ip\": \"%s\",%c", FMT_INDENT(2), tls_cert->ip, fmt); 674 | } 675 | 676 | fprintf(fp, "%.*s\"port\": %d,%c", FMT_INDENT(2), tls_cert->port, fmt); 677 | fprintf(fp, "%.*s\"elapsedTime\": %d,%c", FMT_INDENT(2), tls_cert->elapsed_time_ms, fmt); 678 | 679 | if (tls_cert->sni[0] != 0) { 680 | fprintf(fp, "%.*s\"sni\": \"%s\",%c", FMT_INDENT(2), tls_cert->sni, fmt); 681 | } 682 | 683 | if (tls_cert->alpn[0] != 0) { 684 | fprintf(fp, "%.*s\"alpn\": \"%s\",%c", FMT_INDENT(2), tls_cert->alpn, fmt); 685 | } 686 | 687 | fprintf(fp, "%.*s\"tlsVersion\": \"%s\",%c", FMT_INDENT(2), 688 | tls_cert->tls_version, fmt); 689 | fprintf(fp, "%.*s\"cipher\": \"%s\",%c", FMT_INDENT(2), tls_cert->cipher, fmt); 690 | 691 | if (tls_cert->temp_pubkey_size > 0) { 692 | fprintf(fp, "%.*s\"tempPublicKeyAlg\": \"%s\",%c", FMT_INDENT(2), 693 | tls_cert->temp_pubkey_alg, fmt); 694 | 695 | fprintf(fp, "%.*s\"tempPublicKeySize\": %d,%c", FMT_INDENT(2), 696 | tls_cert->temp_pubkey_size, fmt); 697 | } 698 | 699 | fprintf(fp, "%.*s\"secureRenego\": %s,%c", FMT_INDENT(2), 700 | bool_to_str(tls_cert->secure_renego), fmt); 701 | fprintf(fp, "%.*s\"compression\": \"%s\",%c", FMT_INDENT(2), 702 | tls_cert->compression, fmt); 703 | fprintf(fp, "%.*s\"expansion\": \"%s\",%c", FMT_INDENT(2), 704 | tls_cert->expansion, fmt); 705 | 706 | if (op->session_reuse_test) { 707 | fprintf(fp, "%.*s\"sessionReuse\": %s,%c", FMT_INDENT(2), 708 | bool_to_str(tls_cert->session_reuse_supported), fmt); 709 | } 710 | 711 | if (tls_cert->session_lifetime_hint > 0) { 712 | fprintf(fp, "%.*s\"sessionLifetimeHint\": %lu,%c", FMT_INDENT(2), 713 | tls_cert->session_lifetime_hint, fmt); 714 | } 715 | 716 | if (op->tls_vers_enum) { 717 | 718 | fprintf(fp, "%.*s\"tlsVersions\": [%c", FMT_INDENT(2), fmt); 719 | int i, j = 0; 720 | int vers[MAX_TLS_VERSION]; 721 | 722 | for (i = 0; i < MAX_TLS_VERSION; i++) { 723 | if (tls_cert->tls_ver_support[i]) { 724 | vers[j++] = i; 725 | } 726 | } 727 | 728 | if (j != 0) { 729 | i = 0; 730 | if (j > 1) { 731 | for (i = 0; i < j-1; i++) { 732 | fprintf(fp, "%.*s\"%s\", %c", FMT_INDENT(4), 733 | get_ssl_version_str(vers[i]), fmt); 734 | } 735 | } 736 | 737 | fprintf(fp, "%.*s\"%s\"%c%.*s],%c", FMT_INDENT(4), 738 | get_ssl_version_str(vers[i]), fmt, FMT_INDENT(2), fmt); 739 | } else { // j == 0 740 | fprintf(fp, "%.*s],%c", FMT_INDENT(2), fmt); 741 | } 742 | } 743 | 744 | if (op->cipher_enum) { 745 | 746 | int supported[op->cipher_enum_count]; 747 | int unsupported[op->cipher_enum_count]; 748 | 749 | int i = 0, s = 0, u = 0; 750 | for (i = 0; i < op->cipher_enum_count; i++) { 751 | 752 | if (tls_cert->cipher_suite_support[i]) { 753 | supported[s++] = i; 754 | } else { 755 | unsupported[u++] = i; 756 | } 757 | 758 | } 759 | 760 | int supported1_3[op->cipher1_3_enum_count]; 761 | int unsupported1_3[op->cipher1_3_enum_count]; 762 | 763 | int i13 = 0, s13 = 0, u13 = 0; 764 | for (i13 = 0; i13 < op->cipher1_3_enum_count; i13++) { 765 | 766 | if (tls_cert->cipher1_3_suite_support[i13]) { 767 | supported1_3[s13++] = i13; 768 | } else { 769 | unsupported1_3[u13++] = i13; 770 | } 771 | 772 | } 773 | 774 | fprintf(fp, "%.*s\"cipherSuite\": {%c", FMT_INDENT(2), fmt); 775 | fprintf(fp, "%.*s\"supported\": [%c", FMT_INDENT(4), fmt); 776 | 777 | if (s13 > 0) { 778 | for (i = 0; i < s13-1; i++) { 779 | fprintf(fp, "%.*s\"%s\",%c", FMT_INDENT(6), 780 | op->cipher1_3_enum_list[supported1_3[i]], fmt); 781 | } 782 | 783 | if (s > 0) { 784 | fprintf(fp, "%.*s\"%s\",%c", FMT_INDENT(6), 785 | op->cipher1_3_enum_list[supported1_3[i]], fmt); 786 | } else { 787 | fprintf(fp, "%.*s\"%s\"%c", FMT_INDENT(6), 788 | op->cipher1_3_enum_list[supported1_3[i]], fmt); 789 | 790 | } 791 | } 792 | 793 | if (s > 0) { 794 | for (i = 0; i < s-1; i++) { 795 | fprintf(fp, "%.*s\"%s\",%c", FMT_INDENT(6), 796 | op->cipher_enum_list[supported[i]], fmt); 797 | } 798 | 799 | fprintf(fp, "%.*s\"%s\"%c", FMT_INDENT(6), 800 | op->cipher_enum_list[supported[i]], fmt); 801 | } 802 | 803 | if (op->show_unsupported_ciphers) { 804 | fprintf(fp, "%.*s],%c", FMT_INDENT(4), fmt); 805 | fprintf(fp, "%.*s\"notSupported\": [%c", FMT_INDENT(4), fmt); 806 | 807 | if (u13 > 0) { 808 | for (i = 0; i < u13-1; i++) { 809 | fprintf(fp, "%.*s\"%s\",%c", FMT_INDENT(6), 810 | op->cipher1_3_enum_list[unsupported1_3[i]], fmt); 811 | } 812 | 813 | if (u > 0) { 814 | fprintf(fp, "%.*s\"%s\",%c", FMT_INDENT(6), 815 | op->cipher1_3_enum_list[unsupported1_3[i]], fmt); 816 | } else { 817 | fprintf(fp, "%.*s\"%s\"%c", FMT_INDENT(6), 818 | op->cipher1_3_enum_list[unsupported1_3[i]], fmt); 819 | 820 | } 821 | } 822 | 823 | if (u > 0) { 824 | for (i = 0; i < u-1; i++) { 825 | fprintf(fp, "%.*s\"%s\",%c", FMT_INDENT(6), 826 | op->cipher_enum_list[unsupported[i]], fmt); 827 | } 828 | 829 | fprintf(fp, "%.*s\"%s\"%c", FMT_INDENT(6), 830 | op->cipher_enum_list[unsupported[i]], fmt); 831 | } 832 | } 833 | 834 | fprintf(fp, "%.*s]%c", FMT_INDENT(4), fmt); 835 | fprintf(fp, "%.*s},%c", FMT_INDENT(2), fmt); 836 | } 837 | 838 | fprintf(fp, "%.*s\"x509ChainDepth\": %d,%c", FMT_INDENT(2), 839 | tls_cert->x509_chain_depth, fmt); 840 | 841 | if (tls_cert->verify_cert) { 842 | fprintf(fp, "%.*s\"verifyCertResult\": true,%c", FMT_INDENT(2), fmt); 843 | } else { 844 | fprintf(fp, "%.*s\"verifyCertResult\": false,%c", FMT_INDENT(2), fmt); 845 | fprintf(fp, "%.*s\"verifyCertError\": \"%s\",%c", 846 | FMT_INDENT(2), tls_cert->verify_cert_errmsg, fmt); 847 | } 848 | 849 | fprintf(fp, "%.*s\"verifyHostResult\": %s,%c", 850 | FMT_INDENT(2), bool_to_str(tls_cert->verify_host), fmt); 851 | 852 | fprintf(fp, "%.*s\"ocspStapled\": %s,%c", FMT_INDENT(2), 853 | bool_to_str(tls_cert->ocsp_stapling_response), fmt); 854 | 855 | if (tls_cert->ocsp_stapling_response) { 856 | fprintf(fp, "%.*s\"verifyOcspResult\": %s,%c", FMT_INDENT(2), 857 | bool_to_str(tls_cert->verify_ocsp_basic), fmt); 858 | } 859 | 860 | if (tls_cert->x509_chain_depth > 0) { 861 | fprintf(fp, "%.*s\"certificateChain\": [%c", FMT_INDENT(2), fmt); 862 | } 863 | 864 | int i = 0; 865 | size_t outbuffer_length = 4096; 866 | size_t oblen = 0; 867 | char outbuffer[outbuffer_length]; 868 | while (i < tls_cert->x509_chain_depth) { 869 | 870 | if (i == CERT_CHAIN_MAXLEN) { 871 | break; 872 | } 873 | 874 | fprintf(fp, "%.*s{%c", FMT_INDENT(2), fmt); 875 | fprintf(fp, "%.*s\"version\": %d,%c", FMT_INDENT(4), 876 | tls_cert->x509[i].x509_ver, fmt); 877 | 878 | if (tls_cert->x509[i].subject) { 879 | BIO_get_mem_ptr(tls_cert->x509[i].subject, &bptr); 880 | oblen = ts_json_escape(bptr->data, bptr->length, &outbuffer[0], 881 | outbuffer_length); 882 | fprintf(fp, "%.*s\"subject\": \"%.*s\",%c", FMT_INDENT(4), 883 | (int)oblen, outbuffer, fmt); 884 | } 885 | 886 | if (tls_cert->x509[i].issuer) { 887 | BIO_get_mem_ptr(tls_cert->x509[i].issuer, &bptr); 888 | 889 | oblen = ts_json_escape(bptr->data, bptr->length, &outbuffer[0], 890 | outbuffer_length); 891 | 892 | fprintf(fp, "%.*s\"issuer\": \"%.*s\",%c", FMT_INDENT(4), 893 | (int)oblen, outbuffer, fmt); 894 | } 895 | 896 | if (tls_cert->x509[i].subject_cname) { 897 | BIO_get_mem_ptr(tls_cert->x509[i].subject_cname, &bptr); 898 | oblen = ts_json_escape(bptr->data, bptr->length, &outbuffer[0], 899 | outbuffer_length); 900 | fprintf(fp, "%.*s\"subjectCN\": \"%.*s\",%c", FMT_INDENT(4), 901 | (int)oblen, outbuffer, fmt); 902 | } 903 | 904 | if (0 == i) { 905 | if (tls_cert->san) { 906 | BIO_get_mem_ptr(tls_cert->san, &bptr); 907 | 908 | oblen = ts_json_escape(bptr->data, bptr->length, &outbuffer[0], 909 | outbuffer_length); 910 | 911 | fprintf(fp, "%.*s\"subjectAltName\": \"%.*s\",%c", FMT_INDENT(4), 912 | (int)oblen, outbuffer, fmt); 913 | } 914 | } 915 | 916 | fprintf(fp, "%.*s\"signatureAlg\": \"%s\",%c", FMT_INDENT(4), 917 | tls_cert->x509[i].sig_alg, fmt); 918 | fprintf(fp, "%.*s\"notBefore\": \"%s\",%c", FMT_INDENT(4), 919 | tls_cert->x509[i].not_before, fmt); 920 | fprintf(fp, "%.*s\"notAfter\": \"%s\",%c", FMT_INDENT(4), 921 | tls_cert->x509[i].not_after, fmt); 922 | 923 | fprintf(fp, "%.*s\"expired\": %s,%c", FMT_INDENT(4), 924 | bool_to_str(tls_cert->x509[i].cert_expired), fmt); 925 | 926 | fprintf(fp, "%.*s\"serialNo\": \"%s\",%c", FMT_INDENT(4), 927 | tls_cert->x509[i].serialno, fmt); 928 | 929 | if (tls_cert->x509[i].key_usage) { 930 | BIO_get_mem_ptr(tls_cert->x509[i].key_usage, &bptr); 931 | fprintf(fp, "%.*s\"keyUsage\": \"%.*s\",%c", FMT_INDENT(4), 932 | (int)bptr->length, bptr->data, fmt); 933 | } 934 | 935 | if (tls_cert->x509[i].ext_key_usage) { 936 | BIO_get_mem_ptr(tls_cert->x509[i].ext_key_usage, &bptr); 937 | fprintf(fp, "%.*s\"extKeyUsage\": \"%.*s\",%c", FMT_INDENT(4), 938 | (int)bptr->length, bptr->data, fmt); 939 | } 940 | 941 | fprintf(fp, "%.*s\"publicKeyAlg\": \"%s\",%c", FMT_INDENT(4), 942 | tls_cert->x509[i].pubkey_alg, fmt); 943 | 944 | fprintf(fp, "%.*s\"publicKeySize\": %d,%c", FMT_INDENT(4), 945 | tls_cert->x509[i].pubkey_size, fmt); 946 | 947 | if (tls_cert->x509[i].basic_constr) { 948 | BIO_get_mem_ptr(tls_cert->x509[i].basic_constr, &bptr); 949 | fprintf(fp, "%.*s\"basicConstraints\": \"%.*s\",%c", FMT_INDENT(4), 950 | (int)bptr->length, bptr->data, fmt); 951 | } 952 | 953 | if (tls_cert->x509[i].name_constr) { 954 | BIO_get_mem_ptr(tls_cert->x509[i].name_constr, &bptr); 955 | oblen = ts_json_escape(bptr->data, bptr->length, &outbuffer[0], 956 | outbuffer_length); 957 | fprintf(fp, "%.*s\"nameConstraints\": \"%.*s\",%c", FMT_INDENT(4), 958 | (int)oblen, outbuffer, fmt); 959 | } 960 | 961 | if (tls_cert->x509[i].subject_keyid) { 962 | BIO_get_mem_ptr(tls_cert->x509[i].subject_keyid, &bptr); 963 | fprintf(fp, "%.*s\"subjectKeyIdentifier\": \"%.*s\",%c", FMT_INDENT(4), 964 | (int)bptr->length, bptr->data, fmt); 965 | } 966 | 967 | fprintf(fp, "%.*s\"sha1Fingerprint\": \"%s\"%c", FMT_INDENT(4), 968 | tls_cert->x509[i].sha1_fingerprint, fmt); 969 | //fprintf(fp, "authorityKeyIdentifier: %s; ", tls_cert->x509[i].authority_keyid); 970 | 971 | if ((i + 1 == tls_cert->x509_chain_depth) || (i + 1 == CERT_CHAIN_MAXLEN)){ 972 | fprintf(fp, "%.*s} ]%c", FMT_INDENT(2), fmt); 973 | } else { 974 | fprintf(fp, "%.*s},", FMT_INDENT(2)); 975 | } 976 | 977 | i++; 978 | } 979 | 980 | fprintf(fp, "}\n"); 981 | fflush(fp); 982 | 983 | // print seperate, outside of json 984 | if (op->session_print) { 985 | fprintf(stderr, "host: %s; ip: %s; ssl_session:\n", 986 | tls_cert->host, tls_cert->ip); 987 | PEM_write_SSL_SESSION(stderr, tls_cert->session); 988 | } 989 | } 990 | -------------------------------------------------------------------------------- /common.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Yahoo Inc. 2 | // Copyrights licensed under the New BSD License. See the 3 | // accompanying LICENSE.txt file for terms. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | input_handle_t in_handle; 11 | 12 | void init_stats(stats_t *st) 13 | { 14 | memset(st, 0, sizeof(stats_t)); 15 | gettimeofday(&st->start_time, NULL); 16 | } 17 | 18 | ssize_t ts_get_line_input(input_handle_t *handle, char *line, size_t len) 19 | { 20 | ssize_t read = 0; 21 | line[0] = 0; 22 | 23 | if ((handle->eof) || (handle->fp == NULL)) { 24 | return -1; 25 | } 26 | 27 | if (strlen(handle->host)) { 28 | strncpy(line, handle->host, len); 29 | line[len - 1] = '\0'; 30 | 31 | handle->eof = true; 32 | return len; // TODO len? 33 | } 34 | 35 | while ((read = getline(&line, &len, handle->fp)) != -1) { 36 | // zero-terminate only new lines 37 | if (line[read - 1] == '\n') { 38 | line[read - 1] = 0; 39 | } 40 | 41 | // skip empty lines 42 | if (read > 0) 43 | break; 44 | } 45 | 46 | if ((read < 1) || (len == 0)) { 47 | handle->eof = true; 48 | fclose(handle->fp); 49 | handle->fp = NULL; 50 | return -1; 51 | } 52 | 53 | return read; 54 | } 55 | 56 | uint64_t ts_elapsed_time(struct timeval t1) 57 | { 58 | struct timeval t2; 59 | gettimeofday(&t2, NULL); 60 | return ((t2.tv_sec - t1.tv_sec) * 1000000) + (t2.tv_usec - t1.tv_usec); 61 | } 62 | 63 | void ts_get_ip(int fd, char *ipstr, size_t ipstr_len) 64 | { 65 | ipstr[0] = 0; 66 | socklen_t len; 67 | struct sockaddr_storage addr; 68 | len = sizeof(addr); 69 | 70 | getpeername(fd, (struct sockaddr *) &addr, &len); 71 | 72 | if (addr.ss_family == AF_INET) { 73 | struct sockaddr_in *s = (struct sockaddr_in *) &addr; 74 | inet_ntop(AF_INET, &s->sin_addr, ipstr, ipstr_len); 75 | } else { // AF_INET6 76 | struct sockaddr_in6 *s = (struct sockaddr_in6 *) &addr; 77 | inet_ntop(AF_INET6, &s->sin6_addr, ipstr, ipstr_len); 78 | } 79 | 80 | } 81 | 82 | ts_address_family_t ts_address_family(const char *target) 83 | { 84 | if (target[0] == '[') { 85 | return TS_IPV6; 86 | } 87 | 88 | size_t len = strlen(target); 89 | if (len == strspn(target, "0123456789.:")) { 90 | return TS_IPV4; 91 | } 92 | 93 | return TS_HOSTNAME; 94 | } 95 | 96 | void ts_parse_connect_target(const char *target, char *host, size_t hlen, uint16_t *port) 97 | { 98 | size_t len = strlen(target); 99 | // addr should be atleast 4 chars 100 | if (len < 5) { 101 | return; 102 | } 103 | 104 | ts_address_family_t addr = ts_address_family(target); 105 | switch (addr) { 106 | case TS_HOSTNAME: 107 | case TS_IPV4: 108 | for (int i=len-1; i>=0; i--) { 109 | if ((target[i] == ':') && (i+1 < len)) { 110 | *port = strtol(target + i + 1, NULL, 10); 111 | snprintf(host, hlen, "%.*s", i, target); 112 | return; 113 | } 114 | } 115 | 116 | snprintf(host, OPT_STRLEN, "%s", target); 117 | break; 118 | 119 | case TS_IPV6: 120 | for (int i=len-1; i>=0; i--) { 121 | if ((target[i] == ']') && (target[i+1] == ':') && (i+1 < len)) { 122 | *port = strtol(target + i + 2, NULL, 10); 123 | snprintf(host, hlen, "%.*s", i-1, target+1); 124 | return; 125 | } 126 | } 127 | 128 | snprintf(host, OPT_STRLEN, "%.*s", (int)len-2, target+1); 129 | break; 130 | default: break; 131 | } 132 | 133 | return; 134 | } 135 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([tls-scan], m4_esyscmd([grep -m 1 Version CHANGELOG | awk '{print $2}' | tr -d '\n']), []) 2 | AC_CONFIG_AUX_DIR([.]) 3 | AM_INIT_AUTOMAKE([-Wall -Werror foreign -Wno-portability]) 4 | AC_PROG_CC 5 | AC_CONFIG_FILES([Makefile]) 6 | 7 | AC_CANONICAL_HOST 8 | case "$host_os" in 9 | darwin*) 10 | darwin=true 11 | ;; 12 | linux*) 13 | linux=true 14 | ;; 15 | esac 16 | 17 | case "$host_cpu" in 18 | aarch64) 19 | arm64=true 20 | ;; 21 | x86_64) 22 | amd64=true 23 | ;; 24 | arm) 25 | arm64=true 26 | ;; 27 | esac 28 | 29 | AM_CONDITIONAL([LINUX_ARM64], [test x$linux$arm64 = xtruetrue]) 30 | AM_CONDITIONAL([LINUX_AMD64], [test x$linux$amd64 = xtruetrue]) 31 | AM_CONDITIONAL([DARWIN_AMD64], [test x$darwin = xtrue]) 32 | AC_OUTPUT 33 | 34 | -------------------------------------------------------------------------------- /gnutls13.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "common.h" 13 | #include "cert-parser.h" 14 | 15 | #ifdef HAVE_CONFIG_H 16 | #include 17 | #endif 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | // GnuTLS equivalent of tls 1.3 ciphers 26 | static const char *tlsv1_3_gnutls_ciphers_str = "NONE:+CTYPE-ALL:+COMP-ALL:+GROUP-ALL:+SIGN-ALL:+KX-ALL:+MAC-ALL:+VERS-TLS1.3:+AES-128-GCM:+AES-256-GCM:+AES-128-CCM:+AES-256-CCM:+CHACHA20-POLY1305:+AES-128-CCM-8:+AES-256-CCM-8"; 27 | // note - the following cipher is 1:1 equivalent. 28 | // TLS1_3_MAX_CIPHER_COUNT defined in cert-parser.h 29 | static const char *tlsv1_3_gnutls_ciphers[TLS1_3_MAX_CIPHER_COUNT] = { "AES-128-GCM", "AES-256-GCM", "CHACHA20-POLY1305", "AES-128-CCM", "AES-128-CCM-8"}; 30 | static const char *tlsv1_3_openssl_ciphers[TLS1_3_MAX_CIPHER_COUNT] = { "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256", "TLS_AES_128_CCM_SHA256", "TLS_AES_128_CCM_8_SHA256" }; 31 | 32 | #define TLS1_2_MAX_CHACHA_CIPHER_COUNT 3 33 | 34 | static const char *tlsv1_2_gnutls_chacha_ciphers[TLS1_2_MAX_CHACHA_CIPHER_COUNT] = { 35 | "NONE:+VERS-TLS1.2:-CIPHER-ALL:+CHACHA20-POLY1305:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ECDHE-ECDSA:+CURVE-ALL", 36 | "NONE:+VERS-TLS1.2:-CIPHER-ALL:+CHACHA20-POLY1305:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ECDHE-RSA:+CURVE-ALL", 37 | "NONE:+VERS-TLS1.2:-CIPHER-ALL:+CHACHA20-POLY1305:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+DHE-RSA:+CURVE-ALL", 38 | }; 39 | 40 | static const char *tlsv1_2_openssl_chacha_ciphers[TLS1_2_MAX_CHACHA_CIPHER_COUNT] = { "ECDHE-RSA-CHACHA20-POLY1305-OLD", "ECDHE-ECDSA-CHACHA20-POLY1305-OLD", "DHE-RSA-CHACHA20-POLY1305-OLD" }; 41 | 42 | #define CHECK(x) assert((x)>=0) 43 | 44 | // tls 1_2 chacha cipher index for gnutls 45 | static const char* gnutls1_2_chacha_priority_str(const char *openssl_cipher) 46 | { 47 | for (int i=0; i < TLS1_2_MAX_CHACHA_CIPHER_COUNT; i++) { 48 | if (strcmp(tlsv1_2_openssl_chacha_ciphers[i], openssl_cipher) == 0) { 49 | return tlsv1_2_gnutls_chacha_ciphers[i]; 50 | } 51 | } 52 | 53 | return NULL; 54 | } 55 | 56 | void gnutls13_init(struct options *op) { 57 | op->cipher1_3_enum_count = 0; 58 | 59 | if (op->cipher_enum) { 60 | // if cipher is provided by the user, select only the chosen tls1.3 ciphers 61 | if (op->cipher_user_input) { 62 | for (int i = 0; i < TLS1_3_MAX_CIPHER_COUNT; i++) { 63 | if (strstr(op->ciphers, tlsv1_3_openssl_ciphers[i]) != NULL) { 64 | strcpy(op->cipher1_3_enum_list[op->cipher1_3_enum_count], 65 | tlsv1_3_gnutls_ciphers[i]); 66 | op->cipher1_3_enum_count++; 67 | } 68 | } 69 | } else if ((!op->ssl2) && (!op->ssl3) && (!op->tls1)) { 70 | // select all tls 1.3 ciphers that matches 71 | for (int i = 0; i < TLS1_3_MAX_CIPHER_COUNT; i++) { 72 | strcpy(op->cipher1_3_enum_list[op->cipher1_3_enum_count], 73 | tlsv1_3_gnutls_ciphers[i]); 74 | op->cipher1_3_enum_count++; 75 | } 76 | } 77 | 78 | } 79 | 80 | if (gnutls_check_version("3.4.6") == NULL) { 81 | fprintf(stderr, "GnuTLS 3.4.6 or later is required for this program\n"); 82 | exit(1); 83 | } 84 | 85 | CHECK(gnutls_global_init()); 86 | 87 | // X509 stuff 88 | CHECK(gnutls_certificate_allocate_credentials(&op->xcred)); 89 | 90 | // sets the system trusted CAs for Internet PKI 91 | CHECK(gnutls_certificate_set_x509_system_trust(op->xcred)); 92 | 93 | /* If client holds a certificate it can be set using the following: 94 | * 95 | gnutls_certificate_set_x509_key_file (xcred, "cert.pem", "key.pem", 96 | GNUTLS_X509_FMT_PEM); 97 | */ 98 | } 99 | 100 | void gnutls13_deinit(struct options *op) 101 | { 102 | gnutls_certificate_free_credentials(op->xcred); 103 | gnutls_global_deinit(); 104 | return; 105 | } 106 | 107 | bool _gnutls13_session_init(client_t *cli, int sd, const char *priority) 108 | { 109 | 110 | // Initialize TLS session 111 | CHECK(gnutls_init(&cli->session, GNUTLS_CLIENT)); 112 | CHECK(gnutls_priority_set_direct(cli->session, priority, NULL)); 113 | 114 | // put the x509 credentials to the current session 115 | CHECK(gnutls_credentials_set(cli->session, GNUTLS_CRD_CERTIFICATE, 116 | cli->op->xcred)); 117 | 118 | if (cli->op->sni[0] != 0) { 119 | CHECK(gnutls_server_name_set(cli->session, GNUTLS_NAME_DNS, cli->op->sni, 120 | strlen(cli->op->sni))); 121 | } else if (cli->host[0] != 0) { 122 | CHECK(gnutls_server_name_set(cli->session, GNUTLS_NAME_DNS, cli->host, 123 | strlen(cli->host))); 124 | } 125 | /* if (cli->host[0] != 0) { 126 | // gnutls_session_set_verify_cert(session, cli->host, 0); 127 | CHECK(gnutls_server_name_set(session, GNUTLS_NAME_DNS, cli->host, strlen(cli->host))); 128 | } else { 129 | gnutls_session_set_verify_cert(session, NULL, 0); 130 | }*/ 131 | /* connect to the peer 132 | */ 133 | gnutls_transport_set_int(cli->session, sd); 134 | gnutls_handshake_set_timeout(cli->session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); 135 | return true; 136 | } 137 | 138 | int gnutls13_session_init(client_t *cli, int sd) 139 | { 140 | 141 | if (cli->state == ST_GNUTLS_VERSION) { 142 | _gnutls13_session_init(cli, sd, tlsv1_3_gnutls_ciphers_str); 143 | } else if (cli->state == ST_GNUTLS_CIPHER) { 144 | char priority[512]; 145 | if (cli->cipher1_3_index < cli->op->cipher1_3_enum_count) { 146 | snprintf(priority, 512, "NONE:+CTYPE-ALL:+COMP-ALL:+GROUP-ALL:+SIGN-ALL:+KX-ALL:+MAC-ALL:+VERS-TLS1.3:+%s", cli->op->cipher1_3_enum_list[cli->cipher1_3_index]); 147 | _gnutls13_session_init(cli, sd, priority); 148 | } else { 149 | assert(0); 150 | } 151 | } else if (cli->state == ST_GNUTLS_1_2CHACHA_CIPHER) { 152 | char priority[512]; 153 | if (cli->cipher1_3_index < cli->op->cipher1_3_enum_count) { 154 | const char *p = gnutls1_2_chacha_priority_str(cli->op->cipher_enum_list[cli->cipher_index]); 155 | if (p) { 156 | snprintf(priority, 512, "%s", p); 157 | _gnutls13_session_init(cli, sd, priority); 158 | } else { 159 | assert(0); 160 | } 161 | } else { 162 | assert(0); 163 | } 164 | } else { 165 | assert(0); 166 | } 167 | 168 | return 0; 169 | } 170 | 171 | void gnutls13_session_deinit(client_t *cli) 172 | { 173 | // TODO - check err status and log to stderr 174 | gnutls_deinit(cli->session); 175 | } 176 | 177 | ts_status_t gnutls13_handshake(client_t *cli, int sd) 178 | { 179 | int ret; 180 | bool status = false; 181 | char *desc; 182 | gnutls_datum_t out; 183 | int type; 184 | 185 | retry: 186 | ret = gnutls_handshake(cli->session); 187 | if (ret < 0) { 188 | if (gnutls_error_is_fatal(ret) == 0) { 189 | if (gnutls_record_get_direction(cli->session) == 0) { 190 | return TS_EAGAIN_ERR; // read 191 | } else { 192 | goto retry; // write 193 | } 194 | } else if (ret == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) { 195 | // check certificate verification status 196 | type = gnutls_certificate_type_get(cli->session); 197 | status = gnutls_session_get_verify_cert_status(cli->session); 198 | CHECK(gnutls_certificate_verification_status_print(status, type, &out, 0)); 199 | //printf("cert verify output: %s\n", out.data); 200 | gnutls_free(out.data); 201 | } 202 | 203 | fprintf(stderr, "host: %s; ip: %s; error: Network; errormsg: \ 204 | Error encountered during GnuTLS handshake: %d \ 205 | Unsupported TLS 1.3 version/cipher\n", cli->host, cli->ip, ret); 206 | return TS_HSHAKE_ERR; 207 | } 208 | 209 | desc = gnutls_session_get_desc(cli->session); 210 | //printf("- Session info: %s\n", desc); 211 | gnutls_free(desc); 212 | gnutls_bye(cli->session, GNUTLS_SHUT_RDWR); 213 | 214 | if (cli->state == ST_GNUTLS_VERSION) { 215 | cli->tls_cert->tls_ver_support[cli->tls_ver_index] = true; 216 | } else if (cli->state == ST_GNUTLS_CIPHER) { 217 | if (cli->cipher1_3_index < cli->op->cipher1_3_enum_count) { 218 | cli->tls_cert->cipher1_3_suite_support[cli->cipher1_3_index] = true; 219 | } else { 220 | assert(0); 221 | } 222 | } else if (cli->state == ST_GNUTLS_1_2CHACHA_CIPHER) { 223 | cli->tls_cert->cipher_suite_support[cli->cipher_index] = true; 224 | } else { 225 | assert(0); 226 | } 227 | 228 | return TS_SUCCESS; 229 | } 230 | -------------------------------------------------------------------------------- /include/cert-parser.h: -------------------------------------------------------------------------------- 1 | #ifndef CERTPARSE_H 2 | #define CERTPARSE_H 3 | 4 | // Copyright (c) 2016, Yahoo Inc. 5 | // Copyrights licensed under the New BSD License. See the 6 | // accompanying LICENSE.txt file for terms. 7 | 8 | #include 9 | 10 | #define FQDN_MAXLEN 256 11 | #define SHA1LEN 20 12 | #define CERT_CHAIN_MAXLEN 10 13 | #define PUBKEY_ALGSTR_LEN 16 14 | #define SIG_ALGSTR_LEN 64 15 | #define TS_DATE_LEN 128 16 | #define TS_SERIALNO_LEN 128 17 | #define TS_ERRMSG_LEN 256 18 | 19 | /* ssl2, ssl3, tls1, tls1_1, tls1_2 tls1_3*/ 20 | #define MAX_TLS_VERSION 6 21 | /* openssl scans upto tls1_2. The new version is scanned using gnutls */ 22 | #define MAX_OPENSSL_TLS_VERSION 5 23 | /* used in gnutls13.c */ 24 | #define TLS1_3_MAX_CIPHER_COUNT 5 25 | 26 | enum TLSVersion { 27 | SSLv2 = 0, 28 | SSLv3 = 1, 29 | TLSv1 = 2, 30 | TLSv1_1 = 3, 31 | TLSv1_2 = 4, 32 | TLSv1_3 = 5 33 | }; 34 | 35 | struct x509_cert { 36 | char sha1_fingerprint[3*SHA1LEN+1]; 37 | char sig_alg[SIG_ALGSTR_LEN]; 38 | char pubkey_alg[PUBKEY_ALGSTR_LEN]; 39 | char pubkey_ec_curve[16]; 40 | int pubkey_size; 41 | BIO *key_usage; 42 | int x509_ver; 43 | char issuer_serialno[TS_SERIALNO_LEN]; 44 | BIO *issuer; 45 | char serialno[TS_SERIALNO_LEN]; 46 | BIO *subject; 47 | BIO *subject_cname; 48 | char not_before[TS_DATE_LEN]; 49 | char not_after[TS_DATE_LEN]; 50 | BIO *ext_key_usage; 51 | bool is_ca; 52 | BIO *name_constr; 53 | BIO *basic_constr; 54 | BIO *subject_keyid; 55 | char authority_keyid[256]; 56 | bool cert_expired; 57 | bool valid_cert; 58 | }; 59 | 60 | struct tls_cert { 61 | char host[OPT_STRLEN]; 62 | char ip[OPT_STRLEN]; 63 | uint16_t port; 64 | char cipher[128]; 65 | char sni[256]; 66 | char alpn[256]; 67 | char tls_version[8]; 68 | bool secure_renego; 69 | char compression[32]; 70 | char expansion[32]; 71 | char temp_pubkey_alg[PUBKEY_ALGSTR_LEN]; 72 | int temp_pubkey_size; 73 | unsigned long session_lifetime_hint; 74 | bool session_reuse_supported; 75 | bool ocsp_stapling_response; 76 | bool verify_ocsp_basic; 77 | int x509_chain_depth; 78 | bool verify_cert; 79 | char verify_cert_errmsg[TS_ERRMSG_LEN]; 80 | bool verify_host; 81 | char verify_host_errmsg[TS_ERRMSG_LEN]; 82 | struct x509_cert x509[CERT_CHAIN_MAXLEN]; 83 | BIO *san; 84 | bool tls_ver_support[MAX_TLS_VERSION]; 85 | bool *cipher_suite_support; 86 | bool cipher1_3_suite_support[TLS1_3_MAX_CIPHER_COUNT]; // tls 1.3 ciphers 87 | SSL_SESSION *session; 88 | //unsigned char *ssl_session; 89 | // to keep track of cipher enum scan 90 | int reference_count; 91 | // to measure elapsed time 92 | struct timeval start_time; 93 | // aggregate time per host 94 | int elapsed_time_ms; 95 | }; 96 | 97 | void ts_tls_cert_BIO_free(struct tls_cert *tls_cert); 98 | 99 | void ts_tls_cert_reset(struct tls_cert *tls_cert); 100 | 101 | void ts_tls_cert_parse(SSL *ssl, struct tls_cert *tls_cert, FILE *fp, bool pretty); 102 | 103 | void ts_tls_print_json(struct tls_cert *tls_cert, FILE *fp, bool pretty); 104 | 105 | const char *get_ssl_version_str(int index); 106 | 107 | /* */ 108 | const SSL_METHOD *ts_tls_get_method(int index); 109 | long ts_tls_get_options(int index); 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | // Copyright (c) 2016, Yahoo Inc. 4 | // Copyrights licensed under the New BSD License. See the 5 | // accompanying LICENSE.txt file for terms. 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | // openssl headers 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #define DEFAULT_HOSTLEN 256 21 | #define DEFAULT_ALPNLEN 2048 22 | #define OPT_STRLEN 256 23 | #define OPT_CIPHER_STRLEN 4092 24 | #define OPT_CIPHERSUITES_STRLEN 512 25 | #define CIPHER_ENUM_SZ 256 26 | #define SE_OPENSSL 0 27 | #define SE_GNUTLS 1 28 | 29 | typedef enum { 30 | TS_ERROR = 0, 31 | TS_HOSTNAME, 32 | TS_IPV4, 33 | TS_IPV6, 34 | } ts_address_family_t; 35 | 36 | struct tls_cert; 37 | 38 | typedef struct stats { 39 | int hcount; 40 | int connect_count; 41 | int dns_errcount; 42 | int connect_err_count; 43 | int timeout_count; 44 | int tls_handshake; 45 | int gross_tls_handshake; 46 | int error_count; 47 | int completed_count; 48 | int network_err_count; 49 | int remote_close_count; 50 | int starttls_no_support_count; 51 | struct timeval start_time; 52 | } stats_t; 53 | 54 | void init_stats(stats_t *st); 55 | 56 | typedef enum { 57 | TS_SUCCESS = 0, 58 | TS_TIMEOUT, 59 | TS_DNS_ERR, 60 | TS_HSHAKE_ERR, 61 | TS_CONN_ERR, 62 | TS_SSL_CREAT_ERR, 63 | TS_EAGAIN_ERR, 64 | TS_UNKNOWN_ERR 65 | } ts_status_t; 66 | 67 | typedef enum { 68 | ST_UNKNOWN_TYPE = 0, 69 | ST_CERT, 70 | ST_SESSION_REUSE, 71 | ST_TLS_VERSION, 72 | ST_CIPHER, 73 | ST_HOST_PARALLEL, 74 | ST_GNUTLS_CERT, 75 | ST_GNUTLS_VERSION, 76 | ST_GNUTLS_CIPHER, 77 | ST_GNUTLS_1_2CHACHA_CIPHER, // TLS1.2 with CHACHA cipher 78 | ST_CERT_PRINT 79 | } scan_type_t; 80 | 81 | /* command line options, and related global consts */ 82 | typedef struct options { 83 | int protocol_adapter_index; 84 | char host[OPT_STRLEN]; 85 | uint16_t port; 86 | bool ssl2; 87 | bool ssl3; 88 | bool tls1; 89 | bool tls1_1; 90 | bool tls1_2; 91 | bool tls1_3; 92 | char cacert[OPT_STRLEN]; 93 | unsigned char alpn[DEFAULT_ALPNLEN]; 94 | size_t alpn_size; 95 | char sni[DEFAULT_HOSTLEN]; 96 | char ciphers[OPT_CIPHER_STRLEN]; 97 | bool cipher_user_input; // user provided cipher flag 98 | bool cipher_enum; 99 | char cipher_enum_list[CIPHER_ENUM_SZ][64]; 100 | int cipher_enum_count; 101 | char cipher_list[OPT_CIPHER_STRLEN]; // pre-TLS1.3 ciphers 102 | char ciphersuites[OPT_CIPHERSUITES_STRLEN]; // TLS1.3 ciphers 103 | char cipher1_3_enum_list[CIPHER_ENUM_SZ][64]; // gnutls tls 1.3 ciphers 104 | int cipher1_3_enum_count; // gnutls tls 1.3 scan cipher count 105 | bool show_unsupported_ciphers; 106 | bool tls_vers_enum; 107 | bool no_parallel_enum; 108 | bool session_reuse_test; 109 | bool session_print; 110 | char session_infile[OPT_STRLEN]; 111 | FILE *session_in_fp; 112 | char infile[OPT_STRLEN]; 113 | char outfile[OPT_STRLEN]; 114 | FILE *certlog_fp; 115 | char stats_outfile[OPT_STRLEN]; 116 | FILE *statsfile_fp; 117 | size_t batch_size; 118 | uint32_t timeout; 119 | struct timespec ts_sleep; 120 | bool json; 121 | bool pretty; 122 | bool stdout; 123 | int verbose; 124 | // if the input is IP 125 | bool ip_input; 126 | // GnuTLS 127 | gnutls_certificate_credentials_t xcred; 128 | // tls_cert objects 129 | struct tls_cert **cert_obj_pool; 130 | } options_t; 131 | 132 | /* connection object */ 133 | typedef struct client { 134 | int id; 135 | char host[OPT_STRLEN]; 136 | char ip[OPT_STRLEN]; 137 | uint16_t port; 138 | struct event_base *evbase; 139 | struct evdns_base *dnsbase; 140 | struct bufferevent *bev; 141 | struct bufferevent *temp_bev; 142 | //struct evutil_addrinfo *addr; 143 | // event status to determine error type 144 | ts_status_t event_status; 145 | SSL_CTX *ssl_ctx; 146 | // adapter index 147 | int adapter_index; 148 | // points to adaptor data structure 149 | void *adaptor_data_ptr; 150 | uint16_t timeout; 151 | // reference to global object 152 | const options_t *op; 153 | // reference to global object 154 | const stats_t *st; 155 | // tls session resuse test 156 | bool session_reuse_supported; 157 | // try few times to test session reuse 158 | int reuse_test_count; 159 | // current cipher index (for cipher enum) 160 | int cipher_index; 161 | // TLS version support test index 162 | int tls_ver_index; 163 | // tls 1.3+ cipher index 164 | int cipher1_3_index; 165 | // scan state 166 | scan_type_t state; 167 | // scan engine - openssl or gnutls 168 | uint8_t scan_engine; 169 | // GnuTLS 1.3 handshake event 170 | struct event *handshake1_3_ev; 171 | // GnuTLS 1.3 session 172 | gnutls_session_t session; 173 | struct tls_cert *tls_cert; 174 | } client_t; 175 | 176 | /* input file handle */ 177 | typedef struct input_handle { 178 | FILE *fp; 179 | char host[DEFAULT_HOSTLEN]; 180 | bool eof; 181 | } input_handle_t; 182 | 183 | extern input_handle_t in_handle; 184 | 185 | /* readline */ 186 | ssize_t ts_get_line_input(input_handle_t *in_handle, char *line, size_t len); 187 | 188 | /* elapsed time in seconds */ 189 | uint64_t ts_elapsed_time(struct timeval t1); 190 | 191 | /* returns ip str from socket fd */ 192 | void ts_get_ip(int fd, char *ipstr, size_t ipstr_len); 193 | 194 | /* stats struct object (implemented in main.c) */ 195 | extern stats_t *ts_get_stats_obj(); 196 | 197 | void ts_parse_connect_target(const char *target, char *host, size_t hlen, uint16_t *port); 198 | 199 | /* sniff the input to determine the input is hostname, ipv4 or ipv6 */ 200 | ts_address_family_t ts_address_family(const char *target); 201 | 202 | #endif 203 | -------------------------------------------------------------------------------- /include/proto-adapters.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTO_ts_adapterS_H 2 | #define PROTO_ts_adapterS_H 3 | 4 | // Copyright (c) 2016, Yahoo Inc. 5 | // Copyrights licensed under the New BSD License. See the 6 | // accompanying LICENSE.txt file for terms. 7 | 8 | #include 9 | 10 | /* returns protocol name if found, else return NULL */ 11 | const char *ts_protocol_name(int adapter_index); 12 | /* 13 | * returns ts_adapter table index for supported protocols 14 | * returns -1 if the proto_name is NOT found in the ts_adapters table 15 | */ 16 | int ts_adapter_index(const char *proto_name); 17 | 18 | void ts_adapter_create(client_t *cli); 19 | 20 | void ts_adapter_init(client_t *cli); 21 | 22 | 23 | void ts_adapter_connect(client_t *cli); 24 | 25 | void ts_adapter_read(client_t *cli, unsigned char *read_buf, size_t nread); 26 | 27 | void ts_adapter_write(client_t *cli); 28 | 29 | void ts_adapter_reset(client_t *cli); 30 | 31 | void ts_adapter_destroy(client_t *cli); 32 | 33 | #endif // PROTO_ts_adapterS_H 34 | -------------------------------------------------------------------------------- /man/tls-scan.1: -------------------------------------------------------------------------------- 1 | .\" Manpage for tls-scan. 2 | .TH man 1 "28 Dec 2019" "1.3.0" "tls-scan man page" 3 | .SH NAME 4 | tls-scan \- A fast TLS scanner 5 | .SH SYNOPSIS 6 | tls-scan [OPTION].. 7 | .SH DESCRIPTION 8 | A program to scan TLS based servers and collect x509 certificates, ciphers and related information. It produces results in JSON format. 9 | 10 | tls-scan is a single threaded asynchronous/event-based program (powered by libevent) capable of concurrently scan thousands of TLS servers. It can be combined with other tools such as GNU parallel to vertically scale in multi-core machines. 11 | 12 | The program is designed to support adding new protocols (starttls) easily without disturbing the existing structure. 13 | 14 | For more information, please visit: 15 | https://github.com/prbinu/tls-scan 16 | 17 | .SH OPTIONS 18 | 19 | -H --help 20 | Print a usage message briefly summarizing these command-line options and the bug-reporting address, then exit. 21 | 22 | -c --connect= 23 | target[:port] to scan. target = {hostname, IPv4, [IPv6] }. IPv6 example: [::1]:443 (default port 443). 24 | 25 | -h --host= 26 | Name of the host to scan. By passing an additional flag '--ip', the host value will be intepreted as an IP address. 27 | 28 | -p --port= [default: 443] 29 | Destination TCP port. 30 | 31 | -P --starttls= 32 | Supported protocols: smtp, imap, pop3, ftp, sieve, nntp, xmpp, ldap, rdp, postgres, mysql, tls (default) 33 | 34 | -c --cacert= 35 | Root CA file for certificate validation. By default the program attempts to load `ca-bundle.crt` file from current directory. 36 | 37 | -C --ciphers= 38 | Ciphers to use; try 'openssl ciphers' to see all ciphers. Note that this option will be overwritten by --ssl2, --ssl3, --tls1 --tls1_1, --tls1_2 options, if provided. 39 | 40 | Example: "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384" 41 | 42 | -e --cipher-enum 43 | Enumerate supported ciphers. Currently use --tls-old ciphers. Try --meta-info to find predefined cipher suite options. 44 | 45 | -V --version-enum 46 | Enumerate supported TLS versions. 47 | 48 | -v --version 49 | Print tls-scan version and build information. 50 | 51 | -r --session-reuse 52 | Enable ssl session reuse. 53 | 54 | -u --session-print 55 | Print SSL session in PEM format to stderr. This is currently not included in the JSON output, but print seperately. This flag woould be useful if you wanted to pass SSL session to --session-file to test session reuse. 56 | 57 | -T --session-file= 58 | File that contains SSL session in PEM format. 59 | 60 | -a --all 61 | Shortcut for --version-enum, --cipher-enum and --session-reuse options. This scan can take longer time to complete. Also note if the server employs some form of rate-limiting, your scan may fail. 62 | 63 | -s --sni= [default: hostname] 64 | Set TLS extension servername in ClientHello. Defaults to input hostname and applied to TLSv1+ only. 65 | 66 | -b --concurrency= [default: 1] 67 | Number of concurrent requests. The default is 1. This option specify the number of worker objects. Concurrency should be set based on your system capacity (memory, cpu, network) etc. 68 | 69 | -t --timeout= [default: 10] 70 | Timeout per connection (in seconds). Note that is is per connection and for cipher scans, tls-scan makes several connections to the same server. 71 | 72 | -S --sleep= [default: 0] 73 | Add milliseconds delay between the connection. Only for '--cipher-enum' and '--version-enum' options. Useful to manage server rate-limits. The max sleep value is 60000 (1 minute) 74 | 75 | -f --infile= 76 | Input file with domains or IPs. This is optional and by default the program accepts input from standard input (stdin). 77 | 78 | -o --outfile= 79 | Output file where the result in JSON format is stored. The default is standard output (stdout). 80 | 81 | -n --pretty 82 | Pretty print; add newline ('\\n') between record fields. 83 | 84 | -i --ip 85 | Treat input as IP address. The default is hostname. 86 | 87 | -N --nameserver= 88 | DNS resolver IPs to use and is an optional field. Multiple Namespace IP address can be passed. Format: -N -N -N .. In practice, DNS servers may have tight rate-limit in place. 89 | 90 | --ssl2 91 | Use only SSLv2 ciphers. 92 | 93 | --ssl3 94 | Use only SSLv3 ciphers. 95 | 96 | --tls1 97 | Use only TLSv1 ciphers. 98 | 99 | --tls1_1 100 | Use only TLSv1_1 ciphers. 101 | 102 | --tls1_2 103 | Use only TLSv1_2 ciphers. 104 | 105 | --tls-modern 106 | Mozilla's modern cipher list. See: https://wiki.mozilla.org/Security/Server_Side_TLS. 107 | 108 | --tls-interm 109 | Mozilla's intermediate cipher list. 110 | 111 | --tls-old 112 | Mozilla's old (backward compatible cipher list). 113 | 114 | --no-parallel-enum 115 | Disable parallel cipher and tls version enumeration. Parallel scan is performed only with '--host' option. 116 | 117 | --meta-info 118 | Print program meta information and exit. Useful if you wanted to see predefined cipher options. 119 | 120 | .SH EXAMPLES 121 | 122 | % tls-scan -c smtp.mail.yahoo.com:587 --starttls=smtp --cacert=ca-bundle.crt 2> /dev/null 123 | 124 | % tls-scan --infile=domains.txt --cacert=ca-bundle.crt 2> /dev/null 125 | 126 | % tls-scan -c [::1]:443 --cacert=ca-bundle.crt --pretty 2> /dev/null 127 | 128 | % tls-scan -c 10.10.10.10 --cacert=ca-bundle.crt --pretty 2> /dev/null 129 | 130 | % cat domains.txt | tls-scan --cacert=ca-bundle.crt 2> /dev/null 131 | 132 | % tls-scan --host= --session-print 2> /dev/null 133 | 134 | .SH SEE ALSO 135 | 136 | .SH BUGS 137 | 138 | Reporting Bugs: 139 | https://github.com/prbinu/tls-scan/issues/new 140 | 141 | Known Bugs: 142 | 1. Fail to print certificate information for SSLv2 handshake. 143 | 144 | .SH AUTHOR 145 | Binu Ramakrishnan, Yahoo Inc. 146 | 147 | .SH COPYRIGHT 148 | Copyright 2016-2017 Yahoo Inc. 149 | 150 | BSD-3 License: https://github.com/prbinu/tls-scan/blob/master/LICENSE.txt 151 | -------------------------------------------------------------------------------- /proto-adapters.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Yahoo Inc. 2 | // Copyrights licensed under the New BSD License. See the 3 | // accompanying LICENSE.txt file for terms. 4 | 5 | #include 6 | // openssl headers 7 | #include 8 | #include 9 | #include 10 | // tls-scan 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | /* Callback function definitions */ 17 | typedef void (*create_cb_fptr) (client_t *cli); 18 | typedef void (*init_cb_fptr) (client_t *cli); 19 | 20 | /* Required if client is the first party to send data after TCP connect. */ 21 | typedef void (*tcp_connect_cb_fptr) (client_t *cli); 22 | 23 | /* Called when data is ready to read */ 24 | typedef void (*read_cb_fptr) (client_t *cli, unsigned char *read_buf, 25 | size_t nread); 26 | /* 27 | * Called after a succuessful write, may not required in most cases 28 | */ 29 | typedef void (*write_cb_fptr) (client_t *cli); 30 | typedef void (*reset_cb_fptr) (client_t *cli); 31 | typedef void (*destroy_cb_fptr) (client_t *cli); 32 | 33 | /* Follow STEPS 1-2 to add a new protocol adapters */ 34 | 35 | /* STEP 1: Declare your protocol's callback functions here */ 36 | 37 | // shared functions 38 | static void on_create(client_t *cli); 39 | static void on_init(client_t *cli); 40 | static void on_reset(client_t *cli); 41 | static void on_destroy(client_t *cli); 42 | 43 | // smtp 44 | static void on_smtp_read(client_t *cli, unsigned char *read_buf, size_t nread); 45 | // imap 46 | static void on_imap_read(client_t *cli, unsigned char *read_buf, size_t nread); 47 | // pop3 48 | static void on_pop3_read(client_t *cli, unsigned char *read_buf, size_t nread); 49 | // ftps 50 | static void on_ftps_read(client_t *cli, unsigned char *read_buf, size_t nread); 51 | // sieve 52 | static void on_sieve_read(client_t *cli, unsigned char *read_buf, size_t nread); 53 | // nntp 54 | static void on_nntp_read(client_t *cli, unsigned char *read_buf, size_t nread); 55 | // ldap 56 | static void on_ldap_connect(client_t *cli); 57 | static void on_ldap_read(client_t *cli, unsigned char *read_buf, size_t nread); 58 | // rdp 59 | static void on_rdp_connect(client_t *cli); 60 | static void on_rdp_read(client_t *cli, unsigned char *read_buf, size_t nread); 61 | // xmpp 62 | static void on_xmpp_connect(client_t *cli); 63 | static void on_xmpp_read(client_t *cli, unsigned char *read_buf, size_t nread); 64 | // postgres 65 | static void on_postgres_connect(client_t *cli); 66 | static void on_postgres_read(client_t *cli, unsigned char *read_buf, size_t nread); 67 | // mysql 68 | static void on_mysql_read(client_t *cli, unsigned char *read_buf, size_t nread); 69 | // add new protocols below 70 | 71 | // 72 | typedef struct adapter_table { 73 | char protocol[32]; 74 | create_cb_fptr create_cb; 75 | init_cb_fptr init_cb; 76 | tcp_connect_cb_fptr connect_cb; 77 | read_cb_fptr read_cb; 78 | write_cb_fptr write_cb; 79 | reset_cb_fptr reset_cb; 80 | destroy_cb_fptr destroy_cb; 81 | } adapter_table_t; 82 | 83 | /* STEP 2 84 | * Add a new record to adapters struct below. 85 | * Format: { "protocol-name", create, init, connect, read, write, reset, destroy } 86 | * NULL if the callback is not implemented 87 | */ 88 | static const adapter_table_t adapters[] = { 89 | //{ "protocol-name", create, init, connect, read, write, reset, destroy } 90 | { "tls", NULL, NULL, NULL, NULL, NULL, NULL, NULL }, // implemented in main.c 91 | { "smtp", on_create, on_init, NULL, on_smtp_read, NULL, on_reset, on_destroy }, 92 | { "imap", on_create, on_init, NULL, on_imap_read, NULL, on_reset, on_destroy }, 93 | { "pop3", on_create, on_init, NULL, on_pop3_read, NULL, on_reset, on_destroy }, 94 | { "ftp", on_create, on_init, NULL, on_ftps_read, NULL, on_reset, on_destroy }, 95 | { "sieve", on_create, on_init, NULL, on_sieve_read, NULL, on_reset, on_destroy }, 96 | { "nntp", on_create, on_init, NULL, on_nntp_read, NULL, on_reset, on_destroy }, 97 | { "xmpp", on_create, on_init, on_xmpp_connect, on_xmpp_read, NULL, on_reset, on_destroy }, 98 | { "ldap", NULL, NULL, on_ldap_connect, on_ldap_read, NULL, NULL, NULL }, 99 | { "rdp", NULL, NULL, on_rdp_connect, on_rdp_read, NULL, NULL, NULL }, 100 | { "postgres", NULL, NULL, on_postgres_connect, on_postgres_read, NULL, NULL, NULL }, 101 | { "mysql", NULL, NULL, NULL, on_mysql_read, NULL, NULL, NULL } 102 | }; 103 | 104 | 105 | /* functions that are available for adapters */ 106 | /* TODO make it as callbacks? */ 107 | extern void ts_scan_error(client_t * cli); 108 | extern void ts_scan_do_tls_handshake(client_t * cli); 109 | extern void ts_scan_tcp_write(client_t * cli, const unsigned char *data, 110 | size_t data_len); 111 | extern void ts_scan_do_tls1_3_handshake(client_t *cli); 112 | 113 | const char *ts_protocol_name(int adapter_index) 114 | { 115 | size_t count = sizeof(adapters) / sizeof(adapters[0]); 116 | if ((adapter_index>= 0) && (adapter_index < count)) { 117 | return adapters[adapter_index].protocol; 118 | } 119 | 120 | return NULL; 121 | } 122 | 123 | int ts_adapter_index(const char *proto_name) 124 | { 125 | 126 | if (!proto_name) return -1; 127 | 128 | size_t count = sizeof(adapters) / sizeof(adapters[0]); 129 | for (size_t i = 0; i < count; i++) { 130 | 131 | if (strncmp(adapters[i].protocol, proto_name, 32) == 0) { 132 | return i; 133 | } 134 | 135 | } 136 | 137 | return -1; 138 | } 139 | 140 | void ts_adapter_create(client_t *cli) 141 | { 142 | if (adapters[cli->adapter_index].create_cb) { 143 | adapters[cli->adapter_index].create_cb(cli); 144 | } 145 | } 146 | 147 | void ts_adapter_init(client_t *cli) 148 | { 149 | if (adapters[cli->adapter_index].init_cb) { 150 | adapters[cli->adapter_index].init_cb(cli); 151 | } 152 | } 153 | 154 | void ts_adapter_connect(client_t *cli) 155 | { 156 | if (adapters[cli->adapter_index].connect_cb) { 157 | adapters[cli->adapter_index].connect_cb(cli); 158 | } 159 | } 160 | 161 | void ts_adapter_read(client_t *cli, 162 | unsigned char *read_buf, 163 | size_t nread) 164 | { 165 | if (adapters[cli->adapter_index].read_cb) { 166 | adapters[cli->adapter_index].read_cb(cli, read_buf, nread); 167 | } 168 | } 169 | 170 | void ts_adapter_write(client_t *cli) 171 | { 172 | if (adapters[cli->adapter_index].write_cb) { 173 | adapters[cli->adapter_index].write_cb(cli); 174 | } 175 | } 176 | 177 | void ts_adapter_reset(client_t *cli) 178 | { 179 | if (adapters[cli->adapter_index].reset_cb) { 180 | adapters[cli->adapter_index].reset_cb(cli); 181 | } 182 | } 183 | 184 | void ts_adapter_destroy(client_t *cli) 185 | { 186 | if (adapters[cli->adapter_index].destroy_cb) { 187 | adapters[cli->adapter_index].destroy_cb(cli); 188 | } 189 | } 190 | 191 | static void on_create(client_t *cli) 192 | { 193 | cli->adaptor_data_ptr = malloc(sizeof(int)); 194 | } 195 | 196 | static void on_init(client_t *cli) 197 | { 198 | *(int*)cli->adaptor_data_ptr = 0; 199 | } 200 | 201 | static void on_reset(client_t *cli) 202 | { 203 | *(int*)cli->adaptor_data_ptr = 0; 204 | } 205 | 206 | static void on_destroy(client_t *cli) 207 | { 208 | if (cli->adaptor_data_ptr) { 209 | free(cli->adaptor_data_ptr); 210 | cli->adaptor_data_ptr = 0; 211 | } 212 | } 213 | 214 | void on_smtp_read(client_t * cli, unsigned char *rbuffer, size_t rbuf_len) 215 | { 216 | const char *starttls = "STARTTLS\r\n"; 217 | unsigned char *rbuf = rbuffer; 218 | int *resp_index = (int*)cli->adaptor_data_ptr; 219 | char ehlo[128]; 220 | 221 | if (rbuf_len < 1) { 222 | goto error; 223 | } 224 | 225 | // printf("%.*s+ resp_index: %d\n", (int)rbuf_len, rbuffer, *resp_index); 226 | 227 | switch (*resp_index) { 228 | case 0: 229 | (*resp_index)++; 230 | 231 | if (strncmp((char*)rbuf, "220", 3) != 0) { 232 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", cli->host, cli->ip, 233 | "ERROR: NON 220 response"); 234 | ts_get_stats_obj()->starttls_no_support_count++; 235 | goto error; 236 | } 237 | 238 | snprintf(ehlo, 128, "EHLO %s\r\n", cli->host); 239 | ts_scan_tcp_write(cli, (unsigned char*)ehlo, strlen(ehlo)); 240 | break; 241 | 242 | case 1: 243 | { 244 | (*resp_index)++; 245 | char *saveptr; 246 | char* line = NULL; 247 | bool found = false; 248 | line = strtok_r((char*)rbuf, "\r\n", &saveptr); 249 | 250 | do { 251 | if ((strncmp((char*)line, "250", 3) == 0) && 252 | (strstr(line, "STARTTLS"))) { 253 | found = true; 254 | break; 255 | } 256 | 257 | } while ((line = strtok_r(NULL, "\r\n", &saveptr)) != NULL); 258 | 259 | if (!found) { 260 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", 261 | cli->host, cli->ip, "STARTTLS not supported"); 262 | ts_get_stats_obj()->starttls_no_support_count++; 263 | goto error; 264 | } 265 | 266 | ts_scan_tcp_write(cli, (unsigned char*)starttls, strlen(starttls)); 267 | break; 268 | } 269 | 270 | case 2: 271 | (*resp_index)++; 272 | if (cli->scan_engine == SE_GNUTLS) { 273 | ts_scan_do_tls1_3_handshake(cli); 274 | } else { 275 | ts_scan_do_tls_handshake(cli); 276 | } 277 | break; 278 | 279 | default: 280 | break; 281 | } 282 | 283 | return; 284 | 285 | error: 286 | ts_scan_error(cli); 287 | return; 288 | } 289 | 290 | // mysql 291 | uint8_t ssl_handshake_response[] = { 292 | 0x20, 0x00, 0x00, 0x01, 0x05, 293 | 0xae, 0x0f, 0x00, 0x00, 0x00, 294 | 0x00, 0x01, 0x21, 0x00, 0x00, 295 | 0x00, 0x00, 0x00, 0x00, 0x00, 296 | 0x00, 0x00, 0x00, 0x00, 0x00, 297 | 0x00, 0x00, 0x00, 0x00, 0x00, 298 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 299 | }; 300 | 301 | /* 302 | 303 | MySQL Packet: 304 | https://dev.mysql.com/doc/internals/en/mysql-packet.html 305 | 306 | Protocol::HandshakeV10 307 | https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake 308 | 309 | Protocol::SSLRequest: (response from client before SSL Handshake) 310 | 311 | Payload 312 | 313 | 4 capability flags, CLIENT_SSL always set 314 | 4 max-packet size 315 | 1 character set 316 | string[23] reserved (all [0]) 317 | 318 | */ 319 | 320 | #define MYSQL_PKT_HDR_SIZE 4 321 | void on_mysql_read(client_t * cli, unsigned char *rbuffer, size_t rbuf_len) 322 | { 323 | uint8_t *rbuf = (uint8_t*)rbuffer; 324 | 325 | if (rbuf_len < 1) { 326 | goto error; 327 | } 328 | 329 | /* debug? 330 | for (int i=0; i < rbuf_len; i++) { 331 | printf("%0x ", rbuf[i]); 332 | }*/ 333 | 334 | int index = strlen((char*)&rbuf[MYSQL_PKT_HDR_SIZE+1])+6; 335 | index = index + sizeof(uint32_t); 336 | index = index + strlen((char*)&rbuf[index]+1); 337 | 338 | if (0x0008 & (int16_t)(rbuf[index+3])) { 339 | ts_scan_tcp_write(cli, ssl_handshake_response, 340 | sizeof(ssl_handshake_response)); 341 | 342 | if (cli->scan_engine == SE_GNUTLS) { 343 | ts_scan_do_tls1_3_handshake(cli); 344 | } else { 345 | ts_scan_do_tls_handshake(cli); 346 | } 347 | 348 | } else { 349 | fprintf(stderr, "host: %s; ip: %s; error: SSL NOT Supported\n", 350 | cli->ip, cli->host); 351 | ts_get_stats_obj()->starttls_no_support_count++; 352 | goto error; 353 | } 354 | 355 | return; 356 | 357 | error: 358 | ts_scan_error(cli); 359 | return; 360 | } 361 | 362 | void on_imap_read(client_t *cli, unsigned char *rbuffer, size_t rbuf_len) 363 | { 364 | unsigned char *rbuf = rbuffer; 365 | int *resp_index = (int*)cli->adaptor_data_ptr; 366 | 367 | if (rbuf_len < 1) { 368 | goto error; 369 | } 370 | 371 | switch (*resp_index) { 372 | case 0: 373 | (*resp_index)++; 374 | const char *p = "a CAPABILITY\r\n"; 375 | if (strncmp((char*)rbuf, "* OK", 4) == 0) { 376 | ts_scan_tcp_write(cli, (unsigned char*)p, strlen(p)); 377 | } else { 378 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", 379 | cli->host, cli->ip, "STARTTLS 1 not supported"); 380 | ts_get_stats_obj()->starttls_no_support_count++; 381 | goto error; 382 | } 383 | 384 | break; 385 | case 1: 386 | { 387 | (*resp_index)++; 388 | char *saveptr; 389 | char* line = NULL; 390 | bool found = false; 391 | line = strtok_r((char*)rbuf, "\r\n", &saveptr); 392 | 393 | do { 394 | if (strstr(line, "STARTTLS")) { 395 | found = true; 396 | break; 397 | } 398 | 399 | } while ((line = strtok_r(NULL, "\r\n", &saveptr)) != NULL); 400 | 401 | if (!found) { 402 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", 403 | cli->host, cli->ip, "STARTTLS not supported"); 404 | ts_get_stats_obj()->starttls_no_support_count++; 405 | goto error; 406 | } 407 | 408 | const char *p1 = "a STARTTLS\r\n"; 409 | ts_scan_tcp_write(cli, (unsigned char *)p1, strlen(p1)); 410 | break; 411 | } 412 | 413 | case 2: 414 | (*resp_index)++; 415 | if (strncmp((char*)rbuf, "a OK", 4) == 0) { 416 | if (cli->scan_engine == SE_GNUTLS) { 417 | ts_scan_do_tls1_3_handshake(cli); 418 | } else { 419 | ts_scan_do_tls_handshake(cli); 420 | } 421 | } else { 422 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", 423 | cli->host, cli->ip, "STARTTLS 2 not supported"); 424 | ts_get_stats_obj()->starttls_no_support_count++; 425 | goto error; 426 | } 427 | break; 428 | 429 | default: 430 | break; 431 | } 432 | 433 | return; 434 | 435 | error: 436 | ts_scan_error(cli); 437 | return; 438 | } 439 | 440 | void on_pop3_read(client_t *cli, unsigned char *rbuffer, size_t rbuf_len) 441 | { 442 | unsigned char *rbuf = rbuffer; 443 | int *resp_index = (int*)cli->adaptor_data_ptr; 444 | 445 | if (rbuf_len < 1) { 446 | goto error; 447 | } 448 | 449 | //printf("%.*s+ resp_index: %d\n", (int)rbuf_len, rbuffer, *resp_index); 450 | 451 | switch (*resp_index) { 452 | case 0: 453 | (*resp_index)++; 454 | const char *p = "STLS\r\n"; 455 | if (strncmp((char*)rbuf, "+OK", 3) == 0) { 456 | ts_scan_tcp_write(cli, (unsigned char*)p, strlen(p)); 457 | } else { 458 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", 459 | cli->host, cli->ip, "STARTTLS 1 not supported"); 460 | ts_get_stats_obj()->starttls_no_support_count++; 461 | goto error; 462 | } 463 | 464 | break; 465 | case 1: 466 | (*resp_index)++; 467 | if (strncmp((char*)rbuf, "+OK", 3) == 0) { 468 | if (cli->scan_engine == SE_GNUTLS) { 469 | ts_scan_do_tls1_3_handshake(cli); 470 | } else { 471 | ts_scan_do_tls_handshake(cli); 472 | } 473 | } else { 474 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", 475 | cli->host, cli->ip, "STARTTLS 2 not supported"); 476 | ts_get_stats_obj()->starttls_no_support_count++; 477 | goto error; 478 | } 479 | break; 480 | 481 | default: 482 | break; 483 | } 484 | 485 | return; 486 | 487 | error: 488 | ts_scan_error(cli); 489 | return; 490 | } 491 | 492 | void on_ftps_read(client_t * cli, unsigned char *rbuffer, size_t rbuf_len) 493 | { 494 | unsigned char *rbuf = rbuffer; 495 | int *resp_index = (int*)cli->adaptor_data_ptr; 496 | 497 | if (rbuf_len < 1) { 498 | goto error; 499 | } 500 | 501 | //printf("%.*s+ resp_index: %d\n", (int)rbuf_len, rbuffer, *resp_index); 502 | 503 | switch (*resp_index) { 504 | case 0: 505 | (*resp_index)++; 506 | 507 | if (strncmp((char*)rbuf, "220", 3) != 0) { 508 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", cli->host, cli->ip, 509 | "ERROR: NON 220 response"); 510 | ts_get_stats_obj()->starttls_no_support_count++; 511 | goto error; 512 | } 513 | 514 | const char *p = "FEAT\r\n"; 515 | ts_scan_tcp_write(cli, (unsigned char*)p, strlen(p)); 516 | break; 517 | 518 | case 1: 519 | { 520 | char *saveptr; 521 | char* line = NULL; 522 | line = strtok_r((char*)rbuf, "\r\n", &saveptr); 523 | 524 | do { 525 | if (strncmp((char*)line, "211 ", 4) == 0) { 526 | (*resp_index)++; 527 | const char *p1 = "AUTH TLS\r\n"; 528 | ts_scan_tcp_write(cli, (unsigned char*)p1, strlen(p1)); 529 | break; 530 | } 531 | } while ((line = strtok_r(NULL, "\r\n", &saveptr)) != NULL); 532 | 533 | break; 534 | } 535 | 536 | case 2: 537 | (*resp_index)++; 538 | 539 | if (strncmp((char*)rbuf, "234", 3) != 0) { 540 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", 541 | cli->host, cli->ip, "STARTTLS not supported"); 542 | ts_get_stats_obj()->starttls_no_support_count++; 543 | goto error; 544 | } 545 | 546 | if (cli->scan_engine == SE_GNUTLS) { 547 | ts_scan_do_tls1_3_handshake(cli); 548 | } else { 549 | ts_scan_do_tls_handshake(cli); 550 | } 551 | break; 552 | 553 | default: 554 | break; 555 | } 556 | 557 | return; 558 | 559 | error: 560 | ts_scan_error(cli); 561 | return; 562 | } 563 | 564 | void on_sieve_read(client_t *cli, unsigned char *rbuffer, size_t rbuf_len) 565 | { 566 | unsigned char *rbuf = rbuffer; 567 | int *resp_index = (int*)cli->adaptor_data_ptr; 568 | 569 | if (rbuf_len < 1) { 570 | goto error; 571 | } 572 | 573 | //printf("%.*s+ resp_index: %d\n", (int)rbuf_len, rbuffer, *resp_index); 574 | 575 | switch (*resp_index) { 576 | case 0: 577 | { 578 | char *saveptr; 579 | char* line = NULL; 580 | line = strtok_r((char*)rbuf, "\r\n", &saveptr); 581 | 582 | do { 583 | if (strncmp((char*)line, "OK ", 3) == 0) { 584 | (*resp_index)++; 585 | const char *p = "STARTTLS\r\n"; 586 | ts_scan_tcp_write(cli, (unsigned char*)p, strlen(p)); 587 | break; 588 | } 589 | } while ((line = strtok_r(NULL, "\r\n", &saveptr)) != NULL); 590 | 591 | break; 592 | } 593 | case 1: 594 | (*resp_index)++; 595 | 596 | if (strncmp((char*)rbuf, "OK ", 3) != 0) { 597 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", 598 | cli->host, cli->ip, "STARTTLS not supported"); 599 | ts_get_stats_obj()->starttls_no_support_count++; 600 | goto error; 601 | } 602 | 603 | if (cli->scan_engine == SE_GNUTLS) { 604 | ts_scan_do_tls1_3_handshake(cli); 605 | } else { 606 | ts_scan_do_tls_handshake(cli); 607 | } 608 | break; 609 | 610 | default: 611 | break; 612 | } 613 | 614 | return; 615 | 616 | error: 617 | ts_scan_error(cli); 618 | return; 619 | } 620 | 621 | 622 | void on_nntp_read(client_t *cli, unsigned char *rbuffer, size_t rbuf_len) 623 | { 624 | unsigned char *rbuf = rbuffer; 625 | int *resp_index = (int*)cli->adaptor_data_ptr; 626 | 627 | if (rbuf_len < 1) { 628 | goto error; 629 | } 630 | 631 | //printf("%.*s+ resp_index: %d\n", (int)rbuf_len, rbuffer, *resp_index); 632 | 633 | switch (*resp_index) { 634 | case 0: 635 | (*resp_index)++; 636 | 637 | if (strncmp((char*)rbuf, "200 ", 4) != 0) { 638 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", cli->host, cli->ip, 639 | "ERROR: NON 200 response"); 640 | ts_get_stats_obj()->starttls_no_support_count++; 641 | goto error; 642 | } 643 | 644 | const char *p = "STARTTLS\r\n"; 645 | ts_scan_tcp_write(cli, (unsigned char*)p, strlen(p)); 646 | break; 647 | 648 | case 1: 649 | (*resp_index)++; 650 | 651 | if (strncmp((char*)rbuf, "382 ", 3) != 0) { 652 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", 653 | cli->host, cli->ip, "STARTTLS not supported"); 654 | ts_get_stats_obj()->starttls_no_support_count++; 655 | goto error; 656 | } 657 | 658 | if (cli->scan_engine == SE_GNUTLS) { 659 | ts_scan_do_tls1_3_handshake(cli); 660 | } else { 661 | ts_scan_do_tls_handshake(cli); 662 | } 663 | break; 664 | 665 | default: 666 | break; 667 | } 668 | 669 | return; 670 | 671 | error: 672 | ts_scan_error(cli); 673 | return; 674 | } 675 | 676 | void on_xmpp_connect(client_t *cli) 677 | { 678 | char buf[512]; 679 | char *host = cli->host; 680 | if (cli->host[0] != 0) { 681 | host = cli->ip; 682 | } 683 | 684 | snprintf(buf, sizeof(buf), "\n", host); 685 | ts_scan_tcp_write(cli, ( unsigned char *)buf, strlen(buf)); 686 | } 687 | 688 | void on_xmpp_read(client_t *cli, unsigned char *rbuffer, size_t rbuf_len) 689 | { 690 | unsigned char *rbuf = rbuffer; 691 | int *resp_index = (int*)cli->adaptor_data_ptr; 692 | 693 | if (rbuf_len < 1) { 694 | goto error; 695 | } 696 | 697 | //printf("%.*s+ resp_index: %d\n", (int)rbuf_len, rbuffer, *resp_index); 698 | const char *p = "\n"; 699 | switch (*resp_index) { 700 | case 0: 701 | if (strstr((char*)rbuf, "")) { 702 | (*resp_index)++; 703 | if (strstr((char*)rbuf, "host, cli->ip, "STARTTLS 1 not supported"); 708 | ts_get_stats_obj()->starttls_no_support_count++; 709 | goto error; 710 | } 711 | } 712 | 713 | break; 714 | case 1: 715 | (*resp_index)++; 716 | 717 | if (strncmp((char*)rbuf, "host, cli->ip, "STARTTLS not supported"); 720 | ts_get_stats_obj()->starttls_no_support_count++; 721 | goto error; 722 | } 723 | 724 | if (cli->scan_engine == SE_GNUTLS) { 725 | ts_scan_do_tls1_3_handshake(cli); 726 | } else { 727 | ts_scan_do_tls_handshake(cli); 728 | } 729 | break; 730 | 731 | default: 732 | break; 733 | } 734 | 735 | return; 736 | 737 | error: 738 | ts_scan_error(cli); 739 | return; 740 | } 741 | 742 | void on_ldap_connect(client_t *cli) 743 | { 744 | uint8_t ldap_str[] = { 745 | 0x30, 0x1d, 0x02, 0x01, 0x01, 0x77, 746 | 0x18, 0x80, 0x16, 0x31, 0x2e, 0x33, 747 | 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 748 | 0x2e, 0x31, 0x2e, 0x31, 0x34, 0x36, 749 | 0x36, 0x2e, 0x32, 0x30, 0x30, 0x33, 0x37 750 | }; 751 | ts_scan_tcp_write(cli, ldap_str, sizeof(ldap_str)); 752 | } 753 | 754 | void on_ldap_read(client_t *cli, unsigned char *rbuffer, size_t rbuf_len) 755 | { 756 | if (cli->scan_engine == SE_GNUTLS) { 757 | ts_scan_do_tls1_3_handshake(cli); 758 | } else { 759 | ts_scan_do_tls_handshake(cli); 760 | } 761 | } 762 | 763 | void on_rdp_connect(client_t *cli) 764 | { 765 | uint8_t rdp_str[] = { 766 | 0x03, 0x00, 0x00, 0x13, 0x0E, 0xE0, 767 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 768 | 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00 769 | }; 770 | 771 | ts_scan_tcp_write(cli, rdp_str, sizeof(rdp_str)); 772 | } 773 | 774 | void on_rdp_read(client_t *cli, unsigned char *rbuffer, size_t rbuf_len) 775 | { 776 | //printf("%.*s+ resp_index: %d\n", (int)rbuf_len, rbuffer); 777 | if (rbuf_len < 2) { 778 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", 779 | cli->host, cli->ip, "STARTTLS not supported"); 780 | ts_get_stats_obj()->starttls_no_support_count++; 781 | goto error; 782 | } 783 | 784 | if ((rbuffer[0] != 0x03) || (rbuffer[1] != 0x00)) { 785 | fprintf(stderr, "host: %s; ip: %s; error: %s\n", 786 | cli->host, cli->ip, "STARTTLS 2 not supported"); 787 | ts_get_stats_obj()->starttls_no_support_count++; 788 | goto error; 789 | } 790 | 791 | if (cli->scan_engine == SE_GNUTLS) { 792 | ts_scan_do_tls1_3_handshake(cli); 793 | } else { 794 | ts_scan_do_tls_handshake(cli); 795 | } 796 | 797 | return; 798 | 799 | error: 800 | ts_scan_error(cli); 801 | return; 802 | } 803 | 804 | void on_postgres_connect(client_t *cli) 805 | { 806 | uint8_t postgres_str[] = { 0x00, 0x00, 0x00, 0x08, 0x04, 0xD2, 0x16, 0x2F }; 807 | ts_scan_tcp_write(cli, postgres_str, sizeof(postgres_str)); 808 | } 809 | 810 | void on_postgres_read(client_t *cli, unsigned char *rbuffer, size_t rbuf_len) 811 | { 812 | if (cli->scan_engine == SE_GNUTLS) { 813 | ts_scan_do_tls1_3_handshake(cli); 814 | } else { 815 | ts_scan_do_tls_handshake(cli); 816 | } 817 | } 818 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Testing `tls-scan` 2 | 3 | ## Generate Self-signed TLS X.509 Certificates 4 | 5 | ```bash 6 | # RSA certs 7 | openssl req -new -newkey rsa:2048 -x509 -sha256 -days 3650 -nodes -out rsa-test.crt -keyout rsa-test.key 8 | 9 | # ECDSA certs 10 | openssl req -x509 -nodes -days 3650 -newkey ec:<(openssl ecparam -name prime256v1) -keyout ecdsa-test.key -out ecdsa-test.crt 11 | 12 | # DSS/DSA certs 13 | ./build-root/bin/openssl req -x509 -new -days 3650 -key dsa-test.key -out dsa-test.crt 14 | 15 | ``` 16 | 17 | ## Running Tests 18 | 19 | Export all ciphersuites: 20 | 21 | ```bash 22 | export CIPHERS="ECDHE-ECDSA-CHACHA20-POLY1305-OLD:ECDHE-RSA-CHACHA20-POLY1305-OLD:DHE-RSA-CHACHA20-POLY1305-OLD:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DH-DSS-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DH-RSA-AES256-SHA256:DH-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DH-RSA-AES256-SHA:DH-DSS-AES256-SHA:ECDHE-RSA-CAMELLIA256-SHA384:ECDHE-ECDSA-CAMELLIA256-SHA384:DHE-RSA-CAMELLIA256-SHA256:DHE-DSS-CAMELLIA256-SHA256:DH-RSA-CAMELLIA256-SHA256:DH-DSS-CAMELLIA256-SHA256:DHE-RSA-CAMELLIA256-SHA:DHE-DSS-CAMELLIA256-SHA:DH-RSA-CAMELLIA256-SHA:DH-DSS-CAMELLIA256-SHA:AECDH-AES256-SHA:ADH-AES256-GCM-SHA384:ADH-AES256-SHA256:ADH-AES256-SHA:ADH-CAMELLIA256-SHA256:ADH-CAMELLIA256-SHA:ECDH-RSA-AES256-GCM-SHA384:ECDH-ECDSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA384:ECDH-ECDSA-AES256-SHA384:ECDH-RSA-AES256-SHA:ECDH-ECDSA-AES256-SHA:ECDH-RSA-CAMELLIA256-SHA384:ECDH-ECDSA-CAMELLIA256-SHA384:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:CAMELLIA256-SHA256:CAMELLIA256-SHA:RSA-PSK-AES256-CBC-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:DH-DSS-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DH-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DH-RSA-AES128-SHA256:DH-DSS-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:DH-RSA-AES128-SHA:DH-DSS-AES128-SHA:ECDHE-RSA-CAMELLIA128-SHA256:ECDHE-ECDSA-CAMELLIA128-SHA256:DHE-RSA-CAMELLIA128-SHA256:DHE-DSS-CAMELLIA128-SHA256:DH-RSA-CAMELLIA128-SHA256:DH-DSS-CAMELLIA128-SHA256:DHE-RSA-SEED-SHA:DHE-DSS-SEED-SHA:DH-RSA-SEED-SHA:DH-DSS-SEED-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-DSS-CAMELLIA128-SHA:DH-RSA-CAMELLIA128-SHA:DH-DSS-CAMELLIA128-SHA:AECDH-AES128-SHA:ADH-AES128-GCM-SHA256:ADH-AES128-SHA256:ADH-AES128-SHA:ADH-CAMELLIA128-SHA256:ADH-SEED-SHA:ADH-CAMELLIA128-SHA:ECDH-RSA-AES128-GCM-SHA256:ECDH-ECDSA-AES128-GCM-SHA256:ECDH-RSA-AES128-SHA256:ECDH-ECDSA-AES128-SHA256:ECDH-RSA-AES128-SHA:ECDH-ECDSA-AES128-SHA:ECDH-RSA-CAMELLIA128-SHA256:ECDH-ECDSA-CAMELLIA128-SHA256:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:CAMELLIA128-SHA256:SEED-SHA:CAMELLIA128-SHA:IDEA-CBC-SHA:IDEA-CBC-MD5:RC2-CBC-MD5:RSA-PSK-AES128-CBC-SHA:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:DHE-DSS-RC4-SHA:AECDH-RC4-SHA:ADH-RC4-MD5:ECDH-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:RC4-SHA:RC4-MD5:RC4-MD5:RSA-PSK-RC4-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DH-RSA-DES-CBC3-SHA:DH-DSS-DES-CBC3-SHA:AECDH-DES-CBC3-SHA:ADH-DES-CBC3-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-ECDSA-DES-CBC3-SHA:DES-CBC3-SHA:DES-CBC3-MD5:RSA-PSK-3DES-EDE-CBC-SHA:RC4-64-MD5:EXP1024-DHE-DSS-DES-CBC-SHA:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DH-RSA-DES-CBC-SHA:DH-DSS-DES-CBC-SHA:ADH-DES-CBC-SHA:EXP1024-DES-CBC-SHA:DES-CBC-SHA:EXP1024-RC2-CBC-MD5:DES-CBC-MD5:EXP1024-DHE-DSS-RC4-SHA:EXP1024-RC4-SHA:EXP1024-RC4-MD5:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA:EXP-DH-RSA-DES-CBC-SHA:EXP-DH-DSS-DES-CBC-SHA:EXP-ADH-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC2-CBC-MD5:EXP-ADH-RC4-MD5:EXP-RC4-MD5:EXP-RC4-MD5:AECDH-NULL-SHA:ECDHE-RSA-NULL-SHA:ECDHE-ECDSA-NULL-SHA:ECDH-RSA-NULL-SHA:ECDH-ECDSA-NULL-SHA:NULL-SHA256:NULL-SHA:NULL-MD5:NULL-MD5" 23 | ``` 24 | ### With RSA Certificate 25 | 26 | ```bash 27 | ./build-root/bin/openssl s_server -accept 4443 -cert test/rsa-test.crt -key test/rsa-test.key -cipher "${CIPHERS}" 28 | 29 | ``` 30 | 31 | ### Test `tls-scan` 32 | 33 | ```bash 34 | tls-scan -c localhost:4443 --pretty --all --no-parallel-enum --show-unsupported-ciphers --ciphers="${CIPHERS}" 35 | ``` 36 | 37 | ### With ECDSA Certificate 38 | 39 | ```bash 40 | ./build-root/bin/openssl s_server -accept 4443 -cert test/ecdsa-test.crt -key test/ecdsa-test.key -cipher "${CIPHERS}" 41 | 42 | ``` 43 | 44 | ### With DSA Certificate 45 | 46 | ```bash 47 | ./build-root/bin/openssl s_server -accept 4443 -cert test/dsa-test.crt -key test/dsa-test.key -cipher "${CIPHERS}" 48 | 49 | ``` 50 | 51 | The above examples use PeterMosmans [`openssl-1.0.2`](https://github.com/PeterMosmans/openssl) binary, but you can also use a new openssl version. The output of the above scans are uploaded to the `tls-scan/test` directory. 52 | 53 | ### GNUTLS Server Example 54 | 55 | ```bash 56 | ./build-root/bin/gnutls-serv --x509certfile=test/ecdsa-test.crt --x509keyfile=test/ecdsa-test.key -p 4443 57 | 58 | ``` 59 | -------------------------------------------------------------------------------- /tests/dsa-scan.out: -------------------------------------------------------------------------------- 1 | { 2 | "host": "localhost", 3 | "ip": "127.0.0.1", 4 | "port": 4443, 5 | "elapsedTime": 517, 6 | "tlsVersion": "TLSv1.2", 7 | "cipher": "DHE-DSS-AES256-GCM-SHA384 TLSv1.2 Kx=DH Au=DSS Enc=AESGCM(256) Mac=AEAD", 8 | "tempPublicKeyAlg": "DH", 9 | "tempPublicKeySize": 2048, 10 | "secureRenego": true, 11 | "compression": "zlib compression", 12 | "expansion": "zlib compression", 13 | "sessionLifetimeHint": 300, 14 | "tlsVersions": [ 15 | "SSLv3", 16 | "TLSv1", 17 | "TLSv1_1", 18 | "TLSv1_2" 19 | ], 20 | "cipherSuite": { 21 | "supported": [ 22 | "DHE-DSS-AES256-GCM-SHA384", 23 | "DHE-DSS-AES256-SHA256", 24 | "DHE-DSS-AES256-SHA", 25 | "DHE-DSS-CAMELLIA256-SHA256", 26 | "DHE-DSS-CAMELLIA256-SHA", 27 | "AECDH-AES256-SHA", 28 | "ADH-AES256-GCM-SHA384", 29 | "ADH-AES256-SHA256", 30 | "ADH-AES256-SHA", 31 | "ADH-CAMELLIA256-SHA256", 32 | "ADH-CAMELLIA256-SHA", 33 | "DHE-DSS-AES128-GCM-SHA256", 34 | "DHE-DSS-AES128-SHA256", 35 | "DHE-DSS-AES128-SHA", 36 | "DHE-DSS-CAMELLIA128-SHA256", 37 | "DHE-DSS-SEED-SHA", 38 | "DHE-DSS-CAMELLIA128-SHA", 39 | "AECDH-AES128-SHA", 40 | "ADH-AES128-GCM-SHA256", 41 | "ADH-AES128-SHA256", 42 | "ADH-AES128-SHA", 43 | "ADH-CAMELLIA128-SHA256", 44 | "ADH-SEED-SHA", 45 | "ADH-CAMELLIA128-SHA", 46 | "DHE-DSS-RC4-SHA", 47 | "AECDH-RC4-SHA", 48 | "ADH-RC4-MD5", 49 | "EDH-DSS-DES-CBC3-SHA", 50 | "AECDH-DES-CBC3-SHA", 51 | "ADH-DES-CBC3-SHA", 52 | "EDH-DSS-DES-CBC-SHA", 53 | "ADH-DES-CBC-SHA", 54 | "AECDH-NULL-SHA" 55 | ], 56 | "notSupported": [ 57 | "ECDHE-ECDSA-CHACHA20-POLY1305-OLD", 58 | "ECDHE-RSA-CHACHA20-POLY1305-OLD", 59 | "DHE-RSA-CHACHA20-POLY1305-OLD", 60 | "ECDHE-RSA-AES256-GCM-SHA384", 61 | "ECDHE-ECDSA-AES256-GCM-SHA384", 62 | "ECDHE-RSA-AES256-SHA384", 63 | "ECDHE-ECDSA-AES256-SHA384", 64 | "ECDHE-RSA-AES256-SHA", 65 | "ECDHE-ECDSA-AES256-SHA", 66 | "DH-DSS-AES256-GCM-SHA384", 67 | "DH-RSA-AES256-GCM-SHA384", 68 | "DHE-RSA-AES256-GCM-SHA384", 69 | "DHE-RSA-AES256-SHA256", 70 | "DH-RSA-AES256-SHA256", 71 | "DH-DSS-AES256-SHA256", 72 | "DHE-RSA-AES256-SHA", 73 | "DH-RSA-AES256-SHA", 74 | "DH-DSS-AES256-SHA", 75 | "ECDHE-RSA-CAMELLIA256-SHA384", 76 | "ECDHE-ECDSA-CAMELLIA256-SHA384", 77 | "DHE-RSA-CAMELLIA256-SHA256", 78 | "DH-RSA-CAMELLIA256-SHA256", 79 | "DH-DSS-CAMELLIA256-SHA256", 80 | "DHE-RSA-CAMELLIA256-SHA", 81 | "DH-RSA-CAMELLIA256-SHA", 82 | "DH-DSS-CAMELLIA256-SHA", 83 | "ECDH-RSA-AES256-GCM-SHA384", 84 | "ECDH-ECDSA-AES256-GCM-SHA384", 85 | "ECDH-RSA-AES256-SHA384", 86 | "ECDH-ECDSA-AES256-SHA384", 87 | "ECDH-RSA-AES256-SHA", 88 | "ECDH-ECDSA-AES256-SHA", 89 | "ECDH-RSA-CAMELLIA256-SHA384", 90 | "ECDH-ECDSA-CAMELLIA256-SHA384", 91 | "AES256-GCM-SHA384", 92 | "AES256-SHA256", 93 | "AES256-SHA", 94 | "CAMELLIA256-SHA256", 95 | "CAMELLIA256-SHA", 96 | "RSA-PSK-AES256-CBC-SHA", 97 | "ECDHE-RSA-AES128-GCM-SHA256", 98 | "ECDHE-ECDSA-AES128-GCM-SHA256", 99 | "ECDHE-RSA-AES128-SHA256", 100 | "ECDHE-ECDSA-AES128-SHA256", 101 | "ECDHE-RSA-AES128-SHA", 102 | "ECDHE-ECDSA-AES128-SHA", 103 | "DH-DSS-AES128-GCM-SHA256", 104 | "DH-RSA-AES128-GCM-SHA256", 105 | "DHE-RSA-AES128-GCM-SHA256", 106 | "DHE-RSA-AES128-SHA256", 107 | "DH-RSA-AES128-SHA256", 108 | "DH-DSS-AES128-SHA256", 109 | "DHE-RSA-AES128-SHA", 110 | "DH-RSA-AES128-SHA", 111 | "DH-DSS-AES128-SHA", 112 | "ECDHE-RSA-CAMELLIA128-SHA256", 113 | "ECDHE-ECDSA-CAMELLIA128-SHA256", 114 | "DHE-RSA-CAMELLIA128-SHA256", 115 | "DH-RSA-CAMELLIA128-SHA256", 116 | "DH-DSS-CAMELLIA128-SHA256", 117 | "DHE-RSA-SEED-SHA", 118 | "DH-RSA-SEED-SHA", 119 | "DH-DSS-SEED-SHA", 120 | "DHE-RSA-CAMELLIA128-SHA", 121 | "DH-RSA-CAMELLIA128-SHA", 122 | "DH-DSS-CAMELLIA128-SHA", 123 | "ECDH-RSA-AES128-GCM-SHA256", 124 | "ECDH-ECDSA-AES128-GCM-SHA256", 125 | "ECDH-RSA-AES128-SHA256", 126 | "ECDH-ECDSA-AES128-SHA256", 127 | "ECDH-RSA-AES128-SHA", 128 | "ECDH-ECDSA-AES128-SHA", 129 | "ECDH-RSA-CAMELLIA128-SHA256", 130 | "ECDH-ECDSA-CAMELLIA128-SHA256", 131 | "AES128-GCM-SHA256", 132 | "AES128-SHA256", 133 | "AES128-SHA", 134 | "CAMELLIA128-SHA256", 135 | "SEED-SHA", 136 | "CAMELLIA128-SHA", 137 | "IDEA-CBC-SHA", 138 | "IDEA-CBC-MD5", 139 | "RC2-CBC-MD5", 140 | "RSA-PSK-AES128-CBC-SHA", 141 | "ECDHE-RSA-RC4-SHA", 142 | "ECDHE-ECDSA-RC4-SHA", 143 | "ECDH-RSA-RC4-SHA", 144 | "ECDH-ECDSA-RC4-SHA", 145 | "RC4-SHA", 146 | "RC4-MD5", 147 | "RC4-MD5", 148 | "RSA-PSK-RC4-SHA", 149 | "ECDHE-RSA-DES-CBC3-SHA", 150 | "ECDHE-ECDSA-DES-CBC3-SHA", 151 | "EDH-RSA-DES-CBC3-SHA", 152 | "DH-RSA-DES-CBC3-SHA", 153 | "DH-DSS-DES-CBC3-SHA", 154 | "ECDH-RSA-DES-CBC3-SHA", 155 | "ECDH-ECDSA-DES-CBC3-SHA", 156 | "DES-CBC3-SHA", 157 | "DES-CBC3-MD5", 158 | "RSA-PSK-3DES-EDE-CBC-SHA", 159 | "RC4-64-MD5", 160 | "EXP1024-DHE-DSS-DES-CBC-SHA", 161 | "EDH-RSA-DES-CBC-SHA", 162 | "DH-RSA-DES-CBC-SHA", 163 | "DH-DSS-DES-CBC-SHA", 164 | "EXP1024-DES-CBC-SHA", 165 | "DES-CBC-SHA", 166 | "EXP1024-RC2-CBC-MD5", 167 | "DES-CBC-MD5", 168 | "EXP1024-DHE-DSS-RC4-SHA", 169 | "EXP1024-RC4-SHA", 170 | "EXP1024-RC4-MD5", 171 | "EXP-EDH-RSA-DES-CBC-SHA", 172 | "EXP-EDH-DSS-DES-CBC-SHA", 173 | "EXP-DH-RSA-DES-CBC-SHA", 174 | "EXP-DH-DSS-DES-CBC-SHA", 175 | "EXP-ADH-DES-CBC-SHA", 176 | "EXP-DES-CBC-SHA", 177 | "EXP-RC2-CBC-MD5", 178 | "EXP-RC2-CBC-MD5", 179 | "EXP-ADH-RC4-MD5", 180 | "EXP-RC4-MD5", 181 | "EXP-RC4-MD5", 182 | "ECDHE-RSA-NULL-SHA", 183 | "ECDHE-ECDSA-NULL-SHA", 184 | "ECDH-RSA-NULL-SHA", 185 | "ECDH-ECDSA-NULL-SHA", 186 | "NULL-SHA256", 187 | "NULL-SHA", 188 | "NULL-MD5" 189 | ] 190 | }, 191 | "x509ChainDepth": 1, 192 | "verifyCertResult": false, 193 | "verifyCertError": "self signed certificate", 194 | "verifyHostResult": true, 195 | "ocspStapled": false, 196 | "certificateChain": [ 197 | { 198 | "version": 3, 199 | "subject": "emailAddress=tls-scan@example.com; CN=localhost; OU=tls-scan; O=tls-scan; ST=CA; C=US", 200 | "issuer": "emailAddress=tls-scan@example.com; CN=localhost; OU=tls-scan; O=tls-scan; ST=CA; C=US", 201 | "subjectCN": "localhost", 202 | "signatureAlg": "dsa_with_SHA256", 203 | "notBefore": "Dec 27 20:18:45 2019 GMT", 204 | "notAfter": "Dec 24 20:18:45 2029 GMT", 205 | "expired": false, 206 | "serialNo": "E8:7B:5B:ED:6A:01:D0:5C", 207 | "publicKeyAlg": "DSA", 208 | "publicKeySize": 1024, 209 | "basicConstraints": "CA:TRUE", 210 | "subjectKeyIdentifier": "7C:1C:3C:7F:5E:23:3B:34:CB:AA:D1:60:EE:9B:80:38:55:9B:ED:7B", 211 | "sha1Fingerprint": "DE:32:CD:15:BE:40:C4:19:C4:71:75:6C:36:EB:BC:F3:22:4C:4E:5A" 212 | } ] 213 | } 214 | 215 | <|---------Scan Summary---------|> 216 | [8589] ciphers : ECDHE-ECDSA-CHACHA20-POLY1305-OLD:ECDHE-RSA-CHACHA20-POLY1305-OLD:DHE-RSA-CHACHA20-POLY1305-OLD:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DH-DSS-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DH-RSA-AES256-SHA256:DH-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DH-RSA-AES256-SHA:DH-DSS-AES256-SHA:ECDHE-RSA-CAMELLIA256-SHA384:ECDHE-ECDSA-CAMELLIA256-SHA384:DHE-RSA-CAMELLIA256-SHA256:DHE-DSS-CAMELLIA256-SHA256:DH-RSA-CAMELLIA256-SHA256:DH-DSS-CAMELLIA256-SHA256:DHE-RSA-CAMELLIA256-SHA:DHE-DSS-CAMELLIA256-SHA:DH-RSA-CAMELLIA256-SHA:DH-DSS-CAMELLIA256-SHA:AECDH-AES256-SHA:ADH-AES256-GCM-SHA384:ADH-AES256-SHA256:ADH-AES256-SHA:ADH-CAMELLIA256-SHA256:ADH-CAMELLIA256-SHA:ECDH-RSA-AES256-GCM-SHA384:ECDH-ECDSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA384:ECDH-ECDSA-AES256-SHA384:ECDH-RSA-AES256-SHA:ECDH-ECDSA-AES256-SHA:ECDH-RSA-CAMELLIA256-SHA384:ECDH-ECDSA-CAMELLIA256-SHA384:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:CAMELLIA256-SHA256:CAMELLIA256-SHA:RSA-PSK-AES256-CBC-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:DH-DSS-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DH-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DH-RSA-AES128-SHA256:DH-DSS-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:DH-RSA-AES128-SHA:DH-DSS-AES128-SHA:ECDHE-RSA-CAMELLIA128-SHA256:ECDHE-ECDSA-CAMELLIA128-SHA256:DHE-RSA-CAMELLIA128-SHA256:DHE-DSS-CAMELLIA128-SHA256:DH-RSA-CAMELLIA128-SHA256:DH-DSS-CAMELLIA128-SHA256:DHE-RSA-SEED-SHA:DHE-DSS-SEED-SHA:DH-RSA-SEED-SHA:DH-DSS-SEED-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-DSS-CAMELLIA128-SHA:DH-RSA-CAMELLIA128-SHA:DH-DSS-CAMELLIA128-SHA:AECDH-AES128-SHA:ADH-AES128-GCM-SHA256:ADH-AES128-SHA256:ADH-AES128-SHA:ADH-CAMELLIA128-SHA256:ADH-SEED-SHA:ADH-CAMELLIA128-SHA:ECDH-RSA-AES128-GCM-SHA256:ECDH-ECDSA-AES128-GCM-SHA256:ECDH-RSA-AES128-SHA256:ECDH-ECDSA-AES128-SHA256:ECDH-RSA-AES128-SHA:ECDH-ECDSA-AES128-SHA:ECDH-RSA-CAMELLIA128-SHA256:ECDH-ECDSA-CAMELLIA128-SHA256:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:CAMELLIA128-SHA256:SEED-SHA:CAMELLIA128-SHA:IDEA-CBC-SHA:IDEA-CBC-MD5:RC2-CBC-MD5:RSA-PSK-AES128-CBC-SHA:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:DHE-DSS-RC4-SHA:AECDH-RC4-SHA:ADH-RC4-MD5:ECDH-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:RC4-SHA:RC4-MD5:RC4-MD5:RSA-PSK-RC4-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DH-RSA-DES-CBC3-SHA:DH-DSS-DES-CBC3-SHA:AECDH-DES-CBC3-SHA:ADH-DES-CBC3-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-ECDSA-DES-CBC3-SHA:DES-CBC3-SHA:DES-CBC3-MD5:RSA-PSK-3DES-EDE-CBC-SHA:RC4-64-MD5:EXP1024-DHE-DSS-DES-CBC-SHA:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DH-RSA-DES-CBC-SHA:DH-DSS-DES-CBC-SHA:ADH-DES-CBC-SHA:EXP1024-DES-CBC-SHA:DES-CBC-SHA:EXP1024-RC2-CBC-MD5:DES-CBC-MD5:EXP1024-DHE-DSS-RC4-SHA:EXP1024-RC4-SHA:EXP1024-RC4-MD5:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA:EXP-DH-RSA-DES-CBC-SHA:EXP-DH-DSS-DES-CBC-SHA:EXP-ADH-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC2-CBC-MD5:EXP-ADH-RC4-MD5:EXP-RC4-MD5:EXP-RC4-MD5:AECDH-NULL-SHA:ECDHE-RSA-NULL-SHA:ECDHE-ECDSA-NULL-SHA:ECDH-RSA-NULL-SHA:ECDH-ECDSA-NULL-SHA:NULL-SHA256:NULL-SHA:NULL-MD5: (165) 217 | [8589] dns-lookup : 1 218 | [8589] network-error : 134 219 | [8589] dns-errcount : 0 220 | [8589] remote-close-error : 0 221 | [8589] unknown-error : 0 222 | [8589] timeout-error : 0 223 | [8589] connect-error : 0 224 | [8589] tls-handshake : 1 225 | [8589] gross-tls-handshake : 38 226 | [8589] elapsed-time : 0.661049 secs 227 | <|------------------------------|> 228 | -------------------------------------------------------------------------------- /tests/dsa-test.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDgzCCA0CgAwIBAgIJAOh7W+1qAdBcMAsGCWCGSAFlAwQDAjB5MQswCQYDVQQG 3 | EwJVUzELMAkGA1UECAwCQ0ExETAPBgNVBAoMCHRscy1zY2FuMREwDwYDVQQLDAh0 4 | bHMtc2NhbjESMBAGA1UEAwwJbG9jYWxob3N0MSMwIQYJKoZIhvcNAQkBFhR0bHMt 5 | c2NhbkBleGFtcGxlLmNvbTAeFw0xOTEyMjcyMDE4NDVaFw0yOTEyMjQyMDE4NDVa 6 | MHkxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTERMA8GA1UECgwIdGxzLXNjYW4x 7 | ETAPBgNVBAsMCHRscy1zY2FuMRIwEAYDVQQDDAlsb2NhbGhvc3QxIzAhBgkqhkiG 8 | 9w0BCQEWFHRscy1zY2FuQGV4YW1wbGUuY29tMIIBtzCCASsGByqGSM44BAEwggEe 9 | AoGBAMfquBfWZ/p9JwCgBKwqzifM9AgieFXg5GQvEqnlF9zmweKdUL98eOzz+kL3 10 | tcB7SCNC7739wzSZtfQf7ODMhhNFoSe1JPHETPMWZHqINtIC+wL3eOtxCbaUhU75 11 | zkkfvITd6XZa8b/7VXC4vdyMJ0RsdvLNaL9YyadiGQMdqXN5AhUA+FHgOIUahheB 12 | XjONFgKNKBMXxIsCgYAwDmO6AzHrRPGkFhAYDNZX2a216ABZoVNSlyYDbBKnCkIY 13 | H8zWJ/NXthaLDoMxO6TbpmWtIyyf6v+tDBYEiYYVTWmSM/BPjr6nQi7H1LBlRNY8 14 | JnsLyDLOcM+lwnqewC7KmBl3CMndVge5CjWHqWlIDSDFplB7oQM+n9OoaPhFHgOB 15 | hQACgYEAhvYby7gN1dad1uTiJSTT/BfMreGBbpQdl4/+6Z7IJc/YSE7hHxv/zvIa 16 | z8ZfEkmjNvPq41BY0t2ATKRoUZN7pbYXCizzaHq8J8I0pZK0LSc1+JZVjYiRQkAf 17 | SXkiLctME/w85KMA6iKYKDpM8tOyS1SfZhhVq1W+CqdKrA+mN7CjUDBOMB0GA1Ud 18 | DgQWBBR8HDx/XiM7NMuq0WDum4A4VZvtezAfBgNVHSMEGDAWgBR8HDx/XiM7NMuq 19 | 0WDum4A4VZvtezAMBgNVHRMEBTADAQH/MAsGCWCGSAFlAwQDAgMwADAtAhUAzxJD 20 | 79tZaS/8qD2aQYu2rr9sewcCFEUxPEnsqcmF/9ObFDFdSMlH7RBY 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /tests/dsa-test.key: -------------------------------------------------------------------------------- 1 | -----BEGIN DSA PARAMETERS----- 2 | MIIBHgKBgQDH6rgX1mf6fScAoASsKs4nzPQIInhV4ORkLxKp5Rfc5sHinVC/fHjs 3 | 8/pC97XAe0gjQu+9/cM0mbX0H+zgzIYTRaEntSTxxEzzFmR6iDbSAvsC93jrcQm2 4 | lIVO+c5JH7yE3el2WvG/+1VwuL3cjCdEbHbyzWi/WMmnYhkDHalzeQIVAPhR4DiF 5 | GoYXgV4zjRYCjSgTF8SLAoGAMA5jugMx60TxpBYQGAzWV9mttegAWaFTUpcmA2wS 6 | pwpCGB/M1ifzV7YWiw6DMTuk26ZlrSMsn+r/rQwWBImGFU1pkjPwT46+p0Iux9Sw 7 | ZUTWPCZ7C8gyznDPpcJ6nsAuypgZdwjJ3VYHuQo1h6lpSA0gxaZQe6EDPp/TqGj4 8 | RR4= 9 | -----END DSA PARAMETERS----- 10 | -----BEGIN DSA PRIVATE KEY----- 11 | MIIBuwIBAAKBgQDH6rgX1mf6fScAoASsKs4nzPQIInhV4ORkLxKp5Rfc5sHinVC/ 12 | fHjs8/pC97XAe0gjQu+9/cM0mbX0H+zgzIYTRaEntSTxxEzzFmR6iDbSAvsC93jr 13 | cQm2lIVO+c5JH7yE3el2WvG/+1VwuL3cjCdEbHbyzWi/WMmnYhkDHalzeQIVAPhR 14 | 4DiFGoYXgV4zjRYCjSgTF8SLAoGAMA5jugMx60TxpBYQGAzWV9mttegAWaFTUpcm 15 | A2wSpwpCGB/M1ifzV7YWiw6DMTuk26ZlrSMsn+r/rQwWBImGFU1pkjPwT46+p0Iu 16 | x9SwZUTWPCZ7C8gyznDPpcJ6nsAuypgZdwjJ3VYHuQo1h6lpSA0gxaZQe6EDPp/T 17 | qGj4RR4CgYEAhvYby7gN1dad1uTiJSTT/BfMreGBbpQdl4/+6Z7IJc/YSE7hHxv/ 18 | zvIaz8ZfEkmjNvPq41BY0t2ATKRoUZN7pbYXCizzaHq8J8I0pZK0LSc1+JZVjYiR 19 | QkAfSXkiLctME/w85KMA6iKYKDpM8tOyS1SfZhhVq1W+CqdKrA+mN7ACFEKy16O+ 20 | U7byEy8CoxSWgID9HLva 21 | -----END DSA PRIVATE KEY----- 22 | -------------------------------------------------------------------------------- /tests/ecdsa-scan.out: -------------------------------------------------------------------------------- 1 | { 2 | "host": "localhost", 3 | "ip": "127.0.0.1", 4 | "port": 4443, 5 | "elapsedTime": 287, 6 | "tlsVersion": "TLSv1.2", 7 | "cipher": "ECDHE-ECDSA-CHACHA20-POLY1305-OLD TLSv1.2 Kx=ECDH Au=ECDSA Enc=ChaCha20(256) Mac=AEAD", 8 | "tempPublicKeyAlg": "ECDH prime256v1", 9 | "tempPublicKeySize": 256, 10 | "secureRenego": true, 11 | "compression": "zlib compression", 12 | "expansion": "zlib compression", 13 | "sessionLifetimeHint": 300, 14 | "tlsVersions": [ 15 | "SSLv3", 16 | "TLSv1", 17 | "TLSv1_1", 18 | "TLSv1_2" 19 | ], 20 | "cipherSuite": { 21 | "supported": [ 22 | "ECDHE-ECDSA-CHACHA20-POLY1305-OLD", 23 | "ECDHE-ECDSA-AES256-GCM-SHA384", 24 | "ECDHE-ECDSA-AES256-SHA384", 25 | "ECDHE-ECDSA-AES256-SHA", 26 | "ECDHE-ECDSA-CAMELLIA256-SHA384", 27 | "AECDH-AES256-SHA", 28 | "ADH-AES256-GCM-SHA384", 29 | "ADH-AES256-SHA256", 30 | "ADH-AES256-SHA", 31 | "ADH-CAMELLIA256-SHA256", 32 | "ADH-CAMELLIA256-SHA", 33 | "ECDH-ECDSA-AES256-GCM-SHA384", 34 | "ECDH-ECDSA-AES256-SHA384", 35 | "ECDH-ECDSA-AES256-SHA", 36 | "ECDH-ECDSA-CAMELLIA256-SHA384", 37 | "ECDHE-ECDSA-AES128-GCM-SHA256", 38 | "ECDHE-ECDSA-AES128-SHA256", 39 | "ECDHE-ECDSA-AES128-SHA", 40 | "ECDHE-ECDSA-CAMELLIA128-SHA256", 41 | "AECDH-AES128-SHA", 42 | "ADH-AES128-GCM-SHA256", 43 | "ADH-AES128-SHA256", 44 | "ADH-AES128-SHA", 45 | "ADH-CAMELLIA128-SHA256", 46 | "ADH-SEED-SHA", 47 | "ADH-CAMELLIA128-SHA", 48 | "ECDH-ECDSA-AES128-GCM-SHA256", 49 | "ECDH-ECDSA-AES128-SHA256", 50 | "ECDH-ECDSA-AES128-SHA", 51 | "ECDH-ECDSA-CAMELLIA128-SHA256", 52 | "ECDHE-ECDSA-RC4-SHA", 53 | "AECDH-RC4-SHA", 54 | "ADH-RC4-MD5", 55 | "ECDH-ECDSA-RC4-SHA", 56 | "ECDHE-ECDSA-DES-CBC3-SHA", 57 | "AECDH-DES-CBC3-SHA", 58 | "ADH-DES-CBC3-SHA", 59 | "ECDH-ECDSA-DES-CBC3-SHA", 60 | "ADH-DES-CBC-SHA", 61 | "AECDH-NULL-SHA", 62 | "ECDHE-ECDSA-NULL-SHA", 63 | "ECDH-ECDSA-NULL-SHA" 64 | ], 65 | "notSupported": [ 66 | "ECDHE-RSA-CHACHA20-POLY1305-OLD", 67 | "DHE-RSA-CHACHA20-POLY1305-OLD", 68 | "ECDHE-RSA-AES256-GCM-SHA384", 69 | "ECDHE-RSA-AES256-SHA384", 70 | "ECDHE-RSA-AES256-SHA", 71 | "DH-DSS-AES256-GCM-SHA384", 72 | "DHE-DSS-AES256-GCM-SHA384", 73 | "DH-RSA-AES256-GCM-SHA384", 74 | "DHE-RSA-AES256-GCM-SHA384", 75 | "DHE-RSA-AES256-SHA256", 76 | "DHE-DSS-AES256-SHA256", 77 | "DH-RSA-AES256-SHA256", 78 | "DH-DSS-AES256-SHA256", 79 | "DHE-RSA-AES256-SHA", 80 | "DHE-DSS-AES256-SHA", 81 | "DH-RSA-AES256-SHA", 82 | "DH-DSS-AES256-SHA", 83 | "ECDHE-RSA-CAMELLIA256-SHA384", 84 | "DHE-RSA-CAMELLIA256-SHA256", 85 | "DHE-DSS-CAMELLIA256-SHA256", 86 | "DH-RSA-CAMELLIA256-SHA256", 87 | "DH-DSS-CAMELLIA256-SHA256", 88 | "DHE-RSA-CAMELLIA256-SHA", 89 | "DHE-DSS-CAMELLIA256-SHA", 90 | "DH-RSA-CAMELLIA256-SHA", 91 | "DH-DSS-CAMELLIA256-SHA", 92 | "ECDH-RSA-AES256-GCM-SHA384", 93 | "ECDH-RSA-AES256-SHA384", 94 | "ECDH-RSA-AES256-SHA", 95 | "ECDH-RSA-CAMELLIA256-SHA384", 96 | "AES256-GCM-SHA384", 97 | "AES256-SHA256", 98 | "AES256-SHA", 99 | "CAMELLIA256-SHA256", 100 | "CAMELLIA256-SHA", 101 | "RSA-PSK-AES256-CBC-SHA", 102 | "ECDHE-RSA-AES128-GCM-SHA256", 103 | "ECDHE-RSA-AES128-SHA256", 104 | "ECDHE-RSA-AES128-SHA", 105 | "DH-DSS-AES128-GCM-SHA256", 106 | "DHE-DSS-AES128-GCM-SHA256", 107 | "DH-RSA-AES128-GCM-SHA256", 108 | "DHE-RSA-AES128-GCM-SHA256", 109 | "DHE-RSA-AES128-SHA256", 110 | "DHE-DSS-AES128-SHA256", 111 | "DH-RSA-AES128-SHA256", 112 | "DH-DSS-AES128-SHA256", 113 | "DHE-RSA-AES128-SHA", 114 | "DHE-DSS-AES128-SHA", 115 | "DH-RSA-AES128-SHA", 116 | "DH-DSS-AES128-SHA", 117 | "ECDHE-RSA-CAMELLIA128-SHA256", 118 | "DHE-RSA-CAMELLIA128-SHA256", 119 | "DHE-DSS-CAMELLIA128-SHA256", 120 | "DH-RSA-CAMELLIA128-SHA256", 121 | "DH-DSS-CAMELLIA128-SHA256", 122 | "DHE-RSA-SEED-SHA", 123 | "DHE-DSS-SEED-SHA", 124 | "DH-RSA-SEED-SHA", 125 | "DH-DSS-SEED-SHA", 126 | "DHE-RSA-CAMELLIA128-SHA", 127 | "DHE-DSS-CAMELLIA128-SHA", 128 | "DH-RSA-CAMELLIA128-SHA", 129 | "DH-DSS-CAMELLIA128-SHA", 130 | "ECDH-RSA-AES128-GCM-SHA256", 131 | "ECDH-RSA-AES128-SHA256", 132 | "ECDH-RSA-AES128-SHA", 133 | "ECDH-RSA-CAMELLIA128-SHA256", 134 | "AES128-GCM-SHA256", 135 | "AES128-SHA256", 136 | "AES128-SHA", 137 | "CAMELLIA128-SHA256", 138 | "SEED-SHA", 139 | "CAMELLIA128-SHA", 140 | "IDEA-CBC-SHA", 141 | "IDEA-CBC-MD5", 142 | "RC2-CBC-MD5", 143 | "RSA-PSK-AES128-CBC-SHA", 144 | "ECDHE-RSA-RC4-SHA", 145 | "DHE-DSS-RC4-SHA", 146 | "ECDH-RSA-RC4-SHA", 147 | "RC4-SHA", 148 | "RC4-MD5", 149 | "RC4-MD5", 150 | "RSA-PSK-RC4-SHA", 151 | "ECDHE-RSA-DES-CBC3-SHA", 152 | "EDH-RSA-DES-CBC3-SHA", 153 | "EDH-DSS-DES-CBC3-SHA", 154 | "DH-RSA-DES-CBC3-SHA", 155 | "DH-DSS-DES-CBC3-SHA", 156 | "ECDH-RSA-DES-CBC3-SHA", 157 | "DES-CBC3-SHA", 158 | "DES-CBC3-MD5", 159 | "RSA-PSK-3DES-EDE-CBC-SHA", 160 | "RC4-64-MD5", 161 | "EXP1024-DHE-DSS-DES-CBC-SHA", 162 | "EDH-RSA-DES-CBC-SHA", 163 | "EDH-DSS-DES-CBC-SHA", 164 | "DH-RSA-DES-CBC-SHA", 165 | "DH-DSS-DES-CBC-SHA", 166 | "EXP1024-DES-CBC-SHA", 167 | "DES-CBC-SHA", 168 | "EXP1024-RC2-CBC-MD5", 169 | "DES-CBC-MD5", 170 | "EXP1024-DHE-DSS-RC4-SHA", 171 | "EXP1024-RC4-SHA", 172 | "EXP1024-RC4-MD5", 173 | "EXP-EDH-RSA-DES-CBC-SHA", 174 | "EXP-EDH-DSS-DES-CBC-SHA", 175 | "EXP-DH-RSA-DES-CBC-SHA", 176 | "EXP-DH-DSS-DES-CBC-SHA", 177 | "EXP-ADH-DES-CBC-SHA", 178 | "EXP-DES-CBC-SHA", 179 | "EXP-RC2-CBC-MD5", 180 | "EXP-RC2-CBC-MD5", 181 | "EXP-ADH-RC4-MD5", 182 | "EXP-RC4-MD5", 183 | "EXP-RC4-MD5", 184 | "ECDHE-RSA-NULL-SHA", 185 | "ECDH-RSA-NULL-SHA", 186 | "NULL-SHA256", 187 | "NULL-SHA", 188 | "NULL-MD5" 189 | ] 190 | }, 191 | "x509ChainDepth": 1, 192 | "verifyCertResult": false, 193 | "verifyCertError": "self signed certificate", 194 | "verifyHostResult": true, 195 | "ocspStapled": false, 196 | "certificateChain": [ 197 | { 198 | "version": 3, 199 | "subject": "emailAddress=tls-scan@example.com; CN=localhost; OU=tls-scan; O=tls-scan; ST=CA; C=US", 200 | "issuer": "emailAddress=tls-scan@example.com; CN=localhost; OU=tls-scan; O=tls-scan; ST=CA; C=US", 201 | "subjectCN": "localhost", 202 | "signatureAlg": "ecdsa-with-SHA256", 203 | "notBefore": "Dec 27 19:58:39 2019 GMT", 204 | "notAfter": "Dec 24 19:58:39 2029 GMT", 205 | "expired": false, 206 | "serialNo": "33:DB:B9:F4:82:DE:79:2F:9F:61:12:C3:40:D0:2C:53:10:D0:00:A5", 207 | "publicKeyAlg": "ECC prime256v1", 208 | "publicKeySize": 256, 209 | "basicConstraints": "CA:TRUE critical", 210 | "subjectKeyIdentifier": "14:A7:85:ED:7D:65:B1:49:61:19:0D:24:5E:EB:EC:6F:15:AA:12:18", 211 | "sha1Fingerprint": "AD:B9:4C:2F:ED:CE:86:87:BB:11:A9:DB:A6:B6:78:3D:4D:6E:BB:0F" 212 | } ] 213 | } 214 | 215 | <|---------Scan Summary---------|> 216 | [8578] ciphers : ECDHE-ECDSA-CHACHA20-POLY1305-OLD:ECDHE-RSA-CHACHA20-POLY1305-OLD:DHE-RSA-CHACHA20-POLY1305-OLD:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DH-DSS-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DH-RSA-AES256-SHA256:DH-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DH-RSA-AES256-SHA:DH-DSS-AES256-SHA:ECDHE-RSA-CAMELLIA256-SHA384:ECDHE-ECDSA-CAMELLIA256-SHA384:DHE-RSA-CAMELLIA256-SHA256:DHE-DSS-CAMELLIA256-SHA256:DH-RSA-CAMELLIA256-SHA256:DH-DSS-CAMELLIA256-SHA256:DHE-RSA-CAMELLIA256-SHA:DHE-DSS-CAMELLIA256-SHA:DH-RSA-CAMELLIA256-SHA:DH-DSS-CAMELLIA256-SHA:AECDH-AES256-SHA:ADH-AES256-GCM-SHA384:ADH-AES256-SHA256:ADH-AES256-SHA:ADH-CAMELLIA256-SHA256:ADH-CAMELLIA256-SHA:ECDH-RSA-AES256-GCM-SHA384:ECDH-ECDSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA384:ECDH-ECDSA-AES256-SHA384:ECDH-RSA-AES256-SHA:ECDH-ECDSA-AES256-SHA:ECDH-RSA-CAMELLIA256-SHA384:ECDH-ECDSA-CAMELLIA256-SHA384:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:CAMELLIA256-SHA256:CAMELLIA256-SHA:RSA-PSK-AES256-CBC-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:DH-DSS-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DH-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DH-RSA-AES128-SHA256:DH-DSS-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:DH-RSA-AES128-SHA:DH-DSS-AES128-SHA:ECDHE-RSA-CAMELLIA128-SHA256:ECDHE-ECDSA-CAMELLIA128-SHA256:DHE-RSA-CAMELLIA128-SHA256:DHE-DSS-CAMELLIA128-SHA256:DH-RSA-CAMELLIA128-SHA256:DH-DSS-CAMELLIA128-SHA256:DHE-RSA-SEED-SHA:DHE-DSS-SEED-SHA:DH-RSA-SEED-SHA:DH-DSS-SEED-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-DSS-CAMELLIA128-SHA:DH-RSA-CAMELLIA128-SHA:DH-DSS-CAMELLIA128-SHA:AECDH-AES128-SHA:ADH-AES128-GCM-SHA256:ADH-AES128-SHA256:ADH-AES128-SHA:ADH-CAMELLIA128-SHA256:ADH-SEED-SHA:ADH-CAMELLIA128-SHA:ECDH-RSA-AES128-GCM-SHA256:ECDH-ECDSA-AES128-GCM-SHA256:ECDH-RSA-AES128-SHA256:ECDH-ECDSA-AES128-SHA256:ECDH-RSA-AES128-SHA:ECDH-ECDSA-AES128-SHA:ECDH-RSA-CAMELLIA128-SHA256:ECDH-ECDSA-CAMELLIA128-SHA256:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:CAMELLIA128-SHA256:SEED-SHA:CAMELLIA128-SHA:IDEA-CBC-SHA:IDEA-CBC-MD5:RC2-CBC-MD5:RSA-PSK-AES128-CBC-SHA:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:DHE-DSS-RC4-SHA:AECDH-RC4-SHA:ADH-RC4-MD5:ECDH-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:RC4-SHA:RC4-MD5:RC4-MD5:RSA-PSK-RC4-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DH-RSA-DES-CBC3-SHA:DH-DSS-DES-CBC3-SHA:AECDH-DES-CBC3-SHA:ADH-DES-CBC3-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-ECDSA-DES-CBC3-SHA:DES-CBC3-SHA:DES-CBC3-MD5:RSA-PSK-3DES-EDE-CBC-SHA:RC4-64-MD5:EXP1024-DHE-DSS-DES-CBC-SHA:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DH-RSA-DES-CBC-SHA:DH-DSS-DES-CBC-SHA:ADH-DES-CBC-SHA:EXP1024-DES-CBC-SHA:DES-CBC-SHA:EXP1024-RC2-CBC-MD5:DES-CBC-MD5:EXP1024-DHE-DSS-RC4-SHA:EXP1024-RC4-SHA:EXP1024-RC4-MD5:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA:EXP-DH-RSA-DES-CBC-SHA:EXP-DH-DSS-DES-CBC-SHA:EXP-ADH-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC2-CBC-MD5:EXP-ADH-RC4-MD5:EXP-RC4-MD5:EXP-RC4-MD5:AECDH-NULL-SHA:ECDHE-RSA-NULL-SHA:ECDHE-ECDSA-NULL-SHA:ECDH-RSA-NULL-SHA:ECDH-ECDSA-NULL-SHA:NULL-SHA256:NULL-SHA:NULL-MD5: (165) 217 | [8578] dns-lookup : 1 218 | [8578] network-error : 125 219 | [8578] dns-errcount : 0 220 | [8578] remote-close-error : 0 221 | [8578] unknown-error : 0 222 | [8578] timeout-error : 0 223 | [8578] connect-error : 0 224 | [8578] tls-handshake : 1 225 | [8578] gross-tls-handshake : 47 226 | [8578] elapsed-time : 0.433147 secs 227 | <|------------------------------|> 228 | -------------------------------------------------------------------------------- /tests/ecdsa-test.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICSDCCAe2gAwIBAgIUM9u59ILeeS+fYRLDQNAsUxDQAKUwCgYIKoZIzj0EAwIw 3 | eTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMREwDwYDVQQKDAh0bHMtc2NhbjER 4 | MA8GA1UECwwIdGxzLXNjYW4xEjAQBgNVBAMMCWxvY2FsaG9zdDEjMCEGCSqGSIb3 5 | DQEJARYUdGxzLXNjYW5AZXhhbXBsZS5jb20wHhcNMTkxMjI3MTk1ODM5WhcNMjkx 6 | MjI0MTk1ODM5WjB5MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExETAPBgNVBAoM 7 | CHRscy1zY2FuMREwDwYDVQQLDAh0bHMtc2NhbjESMBAGA1UEAwwJbG9jYWxob3N0 8 | MSMwIQYJKoZIhvcNAQkBFhR0bHMtc2NhbkBleGFtcGxlLmNvbTBZMBMGByqGSM49 9 | AgEGCCqGSM49AwEHA0IABP84WrR5LZkcQxH/EzznE4o/O2g4PhcIeszlBHzrxEG6 10 | 9vIvtOXwgVInySGOeQU7oieIn9uY/1eer+Ia5DtP/5+jUzBRMB0GA1UdDgQWBBQU 11 | p4XtfWWxSWEZDSRe6+xvFaoSGDAfBgNVHSMEGDAWgBQUp4XtfWWxSWEZDSRe6+xv 12 | FaoSGDAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQCT5mReJcGB 13 | nvPenwNo9NQz2+75sgMPPhtbpgt83KnpygIhAKaiceBKSFJBSB2th28W3W2IEP3j 14 | bT2Gd8UE+5mzGZl/ 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /tests/ecdsa-test.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgzaqRUB9pSd4b/dAf 3 | eXLSZZcmsMwhSFdQjKoaMgoplHmhRANCAAT/OFq0eS2ZHEMR/xM85xOKPztoOD4X 4 | CHrM5QR868RBuvbyL7Tl8IFSJ8khjnkFO6IniJ/bmP9Xnq/iGuQ7T/+f 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /tests/rsa-scan.out: -------------------------------------------------------------------------------- 1 | { 2 | "host": "localhost", 3 | "ip": "127.0.0.1", 4 | "port": 4443, 5 | "elapsedTime": 551, 6 | "tlsVersion": "TLSv1.2", 7 | "cipher": "ECDHE-RSA-CHACHA20-POLY1305-OLD TLSv1.2 Kx=ECDH Au=RSA Enc=ChaCha20(256) Mac=AEAD", 8 | "tempPublicKeyAlg": "ECDH prime256v1", 9 | "tempPublicKeySize": 256, 10 | "secureRenego": true, 11 | "compression": "zlib compression", 12 | "expansion": "zlib compression", 13 | "sessionLifetimeHint": 300, 14 | "tlsVersions": [ 15 | "SSLv2", 16 | "SSLv3", 17 | "TLSv1", 18 | "TLSv1_1", 19 | "TLSv1_2" 20 | ], 21 | "cipherSuite": { 22 | "supported": [ 23 | "ECDHE-RSA-CHACHA20-POLY1305-OLD", 24 | "DHE-RSA-CHACHA20-POLY1305-OLD", 25 | "ECDHE-RSA-AES256-GCM-SHA384", 26 | "ECDHE-RSA-AES256-SHA384", 27 | "ECDHE-RSA-AES256-SHA", 28 | "DHE-RSA-AES256-GCM-SHA384", 29 | "DHE-RSA-AES256-SHA256", 30 | "DHE-RSA-AES256-SHA", 31 | "ECDHE-RSA-CAMELLIA256-SHA384", 32 | "DHE-RSA-CAMELLIA256-SHA256", 33 | "DHE-RSA-CAMELLIA256-SHA", 34 | "AECDH-AES256-SHA", 35 | "ADH-AES256-GCM-SHA384", 36 | "ADH-AES256-SHA256", 37 | "ADH-AES256-SHA", 38 | "ADH-CAMELLIA256-SHA256", 39 | "ADH-CAMELLIA256-SHA", 40 | "AES256-GCM-SHA384", 41 | "AES256-SHA256", 42 | "AES256-SHA", 43 | "CAMELLIA256-SHA256", 44 | "CAMELLIA256-SHA", 45 | "ECDHE-RSA-AES128-GCM-SHA256", 46 | "ECDHE-RSA-AES128-SHA256", 47 | "ECDHE-RSA-AES128-SHA", 48 | "DHE-RSA-AES128-GCM-SHA256", 49 | "DHE-RSA-AES128-SHA256", 50 | "DHE-RSA-AES128-SHA", 51 | "ECDHE-RSA-CAMELLIA128-SHA256", 52 | "DHE-RSA-CAMELLIA128-SHA256", 53 | "DHE-RSA-SEED-SHA", 54 | "DHE-RSA-CAMELLIA128-SHA", 55 | "AECDH-AES128-SHA", 56 | "ADH-AES128-GCM-SHA256", 57 | "ADH-AES128-SHA256", 58 | "ADH-AES128-SHA", 59 | "ADH-CAMELLIA128-SHA256", 60 | "ADH-SEED-SHA", 61 | "ADH-CAMELLIA128-SHA", 62 | "AES128-GCM-SHA256", 63 | "AES128-SHA256", 64 | "AES128-SHA", 65 | "CAMELLIA128-SHA256", 66 | "SEED-SHA", 67 | "CAMELLIA128-SHA", 68 | "IDEA-CBC-SHA", 69 | "IDEA-CBC-MD5", 70 | "ECDHE-RSA-RC4-SHA", 71 | "AECDH-RC4-SHA", 72 | "ADH-RC4-MD5", 73 | "RC4-SHA", 74 | "RC4-MD5", 75 | "RC4-MD5", 76 | "ECDHE-RSA-DES-CBC3-SHA", 77 | "EDH-RSA-DES-CBC3-SHA", 78 | "AECDH-DES-CBC3-SHA", 79 | "ADH-DES-CBC3-SHA", 80 | "DES-CBC3-SHA", 81 | "DES-CBC3-MD5", 82 | "EDH-RSA-DES-CBC-SHA", 83 | "ADH-DES-CBC-SHA", 84 | "EXP1024-DES-CBC-SHA", 85 | "DES-CBC-SHA", 86 | "EXP1024-RC2-CBC-MD5", 87 | "DES-CBC-MD5", 88 | "EXP1024-RC4-SHA", 89 | "EXP1024-RC4-MD5", 90 | "EXP-RC4-MD5", 91 | "EXP-RC4-MD5", 92 | "AECDH-NULL-SHA", 93 | "ECDHE-RSA-NULL-SHA", 94 | "NULL-SHA256", 95 | "NULL-SHA", 96 | "NULL-MD5" 97 | ], 98 | "notSupported": [ 99 | "ECDHE-ECDSA-CHACHA20-POLY1305-OLD", 100 | "ECDHE-ECDSA-AES256-GCM-SHA384", 101 | "ECDHE-ECDSA-AES256-SHA384", 102 | "ECDHE-ECDSA-AES256-SHA", 103 | "DH-DSS-AES256-GCM-SHA384", 104 | "DHE-DSS-AES256-GCM-SHA384", 105 | "DH-RSA-AES256-GCM-SHA384", 106 | "DHE-DSS-AES256-SHA256", 107 | "DH-RSA-AES256-SHA256", 108 | "DH-DSS-AES256-SHA256", 109 | "DHE-DSS-AES256-SHA", 110 | "DH-RSA-AES256-SHA", 111 | "DH-DSS-AES256-SHA", 112 | "ECDHE-ECDSA-CAMELLIA256-SHA384", 113 | "DHE-DSS-CAMELLIA256-SHA256", 114 | "DH-RSA-CAMELLIA256-SHA256", 115 | "DH-DSS-CAMELLIA256-SHA256", 116 | "DHE-DSS-CAMELLIA256-SHA", 117 | "DH-RSA-CAMELLIA256-SHA", 118 | "DH-DSS-CAMELLIA256-SHA", 119 | "ECDH-RSA-AES256-GCM-SHA384", 120 | "ECDH-ECDSA-AES256-GCM-SHA384", 121 | "ECDH-RSA-AES256-SHA384", 122 | "ECDH-ECDSA-AES256-SHA384", 123 | "ECDH-RSA-AES256-SHA", 124 | "ECDH-ECDSA-AES256-SHA", 125 | "ECDH-RSA-CAMELLIA256-SHA384", 126 | "ECDH-ECDSA-CAMELLIA256-SHA384", 127 | "RSA-PSK-AES256-CBC-SHA", 128 | "ECDHE-ECDSA-AES128-GCM-SHA256", 129 | "ECDHE-ECDSA-AES128-SHA256", 130 | "ECDHE-ECDSA-AES128-SHA", 131 | "DH-DSS-AES128-GCM-SHA256", 132 | "DHE-DSS-AES128-GCM-SHA256", 133 | "DH-RSA-AES128-GCM-SHA256", 134 | "DHE-DSS-AES128-SHA256", 135 | "DH-RSA-AES128-SHA256", 136 | "DH-DSS-AES128-SHA256", 137 | "DHE-DSS-AES128-SHA", 138 | "DH-RSA-AES128-SHA", 139 | "DH-DSS-AES128-SHA", 140 | "ECDHE-ECDSA-CAMELLIA128-SHA256", 141 | "DHE-DSS-CAMELLIA128-SHA256", 142 | "DH-RSA-CAMELLIA128-SHA256", 143 | "DH-DSS-CAMELLIA128-SHA256", 144 | "DHE-DSS-SEED-SHA", 145 | "DH-RSA-SEED-SHA", 146 | "DH-DSS-SEED-SHA", 147 | "DHE-DSS-CAMELLIA128-SHA", 148 | "DH-RSA-CAMELLIA128-SHA", 149 | "DH-DSS-CAMELLIA128-SHA", 150 | "ECDH-RSA-AES128-GCM-SHA256", 151 | "ECDH-ECDSA-AES128-GCM-SHA256", 152 | "ECDH-RSA-AES128-SHA256", 153 | "ECDH-ECDSA-AES128-SHA256", 154 | "ECDH-RSA-AES128-SHA", 155 | "ECDH-ECDSA-AES128-SHA", 156 | "ECDH-RSA-CAMELLIA128-SHA256", 157 | "ECDH-ECDSA-CAMELLIA128-SHA256", 158 | "RC2-CBC-MD5", 159 | "RSA-PSK-AES128-CBC-SHA", 160 | "ECDHE-ECDSA-RC4-SHA", 161 | "DHE-DSS-RC4-SHA", 162 | "ECDH-RSA-RC4-SHA", 163 | "ECDH-ECDSA-RC4-SHA", 164 | "RSA-PSK-RC4-SHA", 165 | "ECDHE-ECDSA-DES-CBC3-SHA", 166 | "EDH-DSS-DES-CBC3-SHA", 167 | "DH-RSA-DES-CBC3-SHA", 168 | "DH-DSS-DES-CBC3-SHA", 169 | "ECDH-RSA-DES-CBC3-SHA", 170 | "ECDH-ECDSA-DES-CBC3-SHA", 171 | "RSA-PSK-3DES-EDE-CBC-SHA", 172 | "RC4-64-MD5", 173 | "EXP1024-DHE-DSS-DES-CBC-SHA", 174 | "EDH-DSS-DES-CBC-SHA", 175 | "DH-RSA-DES-CBC-SHA", 176 | "DH-DSS-DES-CBC-SHA", 177 | "EXP1024-DHE-DSS-RC4-SHA", 178 | "EXP-EDH-RSA-DES-CBC-SHA", 179 | "EXP-EDH-DSS-DES-CBC-SHA", 180 | "EXP-DH-RSA-DES-CBC-SHA", 181 | "EXP-DH-DSS-DES-CBC-SHA", 182 | "EXP-ADH-DES-CBC-SHA", 183 | "EXP-DES-CBC-SHA", 184 | "EXP-RC2-CBC-MD5", 185 | "EXP-RC2-CBC-MD5", 186 | "EXP-ADH-RC4-MD5", 187 | "ECDHE-ECDSA-NULL-SHA", 188 | "ECDH-RSA-NULL-SHA", 189 | "ECDH-ECDSA-NULL-SHA" 190 | ] 191 | }, 192 | "x509ChainDepth": 1, 193 | "verifyCertResult": false, 194 | "verifyCertError": "self signed certificate", 195 | "verifyHostResult": true, 196 | "ocspStapled": false, 197 | "certificateChain": [ 198 | { 199 | "version": 3, 200 | "subject": "emailAddress=tls-scan@example.com; CN=localhost; OU=tls-scan; O=prbinu; ST=CA; C=US", 201 | "issuer": "emailAddress=tls-scan@example.com; CN=localhost; OU=tls-scan; O=prbinu; ST=CA; C=US", 202 | "subjectCN": "localhost", 203 | "signatureAlg": "sha256WithRSAEncryption", 204 | "notBefore": "Dec 27 17:38:26 2019 GMT", 205 | "notAfter": "Dec 24 17:38:26 2029 GMT", 206 | "expired": false, 207 | "serialNo": "5B:48:7E:E4:F4:6F:6E:22:E2:B9:98:D8:FF:BE:7E:DF:43:4B:7C:0A", 208 | "publicKeyAlg": "RSA", 209 | "publicKeySize": 2048, 210 | "basicConstraints": "CA:TRUE critical", 211 | "subjectKeyIdentifier": "13:49:98:DA:D7:F2:F4:FE:59:63:10:D1:A5:F8:BD:72:3B:42:88:01", 212 | "sha1Fingerprint": "5C:8E:01:7D:8C:6C:44:73:7C:EA:99:34:89:C6:89:B7:2D:E6:90:2B" 213 | } ] 214 | } 215 | 216 | <|---------Scan Summary---------|> 217 | [8565] ciphers : ECDHE-ECDSA-CHACHA20-POLY1305-OLD:ECDHE-RSA-CHACHA20-POLY1305-OLD:DHE-RSA-CHACHA20-POLY1305-OLD:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DH-DSS-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DH-RSA-AES256-SHA256:DH-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DH-RSA-AES256-SHA:DH-DSS-AES256-SHA:ECDHE-RSA-CAMELLIA256-SHA384:ECDHE-ECDSA-CAMELLIA256-SHA384:DHE-RSA-CAMELLIA256-SHA256:DHE-DSS-CAMELLIA256-SHA256:DH-RSA-CAMELLIA256-SHA256:DH-DSS-CAMELLIA256-SHA256:DHE-RSA-CAMELLIA256-SHA:DHE-DSS-CAMELLIA256-SHA:DH-RSA-CAMELLIA256-SHA:DH-DSS-CAMELLIA256-SHA:AECDH-AES256-SHA:ADH-AES256-GCM-SHA384:ADH-AES256-SHA256:ADH-AES256-SHA:ADH-CAMELLIA256-SHA256:ADH-CAMELLIA256-SHA:ECDH-RSA-AES256-GCM-SHA384:ECDH-ECDSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA384:ECDH-ECDSA-AES256-SHA384:ECDH-RSA-AES256-SHA:ECDH-ECDSA-AES256-SHA:ECDH-RSA-CAMELLIA256-SHA384:ECDH-ECDSA-CAMELLIA256-SHA384:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:CAMELLIA256-SHA256:CAMELLIA256-SHA:RSA-PSK-AES256-CBC-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:DH-DSS-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DH-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DH-RSA-AES128-SHA256:DH-DSS-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:DH-RSA-AES128-SHA:DH-DSS-AES128-SHA:ECDHE-RSA-CAMELLIA128-SHA256:ECDHE-ECDSA-CAMELLIA128-SHA256:DHE-RSA-CAMELLIA128-SHA256:DHE-DSS-CAMELLIA128-SHA256:DH-RSA-CAMELLIA128-SHA256:DH-DSS-CAMELLIA128-SHA256:DHE-RSA-SEED-SHA:DHE-DSS-SEED-SHA:DH-RSA-SEED-SHA:DH-DSS-SEED-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-DSS-CAMELLIA128-SHA:DH-RSA-CAMELLIA128-SHA:DH-DSS-CAMELLIA128-SHA:AECDH-AES128-SHA:ADH-AES128-GCM-SHA256:ADH-AES128-SHA256:ADH-AES128-SHA:ADH-CAMELLIA128-SHA256:ADH-SEED-SHA:ADH-CAMELLIA128-SHA:ECDH-RSA-AES128-GCM-SHA256:ECDH-ECDSA-AES128-GCM-SHA256:ECDH-RSA-AES128-SHA256:ECDH-ECDSA-AES128-SHA256:ECDH-RSA-AES128-SHA:ECDH-ECDSA-AES128-SHA:ECDH-RSA-CAMELLIA128-SHA256:ECDH-ECDSA-CAMELLIA128-SHA256:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:CAMELLIA128-SHA256:SEED-SHA:CAMELLIA128-SHA:IDEA-CBC-SHA:IDEA-CBC-MD5:RC2-CBC-MD5:RSA-PSK-AES128-CBC-SHA:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:DHE-DSS-RC4-SHA:AECDH-RC4-SHA:ADH-RC4-MD5:ECDH-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:RC4-SHA:RC4-MD5:RC4-MD5:RSA-PSK-RC4-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DH-RSA-DES-CBC3-SHA:DH-DSS-DES-CBC3-SHA:AECDH-DES-CBC3-SHA:ADH-DES-CBC3-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-ECDSA-DES-CBC3-SHA:DES-CBC3-SHA:DES-CBC3-MD5:RSA-PSK-3DES-EDE-CBC-SHA:RC4-64-MD5:EXP1024-DHE-DSS-DES-CBC-SHA:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DH-RSA-DES-CBC-SHA:DH-DSS-DES-CBC-SHA:ADH-DES-CBC-SHA:EXP1024-DES-CBC-SHA:DES-CBC-SHA:EXP1024-RC2-CBC-MD5:DES-CBC-MD5:EXP1024-DHE-DSS-RC4-SHA:EXP1024-RC4-SHA:EXP1024-RC4-MD5:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA:EXP-DH-RSA-DES-CBC-SHA:EXP-DH-DSS-DES-CBC-SHA:EXP-ADH-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC2-CBC-MD5:EXP-ADH-RC4-MD5:EXP-RC4-MD5:EXP-RC4-MD5:AECDH-NULL-SHA:ECDHE-RSA-NULL-SHA:ECDHE-ECDSA-NULL-SHA:ECDH-RSA-NULL-SHA:ECDH-ECDSA-NULL-SHA:NULL-SHA256:NULL-SHA:NULL-MD5: (165) 218 | [8565] dns-lookup : 1 219 | [8565] network-error : 92 220 | [8565] dns-errcount : 0 221 | [8565] remote-close-error : 0 222 | [8565] unknown-error : 0 223 | [8565] timeout-error : 0 224 | [8565] connect-error : 0 225 | [8565] tls-handshake : 1 226 | [8565] gross-tls-handshake : 80 227 | [8565] elapsed-time : 0.703085 secs 228 | <|------------------------------|> 229 | -------------------------------------------------------------------------------- /tests/rsa-test.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDzzCCAregAwIBAgIUW0h+5PRvbiLiuZjY/75+30NLfAowDQYJKoZIhvcNAQEL 3 | BQAwdzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZwcmJpbnUx 4 | ETAPBgNVBAsMCHRscy1zY2FuMRIwEAYDVQQDDAlsb2NhbGhvc3QxIzAhBgkqhkiG 5 | 9w0BCQEWFHRscy1zY2FuQGV4YW1wbGUuY29tMB4XDTE5MTIyNzE3MzgyNloXDTI5 6 | MTIyNDE3MzgyNlowdzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ8wDQYDVQQK 7 | DAZwcmJpbnUxETAPBgNVBAsMCHRscy1zY2FuMRIwEAYDVQQDDAlsb2NhbGhvc3Qx 8 | IzAhBgkqhkiG9w0BCQEWFHRscy1zY2FuQGV4YW1wbGUuY29tMIIBIjANBgkqhkiG 9 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxMP40ZxqqX4zWs4mKzfjLP1ILhOAeNp3AYD2 10 | qYbSFuX13nge0umd6HkeBoO2t9wPo0c2YMqmegCpMQFUidqZ5AW5pvg2blZHmphA 11 | f3gcv/3gSjAoyBD6mBowdJ2Ylwgii9nZFO+XtqZf9biVaE5wI9wHDrIwqEoEbp+i 12 | i9GTYjxIDgVJDC4t4cPnF5e/4YcakoOymKgaunheBB7r76ppaENGp92HiBySYo9k 13 | A2gDevKfofoywHTfZzTiWcru3S/1KDFVGMuDsuqJQ1HOhsgh5PlBIeJbajbWKI7j 14 | +NToeQo+LJkn1oUoJZNgB5K2kd1hIDNd2qY1PjfcYVhzImoDxwIDAQABo1MwUTAd 15 | BgNVHQ4EFgQUE0mY2tfy9P5ZYxDRpfi9cjtCiAEwHwYDVR0jBBgwFoAUE0mY2tfy 16 | 9P5ZYxDRpfi9cjtCiAEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC 17 | AQEAvNgS1aKtPUc351TQ2CIC30wfobRCPOtTcQlw9RM3mHggJyJ/R4WpPhYCif0n 18 | tYdiZNuJjzlOBoRCVejVCOV8whK/QkXvKt4VJrWCeccKS0AG9Wms/X/W+U38KxNj 19 | mOg2BN+NKQTLbBGh5CtkCl0TplmO2M8hsyINFrk4a5cyCdz4te4mmLKR17Ht9ac+ 20 | 31pHFXbOpbZBzOdZ1OylanQC5eXiPCxPiMra+H0oPKrrse4d9suSe0xLb72XgtrE 21 | gJUfdYTTzi79VKbWvxRbaWd4kts4p4Gf0svYkp24dTxoPJl8cG1F4y7yf22KxxyL 22 | ktXxUaMrSqTxXhilop3PtPFKsQ== 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /tests/rsa-test.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEw/jRnGqpfjNa 3 | ziYrN+Ms/UguE4B42ncBgPaphtIW5fXeeB7S6Z3oeR4Gg7a33A+jRzZgyqZ6AKkx 4 | AVSJ2pnkBbmm+DZuVkeamEB/eBy//eBKMCjIEPqYGjB0nZiXCCKL2dkU75e2pl/1 5 | uJVoTnAj3AcOsjCoSgRun6KL0ZNiPEgOBUkMLi3hw+cXl7/hhxqSg7KYqBq6eF4E 6 | HuvvqmloQ0an3YeIHJJij2QDaAN68p+h+jLAdN9nNOJZyu7dL/UoMVUYy4Oy6olD 7 | Uc6GyCHk+UEh4ltqNtYojuP41Oh5Cj4smSfWhSglk2AHkraR3WEgM13apjU+N9xh 8 | WHMiagPHAgMBAAECggEAcPnImqnBwFG6sM+esL/lC0beM750zG87Vzyx48KKTCV7 9 | V2IG6LjENt+b8sebmNOckvZnbYnNrk1E1+NYo9JE+9yhrRHlRtL5R/VVSxsu/rmJ 10 | F2+xarX8/GpFGiAGpelA1KWfc9rhdDjw7ggumPY4gw5pavG66FWfV9kGnlsV0Zeh 11 | kVenXCByKX594vkMT/S7g//Xtfpda6VxQu38quvpE6EFFJSMx60qaKbrsE3tgXkI 12 | H/BlFtgVpw/dilIvwPvJWzSne4dnbchPGgIQxeSNeJvBRKo9hJtXyfb9peKDQILg 13 | X4yNsbjZ8/IOHuxY7x4u5PxGsVvH2ar1KSiE/S7NKQKBgQD8Y3RGzVkqFrG3XnBu 14 | 3Q0ihnZYWguhijNEa10yZzvlqeU5QzxoRciCYAz9kPp2itXrfgA4ABTY6o1kg5op 15 | nVsXPIqXutmlPUbFNX/ql1qCcETCeymD8PnjseoYLxaNfW50blgijHvoof9B0+0b 16 | 9LDzTnSAEa6I8VBCtQr7he6SawKBgQDHlMKucb/ArfEbOonaXljlIsXKfGzXTgj+ 17 | aIiUfyCxLc+dRyx8CJ3dH2TPeyVA6KNzrEo5StBDH5h/9v9pjcXXzDFmLpFRPjAm 18 | 5zsLCK4BqO6i4fDkfUDBFw8zpAZy2vcyjuPh4Ff9xMR0Z/J7V86ChT+ey80cQ/Wv 19 | KC92vhJDFQKBgA16kj8lwFlLFPqiBIcnLwhk/fUB+hUKeGpzAWsrsDs3s5I3UQaQ 20 | k4DC+G6iS34BNCN7ey+CcZqUDDEOBjGqwa6gARbRV8ZHW3w0uzwgESleLY3lXfY5 21 | 7F6IpePcv+uWg0uciQBvWD64SBWP593SV5Dm47njN9sAaDTnzkpYgME5AoGAHXK/ 22 | vwmdp4qC1ueuS4A8UaLt2FC/po0nGpLGBFM1ua7uwWAtGigTUvs83rhm6Mrqwzia 23 | wxVc4WqaD9ecHDEFDjBc4ZzhaEFqzXK07Y3cTvZkL7hy2OP6WANVBOOg5/WTH+yh 24 | h0SY9yK/cgWca8Kq/+s1VfLwUslcSPn+9uBYxXkCgYEAmicILbGq4jpOVcvTkKVK 25 | yeRsE20t6eNYKOCxFE68ZjFUlzugm+se3eEwLNCD7VqBDaFc4y8F518dab6sYLw1 26 | ch3+td0mEZyTRIoK1IRg+2Kd2eTNXw4gVwrVPjRAwJAMgVM6p3jV0dQ9qdQSG4ss 27 | xR/yBojbdhb+89sJ8M9SCnE= 28 | -----END PRIVATE KEY----- 29 | --------------------------------------------------------------------------------