From 7b208d54971cde2b11765f96594e1d96aca8c396 Mon Sep 17 00:00:00 2001
From: bewue <>
Date: Fri, 16 Oct 2020 23:52:52 +0200
Subject: [PATCH] initial commit
---
.gitignore | 4 +
LICENSE | 678 ++++++++++++++++++
README.md | 13 +
src/org/bitbatzen/tlsserverscanner/Main.java | 37 +
src/org/bitbatzen/tlsserverscanner/Util.java | 239 ++++++
.../bitbatzen/tlsserverscanner/gui/Area.java | 70 ++
.../tlsserverscanner/gui/AreaControls.java | 278 +++++++
.../tlsserverscanner/gui/AreaHosts.java | 161 +++++
.../tlsserverscanner/gui/AreaLog.java | 93 +++
.../tlsserverscanner/gui/AreaResults.java | 297 ++++++++
.../tlsserverscanner/gui/DialogConfirm.java | 93 +++
.../tlsserverscanner/gui/DialogMessage.java | 79 ++
.../tlsserverscanner/gui/DialogNewHost.java | 113 +++
.../tlsserverscanner/gui/DialogSearch.java | 340 +++++++++
.../gui/DialogSelectCipherSuites.java | 355 +++++++++
.../gui/DialogSelectCipherSuitesData.java | 13 +
.../gui/DialogSelectProtocols.java | 173 +++++
.../gui/DialogViewCertificates.java | 193 +++++
.../gui/HostListCellRenderer.java | 57 ++
.../tlsserverscanner/gui/HostListItem.java | 124 ++++
.../tlsserverscanner/gui/HostListModel.java | 40 ++
.../tlsserverscanner/gui/MainWindow.java | 199 +++++
.../tlsserverscanner/gui/MyMenuBar.java | 376 ++++++++++
.../tlsserverscanner/gui/StatisticUtil.java | 269 +++++++
.../tlsserverscanner/scantask/Cert.java | 206 ++++++
.../scantask/IScanTaskHandlerListener.java | 28 +
.../scantask/IScanTaskListener.java | 26 +
.../tlsserverscanner/scantask/SSLUtil.java | 162 +++++
.../tlsserverscanner/scantask/ScanData.java | 156 ++++
.../tlsserverscanner/scantask/ScanTask.java | 513 +++++++++++++
.../scantask/ScanTaskHandler.java | 357 +++++++++
31 files changed, 5742 insertions(+)
create mode 100644 .gitignore
create mode 100644 LICENSE
create mode 100644 README.md
create mode 100644 src/org/bitbatzen/tlsserverscanner/Main.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/Util.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/Area.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/AreaControls.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/AreaHosts.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/AreaLog.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/AreaResults.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/DialogConfirm.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/DialogMessage.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/DialogNewHost.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/DialogSearch.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/DialogSelectCipherSuites.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/DialogSelectCipherSuitesData.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/DialogSelectProtocols.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/DialogViewCertificates.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/HostListCellRenderer.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/HostListItem.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/HostListModel.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/MainWindow.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/MyMenuBar.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/gui/StatisticUtil.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/scantask/Cert.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/scantask/IScanTaskHandlerListener.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/scantask/IScanTaskListener.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/scantask/SSLUtil.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/scantask/ScanData.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/scantask/ScanTask.java
create mode 100644 src/org/bitbatzen/tlsserverscanner/scantask/ScanTaskHandler.java
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3741e00
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/bin/
+.settings
+.project
+.classpath
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8a83a04
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,678 @@
+Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a70942a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+# TLSServerScanner
+
+Scan TLS servers to get information about the supported TLS techniques.
+TLSServerScanner is designed to handle a lot of hosts (> 1000) to get
+meaningful statistics.
+
+- Selectable cipher suites for fetching the certificates
+- Selectable cipher suites for testing
+- Selectable TLS protocol versions for testing
+- View and save the fetched certificates
+- Full-Text search in scan results and certificates
+- Statistics display
+- Multithreaded scans
diff --git a/src/org/bitbatzen/tlsserverscanner/Main.java b/src/org/bitbatzen/tlsserverscanner/Main.java
new file mode 100644
index 0000000..22a9b76
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner;
+
+import javax.swing.SwingUtilities;
+
+import org.bitbatzen.tlsserverscanner.gui.MainWindow;
+
+
+public class Main {
+
+
+ public static void main(String args[]) throws Exception {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ new MainWindow();
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/org/bitbatzen/tlsserverscanner/Util.java b/src/org/bitbatzen/tlsserverscanner/Util.java
new file mode 100644
index 0000000..80009e4
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/Util.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.bitbatzen.tlsserverscanner.gui.HostListItem;
+import org.bitbatzen.tlsserverscanner.scantask.Cert;
+
+
+public class Util {
+
+ public static final String T_APP_NAME = "TLSServerScanner";
+ public static final String T_APP_VERSION = "1.0.1";
+ public static final String T_APP_LICENSE = "GPLv3";
+ public static final String T_AUTOR = "Benjamin W.";
+ public static final String T_CONTACT_EMAIL = "bitbatzen@gmail.com";
+ public static final String T_CODE_URL = "https://github.com/bewue/tlsserverscanner";
+
+ public static final String LF = System.lineSeparator();
+ public static final String BR = "
";
+
+ public static final String COLOR_END_TAG = "";
+ public static final String FONT_END_TAG = "";
+
+
+ public static String getJavaVersionString() {
+ return System.getProperty("java.version");
+ }
+
+ public static int getMajorJavaVersion() {
+ String[] javaVersionElements = System.getProperty("java.version").split("\\.");
+ return Integer.parseInt(javaVersionElements[1]);
+ }
+
+ public static int extractPort(String hostAndPort) {
+ int port = -1;
+ try {
+ URI uri = new URI("test://" + hostAndPort);
+ port = uri.getPort();
+ }
+ catch (URISyntaxException ex) {
+ return -1;
+ }
+
+ return isPortValid(port) ? port : -1;
+ }
+
+ public static String extractHost(String hostAndPort) {
+ String host = null;
+ try {
+ URI uri = new URI("test://" + hostAndPort);
+ host = uri.getHost();
+ }
+ catch (URISyntaxException ex) {
+ return null;
+ }
+
+ return host;
+ }
+
+ public static boolean isPortValid(int port) {
+ return (port >= 0 && port <= 65535);
+ }
+
+ public static boolean checkHostString(String hostAndPort) {
+ return (extractHost(hostAndPort) != null && extractPort(hostAndPort) != -1);
+ }
+
+ public static String getBGColorTag(Color c) {
+ String hex = Integer.toHexString(c.getRGB()).substring(2).toUpperCase();
+ return "";
+ }
+
+ public static String getFGColorTag(Color c) {
+ String hex = Integer.toHexString(c.getRGB()).substring(2).toUpperCase();
+ return "";
+ }
+
+ public static String getFontFamilyTag(Font font) {
+ String tag = "";
+ return tag;
+ }
+
+ public static void saveHostlistToFile(String hostlist, File file) throws Exception {
+ OutputStreamWriter osw = null;
+ try {
+ FileOutputStream fos = new FileOutputStream(file);
+ osw = new OutputStreamWriter(fos, "UTF-8");
+ osw.write(hostlist);
+ osw.flush();
+ }
+ catch (Exception e) {
+ throw e;
+ }
+ finally {
+ close(osw);
+ }
+ }
+
+ /**
+ * @param hostlistToFill
+ * @param file
+ * @return 0 on success or the line number with the syntax error
+ * @throws Exception
+ */
+ public static int loadHostlistFromFile(List hostlistToFill, File file) throws Exception {
+// List hostlist = new ArrayList();
+ BufferedReader br = null;
+ try {
+ FileReader fileReader = new FileReader(file);
+ br = new BufferedReader(fileReader);
+ String line;
+ int lineCounter = 1;
+ while ((line = br.readLine()) != null) {
+ if (checkHostString(line)) {
+ hostlistToFill.add(line);
+ }
+ else {
+ return lineCounter;
+ }
+ lineCounter++;
+ }
+ }
+ catch (Exception e) {
+ throw e;
+ }
+ finally {
+ close(br);
+ }
+
+ return 0;
+ }
+
+ public static void saveCertificatesToDirectory(List hostlist, File directory) throws Exception {
+ FileOutputStream fos = null;
+ try {
+ for (HostListItem host : hostlist) {
+ if (host.getScanTask() == null || host.getScanTask().getScanData().getCertAvailable() == false) {
+ continue;
+ }
+
+ String id = host.getHost() + "_" + host.getPort();
+ File subDirectory = new File(directory.getPath() + File.separator + id);
+ subDirectory.mkdir();
+
+ Cert[] certs = host.getScanTask().getScanData().certs;
+ for (int i = 0; i < certs.length; i++) {
+ Cert cert = certs[i];
+ String filename = "";
+ if (i == 0) {
+ filename = id + ".cert";
+ }
+ else {
+ filename = "issuer_" + i + ".cert";
+ }
+
+ File file = new File(subDirectory.getPath() + File.separator + filename);
+ byte[] buffer = cert.getCert().getEncoded();
+ fos = new FileOutputStream(file);
+ fos.write(buffer);
+ fos.close();
+ fos.flush();
+ }
+ }
+ }
+ catch (Exception e) {
+ throw e;
+ }
+ finally {
+ close(fos);
+ }
+ }
+
+ public static void close(Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ }
+ catch (IOException e) {
+ }
+ }
+ }
+
+ public static String bytesToHex(byte[] bytes, boolean addWhitespaces) {
+ final char[] hexArray = "0123456789ABCDEF".toCharArray();
+ char[] hexChars = new char[bytes.length * 2];
+ for (int i = 0; i < bytes.length; i++) {
+ int v = bytes[i] & 0xFF;
+ hexChars[i * 2] = hexArray[v >>> 4];
+ hexChars[i * 2 + 1] = hexArray[v & 0x0F];
+ }
+
+ if (addWhitespaces) {
+ StringBuilder sb = new StringBuilder(128);
+ for (int i = 0; i < hexChars.length; i++) {
+ if (i % 2 == 0) {
+ sb.append(hexChars[i]);
+ }
+ else {
+ sb.append(hexChars[i] + " ");
+ }
+ }
+
+ return sb.toString();
+ }
+ else {
+ return new String(hexChars);
+ }
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/Area.java b/src/org/bitbatzen/tlsserverscanner/gui/Area.java
new file mode 100644
index 0000000..fba0bde
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/Area.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import javax.swing.JPanel;
+
+import org.bitbatzen.tlsserverscanner.scantask.*;
+
+
+public class Area implements IScanTaskHandlerListener {
+
+ public final static int BORDER_WIDTH = 2;
+
+ protected ScanTaskHandler scanTaskHandler;
+
+ protected JPanel panel;
+
+ protected MainWindow mainWindow;
+
+
+ public Area(MainWindow mainWindow) {
+ this.mainWindow = mainWindow;
+
+ panel = new JPanel();
+
+ scanTaskHandler = null;
+ }
+
+ public JPanel getPanel() {
+ return panel;
+ }
+
+ public void setPanelBounds(int x, int y, int width, int height) {
+ panel.setBounds(x, y, width, height);
+ }
+
+ public void setScanTaskHandler(ScanTaskHandler scanTaskHandler) {
+ this.scanTaskHandler = scanTaskHandler;
+ scanTaskHandler.addListener(this);
+ }
+
+ public ScanTaskHandler getScanTaskHandler() {
+ return scanTaskHandler;
+ }
+
+ @Override
+ public synchronized void onScanTaskHandlerMessage(ScanTask scanTask, String message) {
+ }
+
+ @Override
+ public synchronized void onScanTaskHandlerDone() {
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/AreaControls.java b/src/org/bitbatzen/tlsserverscanner/gui/AreaControls.java
new file mode 100644
index 0000000..b49039c
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/AreaControls.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JToggleButton;
+import javax.swing.SwingUtilities;
+
+import org.bitbatzen.tlsserverscanner.Util;
+import org.bitbatzen.tlsserverscanner.gui.DialogSelectCipherSuites.DialogType;
+import org.bitbatzen.tlsserverscanner.scantask.*;
+
+
+public class AreaControls extends Area implements ActionListener {
+
+ private static final int SPACER_SMALL = 5;
+
+ private JButton buttonNewHost;
+ private JButton buttonDeleteHosts;
+
+ private JButton buttonStartScan;
+ private JButton buttonStopScan;
+
+ private JToggleButton buttonStatistic;
+
+ private JToggleButton buttonPlainText;
+// private JButton buttonClearLog;
+
+
+ public AreaControls(MainWindow mainWindow) {
+ super(mainWindow);
+
+ panel.setBackground(MainWindow.COLOR_BG_CONTROLS);
+
+ BoxLayout layout = new BoxLayout(panel, BoxLayout.X_AXIS);
+ panel.setLayout(layout);
+ panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
+ // button new host
+ buttonNewHost = new JButton("+");
+ buttonNewHost.addActionListener(this);
+ panel.add(buttonNewHost);
+ panel.add(Box.createRigidArea(new Dimension(SPACER_SMALL, 0)));
+ // button delete host(s)
+ buttonDeleteHosts = new JButton("-");
+ buttonDeleteHosts.addActionListener(this);
+ panel.add(buttonDeleteHosts);
+
+// panel.add(Box.createRigidArea(new Dimension(57, 0)));
+
+ // button statistic
+ buttonStatistic = new JToggleButton("Statistic");
+ buttonStatistic.addActionListener(this);
+ panel.add(buttonStatistic);
+
+// panel.add(Box.createRigidArea(new Dimension(50, 0)));
+
+ // button start scan
+ buttonStartScan = new JButton("Start");
+ buttonStartScan.addActionListener(this);
+ panel.add(buttonStartScan);
+ panel.add(Box.createRigidArea(new Dimension(SPACER_SMALL, 0)));
+ // button stop scan
+ buttonStopScan = new JButton("Stop");
+ buttonStopScan.addActionListener(this);
+ panel.add(buttonStopScan);
+
+ // button plain text
+ buttonPlainText = new JToggleButton("Plain Text");
+ buttonPlainText.addActionListener(this);
+ panel.add(buttonPlainText);
+
+ updateGUIElements();
+ }
+
+ private void onClickNewHost() {
+ new DialogNewHost(mainWindow);
+ }
+
+ private void onClickDeleteHosts() {
+ if (mainWindow.getAreaHosts().getHostListItemCount() == 0) {
+ mainWindow.showMessageDialog("Delete Hosts(s)", "No host(s) to delete!");
+ return;
+ }
+ else if (mainWindow.getAreaHosts().getSelectedItem() == null) {
+ mainWindow.showMessageDialog("Delete Hosts(s)", "No host selected!");
+ return;
+ }
+ else if (mainWindow.showConfirmDialog("Delete Host(s)", "Delete selected host(s)?")) {
+ mainWindow.getAreaHosts().removeSelectedHosts();
+ }
+ }
+
+ private void onClickStartScan() {
+ HostListItem[] hostListItems = mainWindow.getAreaHosts().getHostListItems();
+ if (hostListItems.length == 0) {
+ mainWindow.showMessageDialog("Start Scan", "Hostlist is empty!");
+ return;
+ }
+ else {
+ String duplicate = mainWindow.getAreaHosts().checkHostDuplicates();
+ if (duplicate != null) {
+ String text = "Warning:" + Util.BR + Util.BR + "Duplicate in list:" + Util.BR + duplicate + "";
+ if (mainWindow.showConfirmDialog("Start Scan", text) == false) {
+ return;
+ }
+ }
+ }
+
+ boolean certCollectingDisabled = mainWindow.getMyMenuBar().getCertCollectingDisabled();
+ boolean certVerificationDisabled = mainWindow.getMyMenuBar().getCertVerificationDisabled();
+
+ String startScanPopupText = getStartScanPopupText(certCollectingDisabled, certVerificationDisabled);
+ if (mainWindow.showConfirmDialog("Start Scan", startScanPopupText) == false) {
+ return;
+ }
+
+ ScanTaskHandler scanTaskHandler = new ScanTaskHandler();
+ scanTaskHandler.init(certCollectingDisabled == false, certVerificationDisabled == false);
+ scanTaskHandler.setCipherSuitesForCollectingCert(DialogSelectCipherSuites.getSelectedCipherSuites(DialogType.CIPHER_SUITES_FOR_COLLECTING_CERT));
+ scanTaskHandler.setCipherSuitesToTest(DialogSelectCipherSuites.getSelectedCipherSuites(DialogType.CIPHER_SUITES_TO_TEST));
+ scanTaskHandler.setProtocolsToTest(DialogSelectProtocols.getSelectedProtocols());
+ setScanTaskHandler(scanTaskHandler);
+
+ // create the ScanTasks
+ for (HostListItem hli : hostListItems) {
+ ScanTask scanTask = new ScanTask(hli.getHost(), hli.getPort());
+ scanTaskHandler.addScanTask(scanTask);
+ hli.setScanTask(scanTask);
+ }
+
+ mainWindow.getAreaHosts().setScanTaskHandler(scanTaskHandler);
+ mainWindow.getAreaLog().setScanTaskHandler(scanTaskHandler);
+ mainWindow.getAreaResults().setScanTaskHandler(scanTaskHandler);
+
+ mainWindow.getAreaResults().updateView();
+ mainWindow.getAreaHosts().updateAllHosts();
+
+ scanTaskHandler.startScans();
+
+ updateGUIElements();
+ }
+
+ private void onClickStopScan() {
+ if (mainWindow.showConfirmDialog("Stop Scan", "Stop the current scan?")) {
+ scanTaskHandler.stopScans();
+ updateGUIElements();
+ }
+ }
+
+ private void onClickStatistic() {
+ mainWindow.getAreaResults().updateView();
+ }
+
+ private void onClickPlainText() {
+ mainWindow.getAreaResults().setPlainTextEnabled(buttonPlainText.isSelected());
+ }
+
+// private void onClickClearLog() {
+// mainWindow.getAreaLog().clearLog();
+// }
+
+ public void unselectStatisticButtons() {
+ buttonStatistic.setSelected(false);
+ }
+
+ public boolean getStatisticSelected() {
+ return buttonStatistic.isSelected();
+ }
+
+ public void updateGUIElements() {
+ boolean scanRunning = (scanTaskHandler != null && scanTaskHandler.getScansRunning());
+
+ buttonNewHost.setEnabled(!scanRunning);
+ buttonDeleteHosts.setEnabled(!scanRunning);
+
+ buttonStartScan.setEnabled(!scanRunning);
+ buttonStopScan.setEnabled(scanRunning);
+ }
+
+ private String getStartScanPopupText(boolean certCollectingDisabled, boolean certVerificationDisabled) {
+ String hostString = (mainWindow.getAreaHosts().getHostListItemCount() == 1) ? "host" : "hosts";
+ String certCollectingString = certCollectingDisabled ? "- Certificate collecting is disabled!"
+ : "- Certificate collecting is enabled!";
+ String certVerString = certVerificationDisabled ? Util.getBGColorTag(Color.RED) + "DISABLED!" + Util.COLOR_END_TAG
+ : "enabled!";
+
+ int cipherSuitesToTestCount = DialogSelectCipherSuites.getSelectedCipherSuites(DialogType.CIPHER_SUITES_TO_TEST).size();
+
+ String text = ""
+ + "- " + mainWindow.getAreaHosts().getHostListItemCount() + " " + hostString + " to scan!"
+ + "
- " + cipherSuitesToTestCount + " cipher suites to test!"
+ + "
- " + DialogSelectProtocols.getSelectedProtocols().size() + " protocol versions to test!"
+ + "
" + certCollectingString
+ + "
- Certificate verification is " + certVerString
+ + "
Current scan data will be lost!"
+ + "
Start scan?";
+
+ return text;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == buttonNewHost) {
+ onClickNewHost();
+ }
+ else if (e.getSource() == buttonDeleteHosts) {
+ onClickDeleteHosts();
+ }
+ else if (e.getSource() == buttonStartScan) {
+ onClickStartScan();
+ }
+ else if (e.getSource() == buttonStopScan) {
+ onClickStopScan();
+ }
+ else if (e.getSource() == buttonStatistic) {
+ onClickStatistic();
+ }
+ else if (e.getSource() == buttonPlainText) {
+ onClickPlainText();
+ }
+// else if (e.getSource() == buttonClearLog) {
+// onClickClearLog();
+// }
+ }
+
+ @Override
+ public void setPanelBounds(int x, int y, int width, int height) {
+ super.setPanelBounds(x, y, width, height);
+
+ buttonNewHost.setBounds(buttonNewHost.getX(), buttonNewHost.getY(), 45, buttonNewHost.getHeight());
+ buttonDeleteHosts.setBounds(buttonDeleteHosts.getX(), buttonDeleteHosts.getY(), 45, buttonDeleteHosts.getHeight());
+
+ int posXButtonStatistic = ((MainWindow.HOST_AREA_WIDTH - buttonDeleteHosts.getX() + buttonStatistic.getWidth() / 2)) / 2;
+ buttonStatistic.setBounds(posXButtonStatistic, buttonStatistic.getY(), buttonStatistic.getWidth(), buttonStatistic.getHeight());
+
+ buttonStartScan.setBounds(MainWindow.HOST_AREA_WIDTH, buttonStartScan.getY(), buttonStartScan.getWidth(), buttonStartScan.getHeight());
+ int posXButtonStopScan = buttonStartScan.getX() + buttonStartScan.getWidth() + SPACER_SMALL;
+ buttonStopScan.setBounds(posXButtonStopScan, buttonStopScan.getY(), buttonStopScan.getWidth(), buttonStopScan.getHeight());
+
+ int posXButtonPlainText = Math.max(width - buttonPlainText.getWidth() - 5, buttonStopScan.getX() + buttonStopScan.getWidth() + 20);
+ buttonPlainText.setBounds(posXButtonPlainText, buttonPlainText.getY(), buttonPlainText.getWidth(), buttonPlainText.getHeight());
+ }
+
+ @Override
+ public synchronized void onScanTaskHandlerDone() {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ updateGUIElements();
+ }
+ });
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/AreaHosts.java b/src/org/bitbatzen/tlsserverscanner/gui/AreaHosts.java
new file mode 100644
index 0000000..8153f68
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/AreaHosts.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JList;
+import javax.swing.JScrollPane;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import org.bitbatzen.tlsserverscanner.Util;
+
+
+public class AreaHosts extends Area {
+
+ private JScrollPane scrollPane;
+ private JList list;
+
+ private HostListModel listModel;
+
+
+ public AreaHosts(final MainWindow mainWindow) {
+ super(mainWindow);
+
+ listModel = new HostListModel();
+ list = new JList(listModel);
+ list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+ list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
+ list.setVisibleRowCount(-1);
+ list.setCellRenderer(new HostListCellRenderer());
+ list.setBackground(MainWindow.COLOR_BG_HOSTS);
+
+ list.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ mainWindow.getAreaControls().unselectStatisticButtons();
+ mainWindow.getAreaResults().updateView();
+ }
+ });
+ list.addFocusListener(new FocusListener() {
+ @Override
+ public void focusLost(FocusEvent e) {
+ }
+ @Override
+ public void focusGained(FocusEvent e) {
+ mainWindow.getAreaControls().unselectStatisticButtons();
+ mainWindow.getAreaResults().updateView();
+ }
+ });
+
+ scrollPane = new JScrollPane(list);
+ panel.add(scrollPane);
+ }
+
+ public void addHost(String host, int port) {
+ listModel.addElement(new HostListItem(host, port, listModel));
+ updateAllHosts();
+ }
+
+ public void updateAllHosts() {
+ for (int i = 0; i < listModel.size(); i++) {
+ listModel.get(i).update();
+ }
+ }
+
+ public HostListItem[] getHostListItems() {
+ HostListItem[] hostItems = new HostListItem[listModel.size()];
+ for (int i = 0; i < listModel.size(); i++) {
+ hostItems[i] = listModel.get(i);
+ }
+
+ return hostItems;
+ }
+
+ public String getHostList() {
+ StringBuilder sb = new StringBuilder(2000);
+ for (int i = 0; i < listModel.size(); i++) {
+ sb.append(listModel.get(i).getHostWithPort() + Util.LF);
+ }
+
+ return sb.toString();
+ }
+
+ public int getHostListItemCount() {
+ return listModel.size();
+ }
+
+ public List getSelectedHosts() {
+ List hosts = new ArrayList<>();
+ int[] selectedHosts = list.getSelectedIndices();
+ for (int i = 0; i < selectedHosts.length; i++) {
+ hosts.add(listModel.get(selectedHosts[i]));
+ }
+
+ return hosts;
+ }
+
+ public void removeSelectedHosts() {
+ int[] selectedHosts = list.getSelectedIndices();
+ for (int i = selectedHosts.length-1; i >= 0; i--) {
+ listModel.removeElementAt(selectedHosts[i]);
+ }
+
+ updateAllHosts();
+ mainWindow.getAreaResults().updateView();
+ }
+
+ public void removeAllHosts() {
+ listModel.clear();
+ mainWindow.getAreaResults().updateView();
+ }
+
+ public String checkHostDuplicates() {
+ for (int i = 0; i < listModel.size(); i++) {
+ String h1 = listModel.get(i).getHostWithPort();
+ for (int k = 0; k < listModel.size(); k++) {
+ String h2 = listModel.get(k).getHostWithPort();
+ if (i != k && h1.equals(h2)) {
+ return h1;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public void setPanelBounds(int x, int y, int width, int height) {
+ panel.setBounds(x, y, width, height);
+
+ list.setBounds(0, 0, width, height);
+ list.setFixedCellWidth(width - BORDER_WIDTH - 1);
+
+ scrollPane.setBounds(0, 0, width, height);
+ }
+
+ public HostListItem getSelectedItem() {
+ return (HostListItem) list.getSelectedValue();
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/AreaLog.java b/src/org/bitbatzen/tlsserverscanner/gui/AreaLog.java
new file mode 100644
index 0000000..5c4a5ea
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/AreaLog.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.SwingUtilities;
+import javax.swing.text.DefaultCaret;
+
+import org.bitbatzen.tlsserverscanner.Util;
+import org.bitbatzen.tlsserverscanner.scantask.*;
+
+
+public class AreaLog extends Area {
+
+ private JTextArea textArea;
+ private JScrollPane scrollPane;
+
+ private static final int LOG_TRIM_SIZE = 10000;
+ private static final int LOG_TRIM_UPDATE_SIZE = 15000;
+ private StringBuilder log;
+
+
+ public AreaLog(MainWindow mainWindow) {
+ super(mainWindow);
+
+ panel.setLayout(null);
+ textArea = new JTextArea();
+ textArea.setEditable(false);
+ textArea.setFont(MainWindow.FONT_LOG);
+ textArea.setBackground(MainWindow.COLOR_BG_LOG);
+
+ DefaultCaret caret = (DefaultCaret) textArea.getCaret();
+ caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
+
+ scrollPane = new JScrollPane(textArea);
+ panel.add(scrollPane);
+
+ log = new StringBuilder(LOG_TRIM_UPDATE_SIZE + 300);
+ }
+
+ public void addLogMessage(String message) {
+ if (message != null && message.equals("") == false) {
+ if (log.length() >= LOG_TRIM_UPDATE_SIZE) {
+ int newStart = log.length() - LOG_TRIM_SIZE;
+ int correctedStart = log.indexOf(Util.LF, newStart);
+ log = log.replace(0, correctedStart, "...");
+ }
+
+ log.append(message + Util.LF);
+ textArea.setText(log.toString());
+ }
+ }
+
+ public void clearLog() {
+ log.setLength(0);
+ textArea.setText("");
+ }
+
+ @Override
+ public void setPanelBounds(int x, int y, int width, int height) {
+ super.setPanelBounds(x, y, width, height);
+
+ textArea.setBounds(0, 0, width, height);
+ scrollPane.setBounds(0, 0, width, height);
+ }
+
+ @Override
+ public synchronized void onScanTaskHandlerMessage(ScanTask scanTask, final String message) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ addLogMessage(message);
+ }
+ });
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/AreaResults.java b/src/org/bitbatzen/tlsserverscanner/gui/AreaResults.java
new file mode 100644
index 0000000..ede5e63
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/AreaResults.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JEditorPane;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.text.DefaultCaret;
+
+import org.bitbatzen.tlsserverscanner.Util;
+import org.bitbatzen.tlsserverscanner.scantask.*;
+import org.bitbatzen.tlsserverscanner.scantask.ScanTask.State;
+
+
+public class AreaResults extends Area {
+
+ private static final String H1_S = "";
+ private static final String H1_E = "";
+ private static final String H2_S = "";
+ private static final String H2_E = "";
+ private static final String BR = Util.BR;
+
+ private JEditorPane textArea;
+ private JScrollPane scrollPane;
+
+ private ScanTask currentScanTask;
+
+ private boolean plainTextEnabled;
+
+
+ public AreaResults(MainWindow mainWindow) {
+ super(mainWindow);
+
+ panel.setLayout(null);
+ textArea = new JEditorPane();
+ textArea.setFont(MainWindow.FONT_MEDIUM);
+ textArea.setEditable(false);
+
+ setPlainTextEnabled(false);
+
+ textArea.setBackground(MainWindow.COLOR_BG_RESULTS);
+
+ DefaultCaret caret = (DefaultCaret) textArea.getCaret();
+ caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
+
+ scrollPane = new JScrollPane(textArea);
+ panel.add(scrollPane);
+
+ currentScanTask = null;
+ }
+
+ @Override
+ public void setPanelBounds(int x, int y, int width, int height) {
+ super.setPanelBounds(x, y, width, height);
+
+ textArea.setBounds(0, 0, width, height);
+ scrollPane.setBounds(0, 0, width, height);
+ }
+
+ private String getScanTaskStateMessage(ScanTask scanTask) {
+ ScanTask.State state = ScanTask.getState(scanTask);
+ if (state == ScanTask.State.ERROR) {
+ return ScanTask.getStateTag(state) + " " + scanTask.getScanData().getErrorInfo();
+ }
+ else {
+ return ScanTask.getStateTag(state);
+ }
+ }
+
+ public void setPlainTextEnabled(boolean enabled) {
+ plainTextEnabled = enabled;
+ String contentType = plainTextEnabled ? "text/txt" : "text/html";
+ textArea.setContentType(contentType);
+
+ updateView();
+ }
+
+ public void updateView() {
+ String result = "";
+ if (mainWindow.getAreaControls().getStatisticSelected()) {
+ result = getHTMLStatistic();
+ }
+ else {
+ HostListItem item = mainWindow.getAreaHosts().getSelectedItem();
+ if (item != null) {
+ if (item.getScanTask() != null) {
+ currentScanTask = item.getScanTask();
+ result = getHTMLHostResults(currentScanTask);
+ }
+ else {
+ result = getHTMLHostInfo(item);
+ }
+ }
+ }
+
+ if (plainTextEnabled) {
+ result = result.replace("
", Util.LF);
+ result = result.replaceAll("\\<.*?>", "");
+ }
+
+ textArea.setText(result);
+ }
+
+ private String getHTMLHostInfo(HostListItem item) {
+ String text = Util.getFontFamilyTag(MainWindow.FONT_MEDIUM)
+ + H1_S + item.getHost() + ":" + item.getPort() + H1_E + BR
+ + Util.getBGColorTag(ScanTask.getStateColor(ScanTask.State.NONE)) + H2_S + getScanTaskStateMessage(item.getScanTask()) + H2_E + Util.COLOR_END_TAG
+ + Util.FONT_END_TAG;
+ return text;
+ }
+
+ private void appendCipherSuites(StringBuilder sb, ScanTask scanTask, boolean supported) {
+ HashMap map = scanTask.getScanData().cipherSuitesTested;
+ for (Map.Entry entry : map.entrySet()) {
+ String cipherSuite = scanTask.getScanTaskHandler().getCipherSuiteToTest(entry.getKey());
+ if (entry.getValue() == supported) {
+ sb.append(cipherSuite + BR);
+ }
+ }
+ }
+
+ private void appendProtocols(StringBuilder sb, ScanTask scanTask, boolean supported) {
+ HashMap map = scanTask.getScanData().protocolsTested;
+ for (Map.Entry entry : map.entrySet()) {
+ String cipherSuite = scanTask.getScanTaskHandler().getProtocolToTest(entry.getKey());
+ if (entry.getValue() == supported) {
+ sb.append(cipherSuite + BR);
+ }
+ }
+ }
+
+ private String getHTMLHostResults(ScanTask scanTask) {
+ if (scanTask == null) {
+ return "";
+ }
+
+ ScanTask.State state = ScanTask.getState(scanTask);
+ ScanData sd = scanTask.getScanData();
+ StringBuilder sb = new StringBuilder(2500);
+
+ sb.append(Util.getFontFamilyTag(MainWindow.FONT_MEDIUM));
+
+ sb.append(H1_S + sd.getHostWithPort() + " (" + sd.getHostAddress() + ")" + H1_E + BR);
+ sb.append(Util.getBGColorTag(ScanTask.getStateColor(state))
+ + H2_S + getScanTaskStateMessage(scanTask) + H2_E + BR + Util.COLOR_END_TAG);
+ sb.append(BR);
+
+ if (scanTask.getScanTaskHandler().getProtocolsToTestCount() > 0) {
+ sb.append(H2_S + "Supported Protocols:" + H2_E + BR);
+ appendProtocols(sb, scanTask, true);
+ sb.append(BR);
+
+ sb.append(H2_S + "NOT Supported Protocols:" + H2_E + BR);
+ appendProtocols(sb, scanTask, false);
+ sb.append(BR);
+ sb.append(BR);
+ }
+
+ if (scanTask.getScanTaskHandler().getCipherSuitesToTestCount() > 0) {
+ sb.append(H2_S + "Supported Cipher Suites:" + H2_E + BR);
+ appendCipherSuites(sb, scanTask, true);
+ sb.append(BR);
+
+ sb.append(H2_S + "NOT Supported Cipher Suites:" + H2_E + BR);
+ appendCipherSuites(sb, scanTask, false);
+ sb.append(BR);
+ sb.append(BR);
+ }
+
+ if (scanTask.getScanData().getCertAvailable() == false) {
+ return sb.toString();
+ }
+
+ sb.append(H2_S + "Cipher Suite Chosen By Server:" + H2_E + BR);
+ sb.append(sd.cipherSuiteChosenByServer + BR);
+ sb.append(BR);
+
+ sb.append(H2_S + "Certificate Public Key Algorithm:" + H2_E + BR);
+ sb.append(sd.certs[0].getPublicKeyAlgorithmWithKeyLength() + BR);
+ sb.append(BR);
+
+ sb.append(H2_S + "Certificate Signature Algorithm:" + H2_E + BR);
+ sb.append(sd.certs[0].getSignatureAlgorithm() + BR);
+ sb.append(BR);
+
+ sb.append(H2_S + "Certificate Extensions:" + H2_E + BR);
+ List oids = sd.certs[0].getExtensionOIDsWithName();
+ for (String oid : oids) {
+ sb.append(oid + BR);
+ }
+ sb.append(BR);
+
+ return sb.toString();
+ }
+
+ private String getHTMLStatistic() {
+ StringBuilder sb = new StringBuilder(2500);
+
+ sb.append(Util.getFontFamilyTag(MainWindow.FONT_MEDIUM));
+
+ sb.append(H1_S + "Statistic" + H1_E + BR);
+
+ if (scanTaskHandler == null) {
+ sb.append(H2_S + ScanTask.getStateTag(State.WAIT) + H2_E);
+ return sb.toString();
+ }
+
+ ScanTask.State state = scanTaskHandler.getState();
+ String colorTag = Util.getBGColorTag(ScanTask.getStateColor(state));
+ String stateTag = ScanTask.getStateTag(state);
+ sb.append(colorTag + H2_S + stateTag + H2_E + BR + Util.COLOR_END_TAG);
+ sb.append(BR);
+ sb.append(H2_S + "Hosts Completed (" + scanTaskHandler.getScansCompletedCount() + "/" + scanTaskHandler.getScanTasksCount() + ")" + H2_E + BR);
+ if (scanTaskHandler.getScansFailedCount() == 0) {
+ sb.append(H2_S + "Hosts Failed (" + scanTaskHandler.getScansFailedCount() + ")" + H2_E + BR);
+ }
+ else {
+ sb.append(H2_S + "Hosts Failed " + Util.getBGColorTag(ScanTask.getStateColor(State.ERROR))
+ + "(" + scanTaskHandler.getScansFailedCount() + ")" + Util.COLOR_END_TAG + H2_E + BR);
+ }
+ sb.append(BR);
+
+ if (scanTaskHandler.getProtocolsToTestCount() > 0) {
+ sb.append(H2_S + "Protocols:" + H2_E + BR);
+ StatisticUtil.appendProtocolStatistic(sb, scanTaskHandler);
+ sb.append(BR);
+ }
+
+ if (scanTaskHandler.getCipherSuitesToTestCount() > 0) {
+ sb.append(H2_S + "Cipher Suites:" + H2_E + BR);
+ StatisticUtil.appendCipherSuiteStatistic(sb, scanTaskHandler);
+ sb.append(BR);
+ }
+
+ if (scanTaskHandler.getCertCollectingEnabled() == false) {
+ return sb.toString();
+ }
+
+ sb.append(H2_S + "Cipher Suite Chosen By Server:" + H2_E + BR);
+ StatisticUtil.appendCipherSuiteChosenFromServerStatistic(sb, scanTaskHandler);
+ sb.append(BR);
+
+ sb.append(H2_S + "Certificate Public Key Algorithm:" + H2_E + BR);
+ StatisticUtil.appendCertificatePubKeyAlgorithmStatistic(sb, scanTaskHandler);
+ sb.append(BR);
+
+ sb.append(H2_S + "Certificate Public Key Algorithm (with key length):" + H2_E + BR);
+ StatisticUtil.appendCertificatePubKeyAlgorithmWithKeyLengthStatistic(sb, scanTaskHandler);
+ sb.append(BR);
+
+ sb.append(H2_S + "Certificate Signature Algorithm:" + H2_E + BR);
+ StatisticUtil.appendCertificateSignatureAlgorithmStatistic(sb, scanTaskHandler);
+ sb.append(BR);
+
+ sb.append(H2_S + "Certificate Extensions:" + H2_E + BR);
+ StatisticUtil.appendCertificateExtensionStatistic(sb, scanTaskHandler);
+ sb.append(BR);
+
+ sb.append(H2_S + "Certificate Root CAs (Organisation):" + H2_E + BR);
+ StatisticUtil.appendCertificateRootCAOrganisationStatistic(sb, scanTaskHandler);
+ sb.append(BR);
+
+ return sb.toString();
+ }
+
+ @Override
+ public synchronized void onScanTaskHandlerMessage(final ScanTask scanTask, String message) {
+ if (currentScanTask == scanTask || mainWindow.getAreaControls().getStatisticSelected()) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ updateView();
+ }
+ });
+ }
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/DialogConfirm.java b/src/org/bitbatzen/tlsserverscanner/gui/DialogConfirm.java
new file mode 100644
index 0000000..bef1282
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/DialogConfirm.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+
+import java.awt.BorderLayout;
+import java.awt.Font;
+import java.awt.event.*;
+
+
+public class DialogConfirm extends JDialog implements ActionListener {
+
+ private MainWindow mainWindow;
+
+ private JLabel infoLabel;
+
+ private JButton buttonOk;
+ private JButton buttonCancel;
+
+ private boolean returnValue = false;
+
+
+ public DialogConfirm(MainWindow mainWindow, String title, String text) {
+ super(mainWindow.getFrame(), title, true);
+
+ this.mainWindow = mainWindow;
+
+ setResizable(false);
+
+ infoLabel = new JLabel(text);
+ infoLabel.setFont(MainWindow.FONT_MEDIUM.deriveFont(Font.BOLD));
+ infoLabel.setBorder(BorderFactory.createEmptyBorder(20, 30, 15, 30));
+ infoLabel.setHorizontalAlignment(JLabel.CENTER);
+ getContentPane().add(infoLabel, BorderLayout.CENTER);
+
+ JPanel bottomPanel = new JPanel();
+ bottomPanel.setBorder(BorderFactory.createEmptyBorder(5, 20, 10, 20));
+
+ buttonOk = new JButton("Ok");
+ buttonOk.addActionListener(this);
+ bottomPanel.add(buttonOk);
+
+ buttonCancel = new JButton("Cancel");
+ buttonCancel.addActionListener(this);
+ bottomPanel.add(buttonCancel);
+
+ getContentPane().add(bottomPanel, BorderLayout.SOUTH);
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+ pack();
+
+ mainWindow.setDefaultPopupPosition(this);
+ }
+
+ public boolean showDialog() {
+ setVisible(true);
+ return returnValue;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == buttonOk) {
+ returnValue = true;
+ setVisible(false);
+ dispose();
+ }
+ else if (e.getSource() == buttonCancel) {
+ returnValue = false;
+ setVisible(false);
+ dispose();
+ }
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/DialogMessage.java b/src/org/bitbatzen/tlsserverscanner/gui/DialogMessage.java
new file mode 100644
index 0000000..0016628
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/DialogMessage.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+
+import java.awt.BorderLayout;
+import java.awt.Font;
+import java.awt.event.*;
+
+
+public class DialogMessage extends JDialog implements ActionListener {
+
+ private MainWindow mainWindow;
+
+ private JTextArea infoArea;
+
+ private JButton buttonOk;
+
+
+ public DialogMessage(MainWindow mainWindow, String title, String text) {
+ super(mainWindow.getFrame(), title, true);
+
+ this.mainWindow = mainWindow;
+
+ setResizable(false);
+
+ infoArea = new JTextArea(text);
+ infoArea.setEditable(false);
+ infoArea.setFont(MainWindow.FONT_MEDIUM.deriveFont(Font.BOLD));
+ infoArea.setBorder(BorderFactory.createEmptyBorder(20, 30, 15, 30));
+ getContentPane().add(infoArea, BorderLayout.CENTER);
+
+ JPanel bottomPanel = new JPanel();
+ bottomPanel.setBorder(BorderFactory.createEmptyBorder(5, 20, 10, 20));
+
+ buttonOk = new JButton("Ok");
+ buttonOk.addActionListener(this);
+ bottomPanel.add(buttonOk);
+
+ getContentPane().add(bottomPanel, BorderLayout.SOUTH);
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+ pack();
+
+ mainWindow.setDefaultPopupPosition(this);
+ }
+
+ public void showDialog() {
+ setVisible(true);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == buttonOk) {
+ setVisible(false);
+ dispose();
+ }
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/DialogNewHost.java b/src/org/bitbatzen/tlsserverscanner/gui/DialogNewHost.java
new file mode 100644
index 0000000..581128a
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/DialogNewHost.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.bitbatzen.tlsserverscanner.Util;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.event.*;
+
+
+public class DialogNewHost extends JDialog implements ActionListener {
+
+ private MainWindow mainWindow;
+
+ private JTextField textField;
+
+ private JLabel infoLabel;
+
+ private JButton buttonOk;
+ private JButton buttonCancel;
+
+
+ public DialogNewHost(MainWindow mainWindow) {
+ super(mainWindow.getFrame(), "New Host", true);
+
+ this.mainWindow = mainWindow;
+
+ JPanel topPanel = new JPanel(new BorderLayout());
+ topPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
+
+ textField = new JTextField(26);
+ textField.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ textField.setFont(MainWindow.FONT_MEDIUM);
+ topPanel.add(textField, BorderLayout.NORTH);
+
+ JLabel syntaxLabel = new JLabel("(syntax: host:port)");
+ syntaxLabel.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
+ syntaxLabel.setFont(MainWindow.FONT_SMALL);
+ syntaxLabel.setForeground(Color.GRAY);
+ topPanel.add(syntaxLabel, BorderLayout.SOUTH);
+ getContentPane().add(topPanel, BorderLayout.NORTH);
+
+ infoLabel = new JLabel(" ");
+ infoLabel.setFont(MainWindow.FONT_MEDIUM.deriveFont(Font.BOLD));
+ infoLabel.setForeground(Color.RED);
+ infoLabel.setHorizontalAlignment(JLabel.CENTER);
+ getContentPane().add(infoLabel, BorderLayout.CENTER);
+
+ JPanel bottomPanel = new JPanel();
+ bottomPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 10, 5));
+
+ buttonOk = new JButton("Ok");
+ buttonOk.addActionListener(this);
+ bottomPanel.add(buttonOk);
+
+ buttonCancel = new JButton("Cancel");
+ buttonCancel.addActionListener(this);
+ bottomPanel.add(buttonCancel);
+
+ getContentPane().add(bottomPanel, BorderLayout.SOUTH);
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+ pack();
+
+ mainWindow.setDefaultPopupPosition(this);
+
+ setVisible(true);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == buttonOk) {
+ String host = Util.extractHost(textField.getText());
+ int port = Util.extractPort(textField.getText());
+ if (host == null || port == -1) {
+ infoLabel.setText("Invalid Syntax!");
+ return;
+ }
+
+ mainWindow.getAreaHosts().addHost(host, port);
+ setVisible(false);
+ dispose();
+ }
+ else if (e.getSource() == buttonCancel) {
+ setVisible(false);
+ dispose();
+ }
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/DialogSearch.java b/src/org/bitbatzen/tlsserverscanner/gui/DialogSearch.java
new file mode 100644
index 0000000..195993a
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/DialogSearch.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JEditorPane;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.text.DefaultCaret;
+
+import org.bitbatzen.tlsserverscanner.Util;
+import org.bitbatzen.tlsserverscanner.scantask.Cert;
+import org.bitbatzen.tlsserverscanner.scantask.ScanData;
+import org.bitbatzen.tlsserverscanner.scantask.ScanTaskHandler;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+public class DialogSearch extends JDialog implements ActionListener {
+
+ private class SearchResult {
+ public HostListItem hostListItem;
+ public List resultTypes = new ArrayList<>();
+ }
+
+ private MainWindow mainWindow;
+
+ private JEditorPane taResults;
+
+ private JTextField tfSearch;
+
+ private JCheckBox cbHostname;
+ private JCheckBox cbCipherSuiteChosenByServer;
+ private JCheckBox cbCertificate;
+ private JCheckBox cbFullCertificateChain;
+ private JCheckBox cbSupportedCipherSuites;
+ private JCheckBox cbSupportedProtocols;
+
+ private JButton buttonSearch;
+
+ private JLabel labelSearchInfo;
+
+ private List results;
+
+
+ public DialogSearch(MainWindow mainWindow) {
+ super(mainWindow.getFrame(), "Full-Text Search", false);
+ this.mainWindow = mainWindow;
+
+ results = new ArrayList<>();
+
+ final Dimension winSize = new Dimension(650, 550);
+ final Dimension resultsSize = new Dimension(1000, 250);
+
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+ setResizable(false);
+ setSize(winSize);
+
+ // results area
+ taResults = new JEditorPane();
+ taResults.setFont(MainWindow.FONT_MEDIUM);
+ taResults.setEditable(false);
+ taResults.setContentType("text/html");
+ DefaultCaret caret = (DefaultCaret) taResults.getCaret();
+ caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
+
+ JScrollPane scrollPaneResults = new JScrollPane(taResults);
+ scrollPaneResults.setPreferredSize(resultsSize);
+
+ JPanel panelSearchOptions = new JPanel();
+ BoxLayout layout = new BoxLayout(panelSearchOptions, BoxLayout.Y_AXIS);
+ panelSearchOptions.setLayout(layout);
+ panelSearchOptions.setBorder(BorderFactory.createEmptyBorder(15, 25, 25, 25));
+
+ tfSearch = new JTextField();
+ tfSearch.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
+ tfSearch.setFont(MainWindow.FONT_MEDIUM);
+ panelSearchOptions.add(tfSearch);
+
+ panelSearchOptions.add(Box.createRigidArea(new Dimension(0, 5)));
+
+ cbHostname = new JCheckBox("Hostname");
+ cbHostname.setSelected(true);
+ panelSearchOptions.add(cbHostname);
+
+ cbCipherSuiteChosenByServer = new JCheckBox("Cipher Suite Chosen By Server");
+ cbCipherSuiteChosenByServer.setSelected(true);
+ panelSearchOptions.add(cbCipherSuiteChosenByServer);
+
+ cbCertificate = new JCheckBox("Certificate");
+ cbCertificate.setSelected(true);
+ panelSearchOptions.add(cbCertificate);
+
+ cbFullCertificateChain = new JCheckBox("Full Certificate Chain");
+ cbFullCertificateChain.setSelected(true);
+ panelSearchOptions.add(cbFullCertificateChain);
+
+ cbSupportedCipherSuites = new JCheckBox("Supported Cipher Suites");
+ cbSupportedCipherSuites.setSelected(true);
+ panelSearchOptions.add(cbSupportedCipherSuites);
+
+ cbSupportedProtocols = new JCheckBox("Supported Protocols");
+ cbSupportedProtocols.setSelected(true);
+ panelSearchOptions.add(cbSupportedProtocols);
+
+ panelSearchOptions.add(Box.createRigidArea(new Dimension(0, 10)));
+
+ buttonSearch = new JButton("Search");
+ buttonSearch.addActionListener(this);
+ panelSearchOptions.add(buttonSearch);
+
+ panelSearchOptions.add(Box.createRigidArea(new Dimension(0, 10)));
+
+ labelSearchInfo = new JLabel(" ");
+ panelSearchOptions.add(labelSearchInfo);
+
+ getContentPane().setLayout(new BorderLayout());
+ getContentPane().add(panelSearchOptions, BorderLayout.NORTH);
+ getContentPane().add(scrollPaneResults, BorderLayout.SOUTH);
+
+ Dimension parentSize = mainWindow.getFrame().getSize();
+ Point p = mainWindow.getFrame().getLocation();
+ setLocation(p.x + parentSize.width / 2 - getWidth() / 2, p.y + 50);
+ setVisible(true);
+ }
+
+ private void search() {
+ String search = tfSearch.getText();
+ if (search.equals("")) {
+ mainWindow.showMessageDialog("Error", "No text in search box!");
+ return;
+ }
+
+ search = search.toLowerCase();
+ results.clear();
+
+ // hostnames
+ if (cbHostname.isSelected()) {
+ HostListItem[] hostListItems = mainWindow.getAreaHosts().getHostListItems();
+ for (HostListItem host : hostListItems) {
+ if (host.getHostWithPort().toLowerCase().contains(search)) {
+ addResult(host, "Hostname");
+ }
+ }
+ }
+
+ ScanTaskHandler scanTaskHandler = mainWindow.getAreaControls().getScanTaskHandler();
+ if (scanTaskHandler == null) {
+ setResultsText();
+ return;
+ }
+
+ HostListItem[] hostListItems = mainWindow.getAreaHosts().getHostListItems();
+
+ for (HostListItem hostListItem : hostListItems) {
+ if (hostListItem.getScanTask() == null) {
+ continue;
+ }
+
+ ScanData sd = hostListItem.getScanTask().getScanData();
+
+ // server-side chosen cipher suites
+ if (cbCipherSuiteChosenByServer.isSelected()) {
+ if (sd.cipherSuiteChosenByServer.toLowerCase().contains(search)) {
+ addResult(hostListItem, "CipherSuiteChosenByServer");
+ }
+ }
+
+ // certificates
+ if (cbCertificate.isSelected()) {
+ if (sd.getCertAvailable()) {
+ findInCertificate(hostListItem, search, sd.certs[0], "Certificate");
+ }
+ }
+
+ // full certificate chain
+ if (cbFullCertificateChain.isSelected()) {
+ if (sd.getCertAvailable() && sd.certs.length > 1) {
+ for (int i = 1; i < sd.certs.length; i++) {
+ if (findInCertificate(hostListItem, search, sd.certs[i], "FullCertificateChain")) {
+ break;
+ }
+ }
+ }
+ }
+
+ // supported cipher suites
+ if (cbSupportedCipherSuites.isSelected()) {
+ HashMap cipherSuitesTested = sd.cipherSuitesTested;
+ for (Map.Entry entry : cipherSuitesTested.entrySet()) {
+ if (entry.getValue()) {
+ // (supported)
+ String cipherSuite = scanTaskHandler.getCipherSuiteToTest(entry.getKey());
+ if (cipherSuite.toLowerCase().contains(search)) {
+ addResult(hostListItem, "SupportedCipherSuites");
+ break;
+ }
+ }
+ }
+ }
+
+ // supported protocols
+ if (cbSupportedProtocols.isSelected()) {
+ HashMap protocolsTested = sd.protocolsTested;
+ for (Map.Entry entry : protocolsTested.entrySet()) {
+ if (entry.getValue()) {
+ // (supported)
+ String protocol = scanTaskHandler.getProtocolToTest(entry.getKey());
+ if (protocol.toLowerCase().contains(search)) {
+ addResult(hostListItem, "SupportedProtocols");
+ break;
+ }
+ }
+ }
+ }
+ }
+
+
+ setResultsText();
+ }
+
+ private boolean findInCertificate(HostListItem hostListItem, String searchString, Cert cert, String resultType) {
+ if (cert == null) {
+ return false;
+ }
+
+ if (cert.getCert().toString().toLowerCase().contains(searchString)
+ || cert.getSubjectName().toLowerCase().contains(searchString)
+ || cert.getIssuerName().toLowerCase().contains(searchString)
+ || cert.getPublicKeyAlgorithmWithKeyLength().toLowerCase().contains(searchString)
+ || cert.getSignatureAlgorithm().toLowerCase().contains(searchString)) {
+
+ addResult(hostListItem, resultType);
+ return true;
+ }
+ else {
+ List oids = cert.getExtensionOIDsWithName();
+ for (String oid : oids) {
+ if (oid.toLowerCase().contains(searchString)) {
+ addResult(hostListItem, resultType);
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private void addResult(HostListItem hostListItem, String resultType) {
+ for (SearchResult sr : results) {
+ if (sr.hostListItem == hostListItem) {
+ sr.resultTypes.add(resultType);
+ return;
+ }
+ }
+
+ // create new entry
+ SearchResult sr = new SearchResult();
+ sr.hostListItem = hostListItem;
+ sr.resultTypes.add(resultType);
+ results.add(sr);
+ }
+
+ private void setResultsText() {
+ if (results.size() == 0) {
+ taResults.setText("");
+ }
+ else {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Util.getFontFamilyTag(MainWindow.FONT_MEDIUM));
+
+ for (SearchResult sr : results) {
+ sb.append("" + sr.hostListItem.getHostWithPortAndIndex() + "");
+ List resultTypes = sr.resultTypes;
+ sb.append(" (");
+
+ for (int i = 0; i < resultTypes.size(); i++) {
+ String rt = resultTypes.get(i);
+ if (i < resultTypes.size() -1) {
+ sb.append(rt + ", ");
+ }
+ else {
+ sb.append(rt);
+ }
+ }
+
+ sb.append(")");
+ sb.append(Util.BR);
+ }
+
+ taResults.setText(sb.toString());
+ }
+
+ if (results.size() == 1) {
+ labelSearchInfo.setText(results.size() + " hit");
+ }
+ else {
+ labelSearchInfo.setText(results.size() + " hits");
+ }
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ if (arg0.getSource() == buttonSearch) {
+ search();
+ }
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/DialogSelectCipherSuites.java b/src/org/bitbatzen/tlsserverscanner/gui/DialogSelectCipherSuites.java
new file mode 100644
index 0000000..89e49f6
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/DialogSelectCipherSuites.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+import org.bitbatzen.tlsserverscanner.Util;
+import org.bitbatzen.tlsserverscanner.scantask.SSLUtil;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.event.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+public class DialogSelectCipherSuites extends JDialog implements ActionListener, WindowListener {
+
+ public enum DialogType {
+ CIPHER_SUITES_TO_TEST,
+ CIPHER_SUITES_FOR_COLLECTING_CERT
+ }
+
+ private MainWindow mainWindow;
+
+ private JButton buttonOk;
+
+ private JLabel labelCiphersInfo;
+
+ private JCheckBox cbToggleAll;
+ private JCheckBox cbSelectFiltered;
+
+ private JTextField tfFilter;
+ private JLabel labelFilterInfo;
+
+ private List cipherSuites;
+
+ private boolean selectionChanged;
+
+ private DialogType dialogType;
+
+ private static DialogSelectCipherSuitesData[] data = null;
+
+
+ public DialogSelectCipherSuites(MainWindow mainWindow, DialogType dialogType, String title) {
+ super(mainWindow.getFrame(), title, true);
+
+ String header = "";
+ if (dialogType == DialogType.CIPHER_SUITES_TO_TEST) {
+ header = "Select cipher suites to test!";
+ }
+ else if (dialogType == DialogType.CIPHER_SUITES_FOR_COLLECTING_CERT) {
+ header = "Select supported cipher suites for"
+ + Util.BR + "fetching the certificates!";
+ }
+
+ this.mainWindow = mainWindow;
+ this.dialogType = dialogType;
+ selectionChanged = false;
+
+ setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+ addWindowListener(this);
+ setSize(550, 630);
+
+ JPanel panel = new JPanel();
+ BoxLayout layout = new BoxLayout(panel, BoxLayout.Y_AXIS);
+ panel.setLayout(layout);
+ panel.setBorder(BorderFactory.createEmptyBorder(15, 25, 25, 25));
+
+ // header label
+ JLabel labelHeader = new JLabel("" + header + "");
+ labelHeader.setFont(MainWindow.FONT_HUGE);
+ labelHeader.setForeground(MainWindow.COLOR_DIALOG_HEADER);
+ labelHeader.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
+ panel.add(labelHeader);
+
+ // info label
+ labelCiphersInfo = new JLabel("Available cipher suites are depending on the installed"
+ + "
version of the java runtime environment! (current: " + Util.getJavaVersionString() + ")");
+ labelCiphersInfo.setFont(MainWindow.FONT_SMALL);
+ labelCiphersInfo.setForeground(MainWindow.COLOR_HINT);
+ labelCiphersInfo.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
+ panel.add(labelCiphersInfo);
+
+ // checkbox toggle all
+ cbToggleAll = new JCheckBox("Toggle All");
+ cbToggleAll.setSelected(getData().toggleAllSelected);
+ cbToggleAll.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ selectionChanged = true;
+ for (JCheckBox cb : cipherSuites) {
+ cb.setSelected(cbToggleAll.isSelected());
+ }
+ }
+ });
+ panel.add(cbToggleAll);
+
+ panel.add(Box.createRigidArea(new Dimension(0, 5)));
+
+ // checkbox select filtered
+ cbSelectFiltered = new JCheckBox("Select Filtered");
+ cbSelectFiltered.setSelected(getData().selectFilteredSelected);
+ cbSelectFiltered.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent arg0) {
+ selectionChanged = true;
+ onFilterChanged(cbSelectFiltered.isSelected());
+ }
+ });
+ panel.add(cbSelectFiltered);
+
+ panel.add(Box.createRigidArea(new Dimension(0, 5)));
+
+ // filter
+ tfFilter = new JTextField(getData().filterSting);
+ tfFilter.setFont(MainWindow.FONT_MEDIUM);
+ tfFilter.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
+ tfFilter.getDocument().addDocumentListener(new DocumentListener() {
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ onFilterChanged(cbSelectFiltered.isSelected());
+ }
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ onFilterChanged(cbSelectFiltered.isSelected());
+ }
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ onFilterChanged(cbSelectFiltered.isSelected());
+ }
+ });
+ panel.add(tfFilter);
+
+ labelFilterInfo = new JLabel("(filter syntax: whitespace seperated values (aes ecdhe))");
+ labelFilterInfo.setFont(MainWindow.FONT_SMALL);
+ labelFilterInfo.setForeground(Color.GRAY);
+ labelFilterInfo.setBorder(BorderFactory.createEmptyBorder(5, 0, 20, 0));
+ panel.add(labelFilterInfo);
+
+ // available cipher suites
+ JScrollPane scrollPane = new JScrollPane(panel);
+ getContentPane().add(scrollPane, BorderLayout.CENTER);
+ scrollPane.setPreferredSize(new Dimension(450, 600));
+
+ List availableCipherSuites = SSLUtil.getAvailableCipherSuites();
+ Collections.sort(availableCipherSuites);
+
+ cipherSuites = new ArrayList();
+ for (String s : availableCipherSuites) {
+ JCheckBox cb = new JCheckBox(s, getData().selectedCipherSuites.contains(s));
+ cb.addActionListener(this);
+ cipherSuites.add(cb);
+ panel.add(cb);
+ }
+
+ panel.add(Box.createRigidArea(new Dimension(0, 20)));
+
+ // button ok
+ buttonOk = new JButton("Ok");
+ buttonOk.addActionListener(this);
+ panel.add(buttonOk);
+
+ onFilterChanged(false);
+
+ Dimension parentSize = mainWindow.getFrame().getSize();
+ Point p = mainWindow.getFrame().getLocation();
+ setLocation(p.x + parentSize.width / 2 - getWidth() / 2, p.y + 50);
+ setVisible(true);
+ }
+
+ private static DialogSelectCipherSuitesData getData(DialogType dialogType) {
+ if (data == null) {
+ data = new DialogSelectCipherSuitesData[2];
+ data[0] = new DialogSelectCipherSuitesData();
+ data[1] = new DialogSelectCipherSuitesData();
+ data[1].selectedCipherSuites = SSLUtil.getAvailableCipherSuites();
+ }
+
+ if (dialogType == DialogType.CIPHER_SUITES_TO_TEST) {
+ return data[0];
+ }
+ else {
+ return data[1];
+ }
+ }
+
+ private DialogSelectCipherSuitesData getData() {
+ return getData(dialogType);
+ }
+
+ public static List getSelectedCipherSuites(DialogType dialogType) {
+ return getData(dialogType).selectedCipherSuites;
+ }
+
+ private void setElementsEnabled(boolean enabled) {
+ cbSelectFiltered.setEnabled(enabled);
+ cbToggleAll.setEnabled(enabled);
+ tfFilter.setEnabled(enabled);
+ labelFilterInfo.setEnabled(enabled);
+ labelCiphersInfo.setEnabled(enabled);
+ for (JCheckBox cb : cipherSuites) {
+ cb.setEnabled(enabled);
+ }
+ }
+
+ private void onFilterChanged(boolean selectFiltered) {
+ if (selectFiltered) {
+ selectionChanged = true;
+ }
+
+ String[] filter = tfFilter.getText().split(" ");
+ for (JCheckBox cb : cipherSuites) {
+ boolean filtered = true;
+ for (String s : filter) {
+ if (cb.getText().contains(s.toUpperCase()) == false) {
+ filtered = false;
+ break;
+ }
+ }
+
+ if (filtered) {
+ cb.setForeground(Color.BLACK);
+ }
+ else {
+ cb.setForeground(Color.LIGHT_GRAY);
+ }
+
+ if (selectFiltered) {
+ cb.setSelected(filtered);
+ }
+ }
+ }
+
+ private boolean checkSelectedCipherSuitesCount() {
+ if (dialogType == DialogType.CIPHER_SUITES_FOR_COLLECTING_CERT) {
+ int count = 0;
+ for (JCheckBox cb : cipherSuites) {
+ if (cb.isSelected()) {
+ count++;
+ }
+ }
+ if (count == 0) {
+ mainWindow.showMessageDialog("Cipher Suites", "At least one cipher suite has to be selected!");
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+ else {
+ return true;
+ }
+ }
+
+ private void save() {
+ DialogSelectCipherSuitesData data = getData();
+
+ data.selectFilteredSelected = cbSelectFiltered.isSelected();
+ data.toggleAllSelected = cbToggleAll.isSelected();
+ data.filterSting = tfFilter.getText();
+
+ data.selectedCipherSuites.clear();
+ for (JCheckBox cb : cipherSuites) {
+ if (cb.isSelected()) {
+ data.selectedCipherSuites.add(cb.getText());
+ }
+ }
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == buttonOk) {
+ if (checkSelectedCipherSuitesCount()) {
+ save();
+ setVisible(false);
+ dispose();
+ }
+ }
+ else if (e.getSource() instanceof JCheckBox) {
+ selectionChanged = true;
+ }
+ }
+
+ @Override
+ public void windowActivated(WindowEvent arg0) {
+ }
+
+ @Override
+ public void windowClosed(WindowEvent arg0) {
+ }
+
+ @Override
+ public void windowClosing(WindowEvent arg0) {
+ if (selectionChanged) {
+ if (mainWindow.showConfirmDialog("Close", "Save changes?")) {
+ if (checkSelectedCipherSuitesCount()) {
+ save();
+ }
+ else {
+ return;
+ }
+ }
+ }
+
+ setVisible(false);
+ dispose();
+ }
+
+ @Override
+ public void windowDeactivated(WindowEvent arg0) {
+ }
+
+ @Override
+ public void windowDeiconified(WindowEvent arg0) {
+ }
+
+ @Override
+ public void windowIconified(WindowEvent arg0) {
+ }
+
+ @Override
+ public void windowOpened(WindowEvent arg0) {
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/DialogSelectCipherSuitesData.java b/src/org/bitbatzen/tlsserverscanner/gui/DialogSelectCipherSuitesData.java
new file mode 100644
index 0000000..997aa47
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/DialogSelectCipherSuitesData.java
@@ -0,0 +1,13 @@
+package org.bitbatzen.tlsserverscanner.gui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class DialogSelectCipherSuitesData {
+
+ public boolean toggleAllSelected = false;
+ public boolean selectFilteredSelected = false;
+ public String filterSting = "";
+ public List selectedCipherSuites = new ArrayList<>();
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/DialogSelectProtocols.java b/src/org/bitbatzen/tlsserverscanner/gui/DialogSelectProtocols.java
new file mode 100644
index 0000000..248bee3
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/DialogSelectProtocols.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import org.bitbatzen.tlsserverscanner.Util;
+import org.bitbatzen.tlsserverscanner.scantask.SSLUtil;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.event.*;
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class DialogSelectProtocols extends JDialog implements ActionListener, WindowListener {
+
+ private MainWindow mainWindow;
+
+ private JButton buttonOk;
+
+ private JLabel labelProtocolsInfo;
+
+ private List protocols;
+
+ private boolean selectionChanged;
+
+ private static List selectedProtocols = new ArrayList<>();
+
+
+ public DialogSelectProtocols(MainWindow mainWindow) {
+ super(mainWindow.getFrame(), "Protocols", true);
+
+ this.mainWindow = mainWindow;
+ selectionChanged = false;
+
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+ addWindowListener(this);
+ setSize(500, 350);
+
+ JPanel panel = new JPanel();
+ BoxLayout layout = new BoxLayout(panel, BoxLayout.Y_AXIS);
+ panel.setLayout(layout);
+ panel.setBorder(BorderFactory.createEmptyBorder(15, 25, 25, 25));
+
+ // header label
+ JLabel labelHeader = new JLabel("Select protocols to test!");
+ labelHeader.setFont(MainWindow.FONT_HUGE);
+ labelHeader.setForeground(MainWindow.COLOR_DIALOG_HEADER);
+ labelHeader.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
+ panel.add(labelHeader);
+
+ // info label
+ labelProtocolsInfo = new JLabel("Available protocols are depending on the installed"
+ + "
version of the java runtime environment! (current: " + Util.getJavaVersionString() + ")");
+ labelProtocolsInfo.setFont(MainWindow.FONT_SMALL);
+ labelProtocolsInfo.setForeground(MainWindow.COLOR_HINT);
+ labelProtocolsInfo.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
+ panel.add(labelProtocolsInfo);
+
+ // available protocols
+ JScrollPane scrollPane = new JScrollPane(panel);
+ getContentPane().add(scrollPane, BorderLayout.CENTER);
+ scrollPane.setPreferredSize(new Dimension(450, 600));
+
+ List availableProtocols = SSLUtil.getAvailableProtocols();
+// Collections.sort(availableProtocols);
+
+ protocols = new ArrayList();
+ for (String s : availableProtocols) {
+ JCheckBox cb = new JCheckBox(s, selectedProtocols.contains(s));
+ cb.addActionListener(this);
+ protocols.add(cb);
+ panel.add(cb);
+ }
+
+ panel.add(Box.createRigidArea(new Dimension(0, 20)));
+
+ // button ok
+ buttonOk = new JButton("Ok");
+ buttonOk.addActionListener(this);
+ panel.add(buttonOk);
+
+ Dimension parentSize = mainWindow.getFrame().getSize();
+ Point p = mainWindow.getFrame().getLocation();
+ setLocation(p.x + parentSize.width / 2 - getWidth() / 2, p.y + 50);
+ setVisible(true);
+ }
+
+ public static final List getSelectedProtocols() {
+ return selectedProtocols;
+ }
+
+ private void save() {
+ selectedProtocols.clear();
+ for (JCheckBox cb : protocols) {
+ if (cb.isSelected()) {
+ selectedProtocols.add(cb.getText());
+ }
+ }
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == buttonOk) {
+ save();
+ setVisible(false);
+ dispose();
+ }
+ else if (e.getSource() instanceof JCheckBox) {
+ selectionChanged = true;
+ }
+ }
+
+ @Override
+ public void windowActivated(WindowEvent arg0) {
+ }
+
+ @Override
+ public void windowClosed(WindowEvent arg0) {
+ }
+
+ @Override
+ public void windowClosing(WindowEvent arg0) {
+ if (selectionChanged) {
+ if (mainWindow.showConfirmDialog("Close", "Save changes?")) {
+ save();
+ }
+ }
+ }
+
+ @Override
+ public void windowDeactivated(WindowEvent arg0) {
+ }
+
+ @Override
+ public void windowDeiconified(WindowEvent arg0) {
+ }
+
+ @Override
+ public void windowIconified(WindowEvent arg0) {
+ }
+
+ @Override
+ public void windowOpened(WindowEvent arg0) {
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/DialogViewCertificates.java b/src/org/bitbatzen/tlsserverscanner/gui/DialogViewCertificates.java
new file mode 100644
index 0000000..d92ab3f
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/DialogViewCertificates.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import javax.swing.DefaultListModel;
+import javax.swing.JDialog;
+import javax.swing.JEditorPane;
+import javax.swing.JList;
+import javax.swing.JScrollPane;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.text.DefaultCaret;
+
+import org.bitbatzen.tlsserverscanner.Util;
+import org.bitbatzen.tlsserverscanner.scantask.Cert;
+import org.bitbatzen.tlsserverscanner.scantask.ScanData;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.text.SimpleDateFormat;
+import java.util.List;
+
+
+public class DialogViewCertificates extends JDialog {
+
+ private static final String BR = Util.BR;
+ private static final String H2_S = "";
+ private static final String H2_E = "";
+
+ private MainWindow mainWindow;
+
+ private SimpleDateFormat dateFormat;
+
+ private JList certificatesList;
+ private JEditorPane textArea;
+
+ private HostListItem host;
+
+
+ public DialogViewCertificates(MainWindow mainWindow, String title, HostListItem host) {
+ super(mainWindow.getFrame(), title, false);
+ this.mainWindow = mainWindow;
+ this.host = host;
+
+ dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+
+ final Dimension winSize = new Dimension(800, 600);
+ final Dimension resultsSize = new Dimension(550, 600);
+ final Dimension certListSize = new Dimension(240, 600);
+
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+ setResizable(false);
+ setSize(winSize);
+
+ // results area
+ textArea = new JEditorPane();
+ textArea.setFont(MainWindow.FONT_MEDIUM);
+ textArea.setEditable(false);
+ DefaultCaret caret = (DefaultCaret) textArea.getCaret();
+ caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
+
+ JScrollPane scrollPaneResults = new JScrollPane(textArea);
+ scrollPaneResults.setPreferredSize(resultsSize);
+
+ // certificates list
+ DefaultListModel listModel = new DefaultListModel();
+ certificatesList = new JList(listModel);
+ certificatesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ certificatesList.setLayoutOrientation(JList.VERTICAL_WRAP);
+ certificatesList.setVisibleRowCount(-1);
+ certificatesList.setBackground(new Color(220, 220, 220));
+ JScrollPane scrollPaneCertList = new JScrollPane(certificatesList);
+ scrollPaneCertList.setPreferredSize(certListSize);
+
+ certificatesList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ showCertificate(certificatesList.getSelectedIndex());
+ }
+ });
+
+ String[] certNames = host.getScanTask().getScanData().getCertNames();
+ for (String name : certNames) {
+ listModel.addElement(name);
+ }
+
+ certificatesList.setSelectedIndex(0);
+
+ getContentPane().setLayout(new BorderLayout());
+ getContentPane().add(scrollPaneCertList, BorderLayout.WEST);
+ getContentPane().add(scrollPaneResults, BorderLayout.EAST);
+
+ Dimension parentSize = mainWindow.getFrame().getSize();
+ Point p = mainWindow.getFrame().getLocation();
+ setLocation(p.x + parentSize.width / 2 - getWidth() / 2, p.y + 50);
+ setVisible(true);
+ }
+
+ private void showCertificate(int index) {
+ String result = getHTMLCertificateData(host, host.getScanTask().getScanData().certs[index]);
+
+ boolean plainTextEnabled = false;
+ String contentType = plainTextEnabled ? "text/txt" : "text/html";
+ textArea.setContentType(contentType);
+ if (plainTextEnabled) {
+ result = result.replace("
", Util.LF);
+ result = result.replaceAll("\\<.*?>", "");
+ }
+
+ textArea.setText(result);
+ }
+
+ private String getHTMLCertificateData(HostListItem host, Cert cert) {
+ ScanData sd = host.getScanTask().getScanData();
+ StringBuilder sb = new StringBuilder(2500);
+
+ sb.append(Util.getFontFamilyTag(MainWindow.FONT_MEDIUM));
+
+ sb.append(H2_S + "Certificate Format: " + H2_E + BR);
+ sb.append("Type: " + cert.getType() + BR);
+ sb.append("Version: " + cert.getVersion() + BR);
+ sb.append(BR);
+
+ String certVerification = "";
+ if (host.getScanTask().getScanTaskHandler().getCertAndHostnameVerificationEnabled() == false) {
+ certVerification = H2_S + Util.getBGColorTag(Color.RED) + "(certificate verification is disabled!)" + Util.COLOR_END_TAG + H2_E;
+ }
+
+ boolean isSelfSigned = sd.getIsCertSelfSigned();
+ String selfSignedString = isSelfSigned ? "(self-signed) " : "";
+ sb.append(H2_S + "Subject: " + selfSignedString + H2_E + certVerification + BR);
+ sb.append(Cert.formatCertName2(cert.getSubjectName()));
+ sb.append(BR);
+
+ sb.append(H2_S + "Issuer: " + selfSignedString + H2_E + BR);
+ sb.append(Cert.formatCertName2(cert.getIssuerName()));
+ sb.append(BR);
+
+ sb.append(H2_S + "Not Before: " + H2_E + BR);
+ sb.append(dateFormat.format(cert.getNotBefore()) + BR);
+ sb.append(BR);
+
+ sb.append(H2_S + "Not After: " + H2_E + BR);
+ sb.append(dateFormat.format(cert.getNotAfter()) + BR);
+ sb.append(BR);
+
+ String[] alternativeNames = cert.getSubjectAlternativeNames();
+ if (alternativeNames != null && alternativeNames.length > 0) {
+ sb.append(H2_S + "Subject Alternative Names:" + H2_E + BR);
+ for (String s : alternativeNames) {
+ sb.append(s + BR);
+ }
+ sb.append(BR);
+ }
+
+ sb.append(H2_S + "Extensions: " + H2_E + BR);
+ List oids = cert.getExtensionOIDsWithName();
+ for (String oid : oids) {
+ sb.append(oid + BR);
+ }
+ sb.append(BR);
+
+ sb.append(H2_S + "Public Key Algorithm: " + H2_E + BR);
+ sb.append(cert.getPublicKeyAlgorithmWithKeyLength() + BR);
+ sb.append(BR);
+
+ sb.append(H2_S + "Signature Algorithm: " + H2_E + BR);
+ sb.append(cert.getSignatureAlgorithm() + BR);
+ sb.append(BR);
+
+ return sb.toString();
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/HostListCellRenderer.java b/src/org/bitbatzen/tlsserverscanner/gui/HostListCellRenderer.java
new file mode 100644
index 0000000..a787a48
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/HostListCellRenderer.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+
+
+public class HostListCellRenderer extends JLabel implements ListCellRenderer {
+
+
+ public HostListCellRenderer() {
+ setOpaque(true);
+ setFont(MainWindow.FONT_BIG.deriveFont(Font.BOLD));
+ }
+
+ public Component getListCellRendererComponent(JList list, Object value,
+ int index, boolean isSelected, boolean cellHasFocus) {
+
+ HostListItem item = (HostListItem) value;
+ setText(item.getText());
+
+ if (isSelected) {
+ setBackground(Color.DARK_GRAY);
+ setForeground(Color.WHITE);
+ }
+ else {
+ setBackground(item.getBGColor());
+ setForeground(Color.DARK_GRAY);
+ }
+
+ return this;
+ }
+}
+
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/HostListItem.java b/src/org/bitbatzen/tlsserverscanner/gui/HostListItem.java
new file mode 100644
index 0000000..ea272a4
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/HostListItem.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import java.awt.Color;
+
+import javax.swing.SwingUtilities;
+
+import org.bitbatzen.tlsserverscanner.scantask.IScanTaskListener;
+import org.bitbatzen.tlsserverscanner.scantask.ScanTask;
+
+
+public class HostListItem implements IScanTaskListener {
+
+ private String host;
+ private int port;
+
+ private int index;
+
+ private String text;
+
+ private Color bgColor;
+
+ private ScanTask scanTask;
+ private HostListModel listModel;
+
+
+ public HostListItem(String host, int port, HostListModel listModel) {
+ this.host = host;
+ this.port = port;
+ this.listModel = listModel;
+
+ scanTask = null;
+ bgColor = MainWindow.COLOR_BG_HOSTS;
+
+ setText(ScanTask.getStateTag(ScanTask.State.NONE));
+
+ index = listModel.getIndex(this);
+ listModel.update(index);
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getHostWithPort() {
+ return host + ":" + port;
+ }
+
+ public String getHostWithPortAndIndex() {
+ return getIndexString() + " " + getHostWithPort();
+ }
+
+ public void setScanTask(ScanTask scanTask) {
+ this.scanTask = scanTask;
+ scanTask.addListener(this);
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public ScanTask getScanTask() {
+ return scanTask;
+ }
+
+ public Color getBGColor() {
+ return bgColor;
+ }
+
+ public String getIndexString() {
+ int hostCount = Integer.toString(listModel.size()).length();
+ int indexCount = Integer.toString(index + 1).length();
+ String spacer = "";
+ for (int i = 0; i < hostCount - indexCount; i++) {
+ spacer += "0";
+ }
+
+ return "[" + spacer + (index + 1) + "]";
+ }
+
+ public void update() {
+ ScanTask.State state = ScanTask.getState(scanTask);
+ bgColor = ScanTask.getStateColor(state);
+ index = listModel.getIndex(this);
+ setText(ScanTask.getStateTag(state));
+
+ listModel.update(index);
+ }
+
+ private void setText(String state) {
+ text = getIndexString() + state + " " + getHostWithPort();
+ }
+
+ @Override
+ public synchronized void onScanTaskStateChanged(ScanTask scanTask, String message) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ update();
+ }
+ });
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/HostListModel.java b/src/org/bitbatzen/tlsserverscanner/gui/HostListModel.java
new file mode 100644
index 0000000..f52e2ff
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/HostListModel.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import javax.swing.DefaultListModel;
+
+
+public class HostListModel extends DefaultListModel {
+
+ public void update(int index) {
+ fireContentsChanged(this, index, index);
+ }
+
+ public int getIndex(HostListItem element) {
+ for (int i = 0; i < getSize(); i++) {
+ if (get(i) == element) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/MainWindow.java b/src/org/bitbatzen/tlsserverscanner/gui/MainWindow.java
new file mode 100644
index 0000000..94e9668
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/MainWindow.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Point;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.util.Locale;
+
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.UIManager;
+
+import org.bitbatzen.tlsserverscanner.Util;
+
+
+public class MainWindow implements ComponentListener {
+
+ public static final Font FONT_LOG = new Font("Monospaced", Font.PLAIN, 13);
+ public static final Font FONT_SMALL = new Font("Monospaced", Font.PLAIN, 12);
+ public static final Font FONT_MEDIUM = new Font("Monospaced", Font.PLAIN, 14);
+ public static final Font FONT_BIG = new Font("Monospaced", Font.PLAIN, 16);
+ public static final Font FONT_HUGE = new Font("Monospaced", Font.PLAIN, 18);
+
+ public static final Color COLOR_HINT = Color.BLUE;
+ public static final Color COLOR_DIALOG_HEADER = Color.DARK_GRAY;
+
+ public static final Color COLOR_BG_RESULTS = new Color(255, 255, 255);
+ public static final Color COLOR_BG_HOSTS = new Color(255, 255, 255);
+ public static final Color COLOR_BG_LOG = new Color(220, 220, 220);
+ public static final Color COLOR_BG_CONTROLS = new Color(220, 220, 220);
+ public static final Color COLOR_BG_MENUBAR = new Color(220, 220, 220);
+
+ public static final int HOST_AREA_WIDTH = 350;
+
+ private AreaLog areaLog;
+ private AreaControls areaControls;
+ private AreaHosts areaHosts;
+ private AreaResults areaResults;
+ private MyMenuBar myMenuBar;
+
+ private JPanel containerPanel;
+
+ private JFrame frame;
+
+
+ public MainWindow() {
+ areaControls = new AreaControls(this);
+ areaLog = new AreaLog(this);
+ areaHosts = new AreaHosts(this);
+ areaResults = new AreaResults(this);
+
+ initWindow();
+
+ showAppInfoDialog(true);
+ }
+
+ public AreaLog getAreaLog() {
+ return areaLog;
+ }
+
+ public AreaControls getAreaControls() {
+ return areaControls;
+ }
+
+ public AreaHosts getAreaHosts() {
+ return areaHosts;
+ }
+
+ public AreaResults getAreaResults() {
+ return areaResults;
+ }
+
+ public MyMenuBar getMyMenuBar() {
+ return myMenuBar;
+ }
+
+ public JFrame getFrame() {
+ return frame;
+ }
+
+ private void initWindow() {
+ Locale.setDefault(java.util.Locale.ENGLISH);
+ JComponent.setDefaultLocale(Locale.ENGLISH);
+ UIManager.put("OptionPane.messageFont", FONT_MEDIUM);
+
+ frame = new JFrame(Util.T_APP_NAME);
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.addComponentListener(this);
+ frame.setSize(1200, 850);
+
+ containerPanel = new JPanel();
+ frame.getContentPane().add(containerPanel);
+
+ containerPanel.add(areaControls.getPanel());
+ containerPanel.add(areaHosts.getPanel());
+ containerPanel.add(areaResults.getPanel());
+ containerPanel.add(areaLog.getPanel());
+
+ myMenuBar = new MyMenuBar(this);
+ frame.setJMenuBar(myMenuBar);
+
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ }
+
+ public void setDefaultPopupPosition(JDialog popup) {
+ Dimension parentSize = frame.getSize();
+ Point p = frame.getLocation();
+ popup.setLocation(p.x + parentSize.width / 2 - popup.getWidth() / 2, p.y + 150);
+ }
+
+ public void showAppInfoDialog(boolean isIntro) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(Util.T_APP_NAME + " " + Util.T_APP_VERSION + Util.LF);
+ sb.append(Util.LF);
+
+ if (!isIntro) {
+ sb.append(Util.T_APP_NAME + " is licensed under the " + Util.T_APP_LICENSE + "." + Util.LF);
+ sb.append(Util.LF);
+ }
+
+ sb.append("WARNING: Scanning can generate undesired load on the servers!" + Util.LF);
+ sb.append("DO NOT use this program if you are unsure about the consequences!" + Util.LF);
+ sb.append(Util.LF);
+
+ if (!isIntro) {
+ sb.append("Developer: " + Util.T_AUTOR + Util.LF);
+ sb.append("Contact: " + Util.T_CONTACT_EMAIL + Util.LF);
+ sb.append("Code: " + Util.T_CODE_URL + Util.LF);
+ }
+
+ showMessageDialog("About", sb.toString());
+ }
+
+ public boolean showConfirmDialog(String title, String text) {
+ DialogConfirm dialog = new DialogConfirm(this, title, text);
+ return dialog.showDialog();
+ }
+
+ public void showMessageDialog(String title, String text) {
+ DialogMessage dialog = new DialogMessage(this, title, text);
+ dialog.showDialog();
+ }
+
+ @Override
+ public void componentResized(ComponentEvent e) {
+ int cWidth = containerPanel.getWidth();
+ int cHeight = containerPanel.getHeight();
+
+ final int controlAreaHeight = 40;
+ final int logAreaHeight = 200;
+
+ areaControls.setPanelBounds(0, 0, cWidth, controlAreaHeight);
+
+ areaLog.setPanelBounds(0, cHeight - logAreaHeight, cWidth, logAreaHeight);
+
+ int centerAreasHeight = cHeight - logAreaHeight - controlAreaHeight;
+ areaHosts.setPanelBounds(0, controlAreaHeight, HOST_AREA_WIDTH, centerAreasHeight);
+
+ int resultsAreaWidth = cWidth - HOST_AREA_WIDTH;
+ areaResults.setPanelBounds(HOST_AREA_WIDTH, controlAreaHeight, resultsAreaWidth, centerAreasHeight);
+ }
+
+ @Override
+ public void componentHidden(ComponentEvent e) {
+ }
+
+ @Override
+ public void componentMoved(ComponentEvent e) {
+ }
+
+ @Override
+ public void componentShown(ComponentEvent e) {
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/MyMenuBar.java b/src/org/bitbatzen/tlsserverscanner/gui/MyMenuBar.java
new file mode 100644
index 0000000..6360fc0
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/MyMenuBar.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JFileChooser;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+
+import org.bitbatzen.tlsserverscanner.Util;
+import org.bitbatzen.tlsserverscanner.gui.DialogSelectCipherSuites.DialogType;
+import org.bitbatzen.tlsserverscanner.scantask.ScanTask.State;
+import org.bitbatzen.tlsserverscanner.scantask.ScanTaskHandler;
+
+
+public class MyMenuBar extends JMenuBar implements ActionListener {
+
+ // menu file
+ private JMenuItem itemSaveHostList;
+ private JMenuItem itemLoadHostList;
+ private JMenuItem itemQuit;
+
+ // menu options
+ private JMenuItem itemCertOptions;
+ private JMenuItem itemSelectCipherSuites;
+ private JMenuItem itemSelectProtocols;
+ private JCheckBoxMenuItem itemCBDisableCertCollecting;
+ private JCheckBoxMenuItem itemCBDisableCertVerification;
+
+ // menu tools
+ private JMenuItem itemViewCertificates;
+ private JMenuItem itemSaveSelectedCertificates;
+ private JMenuItem itemSearch;
+
+ // menu info
+ private JMenuItem itemAbout;
+
+ private MainWindow mainWindow;
+
+ private Font font;
+
+
+ public MyMenuBar(MainWindow mainWindow) {
+ this.mainWindow = mainWindow;
+ font = new JMenuItem().getFont().deriveFont(13.0f);
+ initMenuBar();
+ }
+
+
+ private void initMenuBar() {
+ setBackground(MainWindow.COLOR_BG_MENUBAR);
+
+ // file
+ JMenu menuFile = createMenu("File");
+ itemSaveHostList = createMenuItem("Save Hostlist");
+ menuFile.add(itemSaveHostList);
+
+ itemLoadHostList = createMenuItem("Load Hostlist");
+ menuFile.add(itemLoadHostList);
+
+ itemQuit = createMenuItem("Quit");
+ menuFile.add(itemQuit);
+
+ // options
+ JMenu menuOptions = createMenu("Options");
+ itemCertOptions = createMenuItem("Certificate");
+ menuOptions.add(itemCertOptions);
+
+ itemSelectCipherSuites = createMenuItem("Cipher Suites");
+ menuOptions.add(itemSelectCipherSuites);
+
+ itemSelectProtocols = createMenuItem("Protocols");
+ menuOptions.add(itemSelectProtocols);
+
+ // checkbox disable cert. collecting
+ itemCBDisableCertCollecting = new JCheckBoxMenuItem("Disable Cert. Collecting");
+ itemCBDisableCertCollecting.setFont(font);
+ menuOptions.add(itemCBDisableCertCollecting);
+
+ // checkbox disable cert. verification
+ itemCBDisableCertVerification = new JCheckBoxMenuItem("Disable Cert. Verification");
+ itemCBDisableCertVerification.setFont(font);
+ menuOptions.add(itemCBDisableCertVerification);
+
+ // tools
+ JMenu menuTools = createMenu("Tools");
+ itemViewCertificates = createMenuItem("View Certificates");
+ menuTools.add(itemViewCertificates);
+
+ itemSaveSelectedCertificates = createMenuItem("Save Selected Certificates");
+ menuTools.add(itemSaveSelectedCertificates);
+
+ itemSearch = createMenuItem("Full-Text Search");
+ menuTools.add(itemSearch);
+
+ // info
+ JMenu menuInfo = createMenu("Info");
+ itemAbout = createMenuItem("About");
+ menuInfo.add(itemAbout);
+
+ add(menuFile);
+ add(menuOptions);
+ add(menuTools);
+ add(menuInfo);
+ }
+
+ public boolean getCertVerificationDisabled() {
+ return itemCBDisableCertVerification.isSelected();
+ }
+
+ public boolean getCertCollectingDisabled() {
+ return itemCBDisableCertCollecting.isSelected();
+ }
+
+ private JMenuItem createMenuItem(String name) {
+ JMenuItem menuItem = new JMenuItem(name);
+ menuItem.setFont(font);
+ menuItem.addActionListener(this);
+ return menuItem;
+ }
+
+ private JMenu createMenu(String name) {
+ JMenu menu = new JMenu(name);
+ menu.setFont(font);
+ return menu;
+ }
+
+ private void onClickSaveHostList() {
+ String title = "Save Hostlist";
+ String hostlist = mainWindow.getAreaHosts().getHostList();
+ if (hostlist.equals("")) {
+ mainWindow.showMessageDialog(title, "Hostlist is empty!");
+ return;
+ }
+
+ final JFileChooser fc = new JFileChooser(title);
+ fc.setDialogTitle(title);
+ fc.setApproveButtonText("Save");
+ fc.setApproveButtonToolTipText("Save");
+ int returnVal = fc.showOpenDialog(mainWindow.getFrame());
+
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+ String prefix = fc.getSelectedFile().getName().toLowerCase().contains(".txt") ? "" : ".txt";
+ File file = new File(fc.getSelectedFile().getAbsolutePath() + prefix);
+ if (file.exists()) {
+ String text = "Replace " + file.getName() + " ?";
+ if (mainWindow.showConfirmDialog(title, text) == false) {
+ return;
+ }
+ }
+
+ try {
+ Util.saveHostlistToFile(hostlist, file);
+ }
+ catch (Exception e) {
+ mainWindow.showMessageDialog(title, "Failed to save file:"
+ + Util.BR + e.getMessage() + "");
+ }
+
+ mainWindow.showMessageDialog(title, "Saved hostlist to:"
+ + Util.BR + Util.BR + file.getPath() + "");
+ }
+ }
+
+ private void onClickLoadHostList() {
+ String title = "Load Hostlist";
+ if (mainWindow.getAreaHosts().getHostListItemCount() > 0) {
+ if (mainWindow.showConfirmDialog(title, "The existing hostlist will be replaced!") == false) {
+ return;
+ }
+ }
+
+ final JFileChooser fc = new JFileChooser(title);
+ fc.setDialogTitle(title);
+ int returnVal = fc.showOpenDialog(mainWindow.getFrame());
+
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+ List hostlist = new ArrayList<>();
+
+ try {
+ int result = Util.loadHostlistFromFile(hostlist, fc.getSelectedFile());
+ if (result != 0) {
+ mainWindow.showMessageDialog(title, "Syntax error in line " + result
+ + Util.BR + Util.BR + "(syntax: host:port)");
+ return;
+ }
+ }
+ catch (Exception e) {
+ mainWindow.showMessageDialog(title, "Failed to read file:"
+ + Util.BR + e.getMessage() + "");
+ return;
+ }
+
+ if (hostlist.size() == 0) {
+ mainWindow.showMessageDialog(title, "Hostlist is empty!");
+ return;
+ }
+
+ if (hostlist.size() > 200) {
+ mainWindow.showMessageDialog(title, "" + hostlist.size() + " hosts to load!"
+ + Util.BR + "This could take a few seconds.");
+ }
+
+ mainWindow.getAreaHosts().removeAllHosts();
+
+ for (String h : hostlist) {
+ String host = Util.extractHost(h);
+ int port = Util.extractPort(h);
+ mainWindow.getAreaHosts().addHost(host, port);
+ }
+
+ mainWindow.showMessageDialog(title, "Added " + hostlist.size() + " hosts!");
+ }
+ }
+
+ private void onClickViewCertificate() {
+ HostListItem host = mainWindow.getAreaHosts().getSelectedItem();
+ if (host == null) {
+ mainWindow.showMessageDialog("Certificates", "No host selected!");
+ return;
+ }
+ else if (host.getScanTask() == null || host.getScanTask().getScanData().getCertAvailable() == false) {
+ mainWindow.showMessageDialog("Certificates", "No certificate found!");
+ return;
+ }
+
+ String title = "Certificates (" + host.getHostWithPort() + ")";
+ new DialogViewCertificates(mainWindow, title, host);
+ }
+
+ private void onClickQuit() {
+ if (mainWindow.showConfirmDialog("Quit", "Unsaved data will be lost!
Quit?")) {
+ mainWindow.getFrame().dispose();
+ }
+ }
+
+ private void onClickCertOptions() {
+ new DialogSelectCipherSuites(mainWindow, DialogType.CIPHER_SUITES_FOR_COLLECTING_CERT, "Certifcate");
+ }
+
+ private void onClickSelectCipherSuitesToTest() {
+ new DialogSelectCipherSuites(mainWindow, DialogType.CIPHER_SUITES_TO_TEST, "Cipher Suites");
+ }
+
+ private void onClickSelectProtocols() {
+ new DialogSelectProtocols(mainWindow);
+ }
+
+ private void onClickSaveSelectedCertificates() {
+ String title = "Save Selected Certificates";
+ List hosts = mainWindow.getAreaHosts().getSelectedHosts();
+ if (hosts.isEmpty()) {
+ mainWindow.showMessageDialog(title, "No host selected!");
+ return;
+ }
+
+ int certificateCount = 0;
+ int allCertificatesCount = 0;
+ for (HostListItem host : hosts) {
+ if (host.getScanTask() != null && host.getScanTask().getScanData().getCertAvailable()) {
+ certificateCount++;
+ allCertificatesCount += host.getScanTask().getScanData().certs.length;
+ }
+ }
+ if (certificateCount == 0) {
+ mainWindow.showMessageDialog(title, "No certificates found!");
+ return;
+ }
+ else {
+ String hostString = certificateCount == 1 ? "host" : "hosts";
+ mainWindow.showMessageDialog(title, "Found " + allCertificatesCount + " certificates!"
+ + " (" + certificateCount + " " + hostString + " selected)");
+ }
+
+
+ final JFileChooser fc = new JFileChooser();
+ fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ fc.setDialogTitle("Select a Folder");
+ fc.setApproveButtonText("Save");
+ fc.setApproveButtonToolTipText("Save to Folder");
+ int returnVal = fc.showOpenDialog(mainWindow.getFrame());
+
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+ File directory = fc.getSelectedFile();
+
+ try {
+ Util.saveCertificatesToDirectory(hosts, directory);
+ }
+ catch (Exception e) {
+ mainWindow.showMessageDialog(title, "Failed to save certificates:"
+ + Util.BR + e.getMessage() + "");
+ }
+
+ mainWindow.showMessageDialog(title, "Saved certificates to:"
+ + Util.BR + Util.BR + directory.getPath() + "");
+ }
+ }
+
+ private void onClickSearch() {
+ ScanTaskHandler scanTaskHandler = mainWindow.getAreaControls().getScanTaskHandler();
+ if (mainWindow.getAreaHosts().getHostListItemCount() == 0) {
+ mainWindow.showMessageDialog(itemSearch.getText(), "Hostlist is empty!");
+ return;
+ }
+ else if (scanTaskHandler != null && scanTaskHandler.getState() == State.RUNNING) {
+ mainWindow.showMessageDialog(itemSearch.getText(), "You have to finish the current scan!");
+ return;
+ }
+
+ new DialogSearch(mainWindow);
+ }
+
+ private void onClickAbout() {
+ mainWindow.showAppInfoDialog(false);
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ if (arg0.getSource() == itemSaveHostList) {
+ onClickSaveHostList();
+ }
+ else if (arg0.getSource() == itemLoadHostList) {
+ onClickLoadHostList();
+ }
+ else if (arg0.getSource() == itemSaveSelectedCertificates) {
+ onClickSaveSelectedCertificates();
+ }
+ else if (arg0.getSource() == itemQuit) {
+ onClickQuit();
+ }
+ else if (arg0.getSource() == itemCertOptions) {
+ onClickCertOptions();
+ }
+ else if (arg0.getSource() == itemSelectCipherSuites) {
+ onClickSelectCipherSuitesToTest();
+ }
+ else if (arg0.getSource() == itemSelectProtocols) {
+ onClickSelectProtocols();
+ }
+ else if (arg0.getSource() == itemViewCertificates) {
+ onClickViewCertificate();
+ }
+ else if (arg0.getSource() == itemSearch) {
+ onClickSearch();
+ }
+ else if (arg0.getSource() == itemAbout) {
+ onClickAbout();
+ }
+ }
+
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/gui/StatisticUtil.java b/src/org/bitbatzen/tlsserverscanner/gui/StatisticUtil.java
new file mode 100644
index 0000000..93b1a29
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/gui/StatisticUtil.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.gui;
+
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.bitbatzen.tlsserverscanner.Util;
+import org.bitbatzen.tlsserverscanner.scantask.Cert;
+import org.bitbatzen.tlsserverscanner.scantask.SSLUtil;
+import org.bitbatzen.tlsserverscanner.scantask.ScanTask;
+import org.bitbatzen.tlsserverscanner.scantask.ScanTaskHandler;
+
+
+public class StatisticUtil {
+
+
+ private static > Map sortByValues(final Map map) {
+ Comparator valueComparator = new Comparator() {
+ public int compare(K k1, K k2) {
+ int compare = map.get(k2).compareTo(map.get(k1));
+ if (compare == 0) return 1;
+ else return compare;
+ }
+ };
+
+ Map sortedByValues = new TreeMap(valueComparator);
+ sortedByValues.putAll(map);
+ return sortedByValues;
+ }
+
+ private static HashMap createEmptyStatisicMap(int size) {
+ HashMap statisticMap = new HashMap<>();
+ for (int i = 0; i < size; i++) {
+ statisticMap.put(i, 0);
+ }
+
+ return statisticMap;
+ }
+
+ private static String getAmountString(float max, float v) {
+ if (v > 0) {
+ String percentage = String.format("%.2f", (float) v / (float) max * 100.0f);
+ return " (" + (int) v + ", " + percentage + "%)";
+ }
+ else {
+ return " (" + (int) v + ")";
+ }
+ }
+
+ public static void appendCipherSuiteChosenFromServerStatistic(StringBuilder sb, ScanTaskHandler scanTaskHandler) {
+ HashMap statisticMap = new HashMap<>();
+
+ for (ScanTask st : scanTaskHandler.getScanTasks()) {
+ if (st.getScanData().getCertAvailable() == false) {
+ continue;
+ }
+
+ String sscCipherSuite = st.getScanData().cipherSuiteChosenByServer;
+ if (sscCipherSuite.isEmpty() == false) {
+ if (statisticMap.containsKey(sscCipherSuite)) {
+ int v = statisticMap.get(sscCipherSuite) + 1;
+ statisticMap.put(sscCipherSuite, v);
+ }
+ else {
+ statisticMap.put(sscCipherSuite, 1);
+ }
+ }
+ }
+
+ Map sortedMap = sortByValues(statisticMap);
+ for (Map.Entry entry : sortedMap.entrySet()) {
+ sb.append(entry.getKey() + " " + getAmountString(scanTaskHandler.getScanTasks().size(), entry.getValue()) + Util.BR);
+ }
+ }
+
+
+ public static void appendProtocolStatistic(StringBuilder sb, ScanTaskHandler scanTaskHandler) {
+ HashMap statisticMap = createEmptyStatisicMap(scanTaskHandler.getProtocolsToTestCount());
+
+ for (ScanTask st : scanTaskHandler.getScanTasks()) {
+ HashMap tested = st.getScanData().protocolsTested;
+ for (Map.Entry entry : tested.entrySet()) {
+ if (entry.getValue() == true) {
+ statisticMap.put(entry.getKey(), statisticMap.get(entry.getKey()) + 1);
+ }
+ }
+ }
+
+ Map sortedMap = sortByValues(statisticMap);
+ for (Map.Entry entry : sortedMap.entrySet()) {
+ String protocol = scanTaskHandler.getProtocolToTest(entry.getKey());
+ sb.append(protocol + getAmountString(scanTaskHandler.getScanTasks().size(), entry.getValue()) + Util.BR);
+ }
+ }
+
+ public static void appendCipherSuiteStatistic(StringBuilder sb, ScanTaskHandler scanTaskHandler) {
+ HashMap statisticMap = createEmptyStatisicMap(scanTaskHandler.getCipherSuitesToTestCount());
+
+ for (ScanTask st : scanTaskHandler.getScanTasks()) {
+ HashMap tested = st.getScanData().cipherSuitesTested;
+ for (Map.Entry entry : tested.entrySet()) {
+ if (entry.getValue() == true) {
+ statisticMap.put(entry.getKey(), statisticMap.get(entry.getKey()) + 1);
+ }
+ }
+ }
+
+ Map sortedMap = sortByValues(statisticMap);
+ for (Map.Entry entry : sortedMap.entrySet()) {
+ String cipherSuite = scanTaskHandler.getCipherSuiteToTest(entry.getKey());
+ sb.append(cipherSuite + getAmountString(scanTaskHandler.getScanTasks().size(), entry.getValue()) + Util.BR);
+ }
+ }
+
+ public static void appendCertificatePubKeyAlgorithmStatistic(StringBuilder sb, ScanTaskHandler scanTaskHandler) {
+ HashMap statisticMap = new HashMap<>();
+
+ for (ScanTask st : scanTaskHandler.getScanTasks()) {
+ if (st.getScanData().getCertAvailable() == false) {
+ continue;
+ }
+
+ String pubKeyAlgorithm = st.getScanData().certs[0].getPublicKeyAlgorithm();
+ if (pubKeyAlgorithm.isEmpty() == false) {
+ if (statisticMap.containsKey(pubKeyAlgorithm)) {
+ int v = statisticMap.get(pubKeyAlgorithm) + 1;
+ statisticMap.put(pubKeyAlgorithm, v);
+ }
+ else {
+ statisticMap.put(pubKeyAlgorithm, 1);
+ }
+ }
+ }
+
+ Map sortedMap = sortByValues(statisticMap);
+ for (Map.Entry entry : sortedMap.entrySet()) {
+ sb.append(entry.getKey() + " " + getAmountString(scanTaskHandler.getScanTasks().size(), entry.getValue()) + Util.BR);
+ }
+ }
+
+ public static void appendCertificatePubKeyAlgorithmWithKeyLengthStatistic(StringBuilder sb, ScanTaskHandler scanTaskHandler) {
+ HashMap statisticMap = new HashMap<>();
+
+ for (ScanTask st : scanTaskHandler.getScanTasks()) {
+ if (st.getScanData().getCertAvailable() == false) {
+ continue;
+ }
+
+ String pubKeyAlgorithm = st.getScanData().certs[0].getPublicKeyAlgorithm();
+ if (pubKeyAlgorithm.isEmpty()) {
+ continue;
+ }
+
+ pubKeyAlgorithm = st.getScanData().certs[0].getPublicKeyAlgorithmWithKeyLength();
+ if (statisticMap.containsKey(pubKeyAlgorithm)) {
+ int v = statisticMap.get(pubKeyAlgorithm) + 1;
+ statisticMap.put(pubKeyAlgorithm, v);
+ }
+ else {
+ statisticMap.put(pubKeyAlgorithm, 1);
+ }
+ }
+
+ Map sortedMap = sortByValues(statisticMap);
+ for (Map.Entry entry : sortedMap.entrySet()) {
+ sb.append(entry.getKey() + " " + getAmountString(scanTaskHandler.getScanTasks().size(), entry.getValue()) + Util.BR);
+ }
+ }
+
+ public static void appendCertificateSignatureAlgorithmStatistic(StringBuilder sb, ScanTaskHandler scanTaskHandler) {
+ HashMap statisticMap = new HashMap<>();
+
+ for (ScanTask st : scanTaskHandler.getScanTasks()) {
+ if (st.getScanData().getCertAvailable() == false) {
+ continue;
+ }
+
+ String signatureAlgorithm = st.getScanData().certs[0].getSignatureAlgorithm();
+ if (signatureAlgorithm.isEmpty() == false) {
+ if (statisticMap.containsKey(signatureAlgorithm)) {
+ int v = statisticMap.get(signatureAlgorithm) + 1;
+ statisticMap.put(signatureAlgorithm, v);
+ }
+ else {
+ statisticMap.put(signatureAlgorithm, 1);
+ }
+ }
+ }
+
+ Map sortedMap = sortByValues(statisticMap);
+ for (Map.Entry entry : sortedMap.entrySet()) {
+ sb.append(entry.getKey() + " " + getAmountString(scanTaskHandler.getScanTasks().size(), entry.getValue()) + Util.BR);
+ }
+ }
+
+ public static void appendCertificateExtensionStatistic(StringBuilder sb, ScanTaskHandler scanTaskHandler) {
+ HashMap statisticMap = new HashMap<>();
+
+ for (ScanTask st : scanTaskHandler.getScanTasks()) {
+ if (st.getScanData().getCertAvailable() == false) {
+ continue;
+ }
+
+ List extensions = st.getScanData().certs[0].getExtensionOIDs();
+ for (String s : extensions) {
+ if (statisticMap.containsKey(s)) {
+ int v = statisticMap.get(s) + 1;
+ statisticMap.put(s, v);
+ }
+ else {
+ statisticMap.put(s, 1);
+ }
+ }
+ }
+
+ Map sortedMap = sortByValues(statisticMap);
+ for (Map.Entry entry : sortedMap.entrySet()) {
+ String extension = "(" + entry.getKey() + ") " + SSLUtil.getCertExtensionName(entry.getKey());
+ sb.append(extension + getAmountString(scanTaskHandler.getScanTasks().size(), entry.getValue()) + Util.BR);
+ }
+ }
+
+ public static void appendCertificateRootCAOrganisationStatistic(StringBuilder sb, ScanTaskHandler scanTaskHandler) {
+ HashMap statisticMap = new HashMap<>();
+
+ for (ScanTask st : scanTaskHandler.getScanTasks()) {
+ if (st.getScanData().getCertAvailable() == false) {
+ continue;
+ }
+
+ String rootCAOrg = Cert.getNameValue(st.getScanData().getCertRootName(), "O=");
+ if (rootCAOrg.isEmpty() == false) {
+ if (statisticMap.containsKey(rootCAOrg)) {
+ int v = statisticMap.get(rootCAOrg) + 1;
+ statisticMap.put(rootCAOrg, v);
+ }
+ else {
+ statisticMap.put(rootCAOrg, 1);
+ }
+ }
+ }
+
+ Map sortedMap = sortByValues(statisticMap);
+ for (Map.Entry entry : sortedMap.entrySet()) {
+ sb.append(entry.getKey() + getAmountString(scanTaskHandler.getScanTasks().size(), entry.getValue()) + Util.BR);
+ }
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/scantask/Cert.java b/src/org/bitbatzen/tlsserverscanner/scantask/Cert.java
new file mode 100644
index 0000000..090dc6f
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/scantask/Cert.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.scantask;
+
+import java.security.cert.X509Certificate;
+import java.math.BigInteger;
+import java.security.cert.CertificateParsingException;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import javax.crypto.interfaces.DHPublicKey;
+
+import org.bitbatzen.tlsserverscanner.Util;
+
+
+public class Cert {
+
+ private X509Certificate cert;
+
+
+ public Cert(X509Certificate cert) {
+ this.cert = cert;
+ }
+
+ public X509Certificate getCert() {
+ return cert;
+ }
+
+ public String getSubjectName() {
+ return cert.getSubjectX500Principal().getName();
+ }
+
+ public String getIssuerName() {
+ return cert.getIssuerX500Principal().getName();
+ }
+
+ public static String getNameValue(String certName, String searchTag) {
+ if (certName == null || certName.isEmpty()) {
+ return "";
+ }
+
+ int begin = certName.indexOf(searchTag);
+ int end = certName.indexOf(",", begin);
+ if (end < begin) {
+ end = certName.length();
+ }
+
+ while (certName.charAt(end - 1) == '\\') {
+ end = certName.indexOf(",", end + 1);
+ }
+
+ if (begin != -1 && end != -1) {
+ String subString = certName.substring(begin + searchTag.length(), end);
+ return subString.replace("\\", "");
+ }
+ else {
+ return "";
+ }
+ }
+
+ public static String formatCertName(String s) {
+ if (s == null) {
+ return "";
+ }
+
+ s = s.replace("\\", "");
+ return s.replace(",", ", ");
+ }
+
+ public static String formatCertName2(String s) {
+ if (s == null) {
+ return "";
+ }
+
+ s = s.replace("\\,", "@@@");
+ String formatted = "";
+ String[] splitted = s.split(",");
+ for (String ss : splitted) {
+ formatted += ss.replace("=", " = ") + Util.BR;
+ }
+
+ return formatted.replace("@@@", ",");
+ }
+
+ public String[] getSubjectAlternativeNames() {
+ try {
+ Collection> col = cert.getSubjectAlternativeNames();
+ if (col == null) {
+ return null;
+ }
+ String[] alts = new String[col.size()];
+ int c = 0;
+ for (List> l : col) {
+ alts[c] = l.get(1).toString();
+ c++;
+ }
+ return alts;
+ }
+ catch (CertificateParsingException e) {
+ return null;
+ }
+ }
+
+ public String getVersion() {
+ return Integer.toString(cert.getVersion());
+ }
+
+ public String getType() {
+ return cert.getType();
+ }
+
+ public BigInteger getSerialNumber() {
+ return cert.getSerialNumber();
+ }
+
+ public Date getNotBefore() {
+ return cert.getNotBefore();
+ }
+
+ public Date getNotAfter() {
+ return cert.getNotAfter();
+ }
+
+ public String getSignatureAlgorithm() {
+ return cert.getSigAlgName();
+ }
+
+ public String getPublicKeyAlgorithm() {
+ return cert.getPublicKey().getAlgorithm();
+ }
+
+ public String getPublicKeyLength() {
+ if (cert.getPublicKey() instanceof RSAPublicKey) {
+ RSAPublicKey rsaPubKey = (RSAPublicKey) cert.getPublicKey();
+ return Integer.toString(rsaPubKey.getModulus().bitLength());
+ }
+ else if (cert.getPublicKey() instanceof ECPublicKey) {
+ ECPublicKey ecPubKey = (ECPublicKey) cert.getPublicKey();
+ return Integer.toString(ecPubKey.getParams().getCurve().getField().getFieldSize());
+ }
+ else if (cert.getPublicKey() instanceof DHPublicKey) {
+ DHPublicKey dhPubKey = (DHPublicKey) cert.getPublicKey();
+ return Integer.toString(dhPubKey.getParams().getP().bitLength());
+ }
+ else if (cert.getPublicKey() instanceof DSAPublicKey) {
+ DSAPublicKey dsaPubKey = (DSAPublicKey) cert.getPublicKey();
+ return Integer.toString(dsaPubKey.getParams().getP().bitLength());
+ }
+ else {
+ return "Unkown";
+ }
+ }
+
+ public String getPublicKeyAlgorithmWithKeyLength() {
+ return getPublicKeyAlgorithm() + " (" + getPublicKeyLength() + ")";
+ }
+
+ public List getExtensionOIDs() {
+ List oids = new ArrayList<>();
+
+ Set criticalExtensions = cert.getCriticalExtensionOIDs();
+ if (criticalExtensions != null && criticalExtensions.isEmpty() == false) {
+ oids.addAll(criticalExtensions);
+ }
+
+ Set nonCriticalExtensions = cert.getNonCriticalExtensionOIDs();
+ if (nonCriticalExtensions != null && nonCriticalExtensions.isEmpty() == false) {
+ oids.addAll(nonCriticalExtensions);
+ }
+
+ return oids;
+ }
+
+ public List getExtensionOIDsWithName() {
+ List oidsWithName = new ArrayList<>();
+ List oids = getExtensionOIDs();
+ for (String oid : oids) {
+ oidsWithName.add("(" + oid + ") " + SSLUtil.getCertExtensionName(oid));
+ }
+
+ return oidsWithName;
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/scantask/IScanTaskHandlerListener.java b/src/org/bitbatzen/tlsserverscanner/scantask/IScanTaskHandlerListener.java
new file mode 100644
index 0000000..59b1542
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/scantask/IScanTaskHandlerListener.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.scantask;
+
+
+public interface IScanTaskHandlerListener {
+
+ public void onScanTaskHandlerMessage(ScanTask scanTask, String message);
+
+ public void onScanTaskHandlerDone();
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/scantask/IScanTaskListener.java b/src/org/bitbatzen/tlsserverscanner/scantask/IScanTaskListener.java
new file mode 100644
index 0000000..0b84b75
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/scantask/IScanTaskListener.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.scantask;
+
+
+public interface IScanTaskListener {
+
+ public void onScanTaskStateChanged(ScanTask scanTask, String message);
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/scantask/SSLUtil.java b/src/org/bitbatzen/tlsserverscanner/scantask/SSLUtil.java
new file mode 100644
index 0000000..f2ca6c5
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/scantask/SSLUtil.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.scantask;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.bitbatzen.tlsserverscanner.Util;
+
+
+public class SSLUtil {
+
+ private static final HashMap certExtensionsMap = createCertExtensionsMap();
+
+ private static HostnameVerifier DEFAULT_HOSTNAME_VERIFIER = HttpsURLConnection.getDefaultHostnameVerifier();
+ private static SSLSocketFactory DEFAULT_SSL_SOCKET_FACTORY = HttpsURLConnection.getDefaultSSLSocketFactory();
+
+ private static final TrustManager[] ALL_TRUSTING_TRUST_MANAGER = new TrustManager[] {
+ new X509TrustManager() {
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ public void checkClientTrusted(X509Certificate[] certs, String authType) {}
+ public void checkServerTrusted(X509Certificate[] certs, String authType) {}
+ }
+ };
+
+ private static final HostnameVerifier ALL_TRUSTING_HOSTNAME_VERIFIER = new HostnameVerifier() {
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ };
+
+ public static SSLSocketFactory getSSLSocketFactory(boolean certAndHostnameVerificationEnabled) {
+ HostnameVerifier hostnameVerifier = DEFAULT_HOSTNAME_VERIFIER;
+ SSLSocketFactory sslSocketFactory = DEFAULT_SSL_SOCKET_FACTORY;
+
+ if (certAndHostnameVerificationEnabled == false) {
+ try {
+ SSLContext sc = SSLContext.getInstance("TLS");
+ sc.init(null, ALL_TRUSTING_TRUST_MANAGER, new java.security.SecureRandom());
+ sslSocketFactory = sc.getSocketFactory();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ hostnameVerifier = ALL_TRUSTING_HOSTNAME_VERIFIER;
+ }
+
+ HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
+ HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
+
+ return HttpsURLConnection.getDefaultSSLSocketFactory();
+ }
+
+ public static List getAvailableCipherSuites() {
+ String[] cipherSuites = HttpsURLConnection.getDefaultSSLSocketFactory().getDefaultCipherSuites();
+ List list = new ArrayList<>();
+ for (String s : cipherSuites) {
+ list.add(s);
+ }
+
+ return list;
+ }
+
+ public static List getAvailableProtocols() {
+ List list = new ArrayList<>();
+ SSLSocket socket = null;
+
+ try {
+ socket = (SSLSocket) getSSLSocketFactory(false).createSocket();
+ String[] protocols = socket.getSupportedProtocols();
+ for (String s : protocols) {
+ if (s.equals("SSLv2Hello") == false) {
+ list.add(s);
+ }
+ }
+ return list;
+ }
+ catch (IOException e) {
+ return list;
+ }
+ finally {
+ Util.close(socket);
+ }
+ }
+
+ public static String getCertExtensionName(String oid) {
+ String name = certExtensionsMap.get(oid);
+ return (name == null) ? "Unkown" : name;
+ }
+
+ private static HashMap createCertExtensionsMap() {
+ HashMap map = new HashMap<>();
+// map.put("1.3.6.1.4.1.11129.2.4.2", "");
+ map.put("1.3.6.1.5.5.7.1.1", "Authority Info Access");
+
+ map.put("2.5.29.1", "old Authority Key Identifier");
+ map.put("2.5.29.2", "old Primary Key Attributes ");
+ map.put("2.5.29.3", "Certificate Policies");
+ map.put("2.5.29.4", "Primary Key Usage Restriction");
+ map.put("2.5.29.9", "Subject Directory Attributes");
+
+ map.put("2.5.29.14", "Subject Key Identifier");
+ map.put("2.5.29.15", "Key Usage");
+ map.put("2.5.29.16", "Private Key Usage Period");
+ map.put("2.5.29.17", "Subject Alternative Name");
+ map.put("2.5.29.18", "Issuer Alternative Name");
+
+ map.put("2.5.29.19", "Basic Constraints");
+ map.put("2.5.29.20", "CRL Number");
+ map.put("2.5.29.21", "Reason code");
+ map.put("2.5.29.23", "Hold Instruction Code");
+ map.put("2.5.29.24", "Invalidity Date");
+
+ map.put("2.5.29.27", "Delta CRL indicator");
+ map.put("2.5.29.28", "Issuing Distribution Point");
+ map.put("2.5.29.29", "Certificate Issuer");
+ map.put("2.5.29.30", "Name Constraints");
+ map.put("2.5.29.31", "CRL Distribution Points");
+
+ map.put("2.5.29.32", "Certificate Policies");
+ map.put("2.5.29.33", "Policy Mappings");
+ map.put("2.5.29.35", "Authority Key Identifier");
+ map.put("2.5.29.36", "Policy Constraints");
+ map.put("2.5.29.37", "Extended key usage");
+
+ map.put("2.5.29.46", "FreshestCRL");
+ map.put("2.5.29.54", "X.509 version 3 certificate extension Inhibit Any-policy");
+ return map;
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/scantask/ScanData.java b/src/org/bitbatzen/tlsserverscanner/scantask/ScanData.java
new file mode 100644
index 0000000..e48af87
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/scantask/ScanData.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.scantask;
+
+import java.net.InetAddress;
+import java.util.HashMap;
+
+
+public class ScanData {
+
+ public String host;
+ public int port;
+
+ public InetAddress inetAddr;
+
+ public boolean scanStarted;
+ public boolean scanFinished;
+
+ public boolean scanAborted;
+ public boolean scanStoppedByUser;
+
+ public enum ScanError {
+ NONE,
+ FAILED_TO_RESOLVE_HOSTNAME,
+ PORT_NOT_REACHABLE,
+ FAILED_TO_CREATE_SOCKET,
+ INVALID_SSL_PARAMETER,
+ SSL_HANDSHAKE_TIMEOUT,
+ MAYBE_NO_SSL_SERVICE_RUNNING,
+ INVALID_CERTIFICATE,
+ FAILED_TO_FETCH_CERTIFICATE
+ }
+
+ public ScanError scanError;
+
+ public HashMap cipherSuitesTested;
+ public HashMap protocolsTested;
+
+ public Cert[] certs;
+
+ public String cipherSuiteChosenByServer;
+
+
+ public ScanData(String host, int port) {
+ this.host = host;
+ this.port = port;
+ inetAddr = null;
+
+ scanStarted = false;
+ scanFinished = false;
+
+ scanAborted = false;
+ scanStoppedByUser = false;
+
+ scanError = ScanError.NONE;
+
+ cipherSuitesTested = new HashMap<>();
+ protocolsTested = new HashMap<>();
+
+ certs = null;
+ cipherSuiteChosenByServer = "";
+ }
+
+ public static String getErrorInfo(ScanError scanError) {
+ switch (scanError) {
+ case FAILED_TO_RESOLVE_HOSTNAME:
+ return "Failed to resolve hostname!";
+ case PORT_NOT_REACHABLE:
+ return "Port not reachable!";
+ case FAILED_TO_CREATE_SOCKET:
+ return "Failed to create ssl socket!";
+ case INVALID_SSL_PARAMETER:
+ return "Invalid ssl parameter!";
+ case SSL_HANDSHAKE_TIMEOUT:
+ return "Timeout during ssl handshake!";
+ case MAYBE_NO_SSL_SERVICE_RUNNING:
+ return "No ssl service running?";
+ case INVALID_CERTIFICATE:
+ return "Invalid certificate!";
+ case FAILED_TO_FETCH_CERTIFICATE:
+ return "Failed to fetch certificate! (Cipher suites not supported?)";
+ default:
+ return "";
+ }
+ }
+
+ public String getErrorInfo() {
+ return getErrorInfo(scanError);
+ }
+
+ public boolean isHostnameResolved() {
+ return (inetAddr != null && inetAddr.getHostAddress() != null && inetAddr.getHostAddress() != "");
+ }
+
+ public String getHostAddress() {
+ if (isHostnameResolved()) {
+ return inetAddr.getHostAddress();
+ }
+ else {
+ return "";
+ }
+ }
+
+ public String getHostWithPort() {
+ return host + ":" + port;
+ }
+
+ public boolean getCertAvailable() {
+ return certs != null && certs.length > 0 && certs[0] != null;
+ }
+
+ public String getCertSubjectName() {
+ return certs[0].getSubjectName();
+ }
+
+ public String[] getCertNames() {
+ String[] certNames = new String[certs.length];
+ for (int i = 0; i < certs.length; i++) {
+ Cert c = certs[i];
+ String name = "";
+ name = Cert.getNameValue(c.getSubjectName(), "CN=");
+ if (name.equals("")) {
+ name = "Issuer (" + i + ")";
+ }
+
+ certNames[i] = name;
+ }
+
+ return certNames;
+ }
+
+ public String getCertRootName() {
+ return certs[certs.length - 1].getIssuerName();
+ }
+
+ public boolean getIsCertSelfSigned() {
+ return getCertSubjectName().equals(getCertRootName());
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/scantask/ScanTask.java b/src/org/bitbatzen/tlsserverscanner/scantask/ScanTask.java
new file mode 100644
index 0000000..9318f33
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/scantask/ScanTask.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.scantask;
+
+import java.awt.Color;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+import org.bitbatzen.tlsserverscanner.Util;
+import org.bitbatzen.tlsserverscanner.scantask.ScanData.ScanError;
+
+
+public class ScanTask implements Runnable {
+
+ public static final String LOG_SCAN_STOPPED_BY_USER = "Scan was stopped by user!";
+
+ public enum State {
+ NONE,
+ WAIT,
+ RUNNING,
+ ERROR,
+ STOPPED,
+ DONE
+ }
+
+ private ScanTaskHandler scanTaskHandler;
+
+ private static final int PORT_REACHABLE_CHECK_TIMEOUT = 8000;
+
+ private static final int SSL_HANDSHAKE_TIMEOUT = 8000;
+ private Timer timeoutTimer;
+
+ private float progress;
+
+ private Thread thread;
+
+ private List listeners;
+
+ private ScanData sd;
+
+ private static final String ERROR_LOG_TAG = "Error:";
+
+ private SSLParameters sslParams;
+
+
+ public ScanTask(String host, int port) {
+ sd = new ScanData(host, port);
+
+ scanTaskHandler = null;
+
+ progress = 0.0f;
+
+ timeoutTimer = null;
+
+ listeners = new ArrayList<>();
+
+ sslParams = new SSLParameters();
+ }
+
+ public void addListener(IScanTaskListener listener) {
+ listeners.add(listener);
+ }
+
+ public void startThread() {
+ sd.scanStarted = true;
+ sd.scanFinished = false;
+ progress = 0.0f;
+
+ thread = new Thread(this);
+ thread.start();
+ }
+
+ public void stopThread() {
+ if (sd.scanFinished == false) {
+ sd.scanStoppedByUser = true;
+ if (sd.scanStarted) {
+ sd.scanAborted = true;
+ finish(LOG_SCAN_STOPPED_BY_USER);
+ }
+ else {
+ stateChanged(null);
+ }
+ }
+ else {
+ stateChanged(null);
+ }
+ }
+
+ public boolean getScanFinished() {
+ return sd.scanFinished;
+ }
+
+ public boolean getScanStarted() {
+ return sd.scanStarted;
+ }
+
+ public ScanData getScanData() {
+ return sd;
+ }
+
+ public float getProgress() {
+ return progress;
+ }
+
+ public boolean getScanRunning() {
+ return sd.scanStarted && !sd.scanFinished;
+ }
+
+ public void setScanTaskHandler(ScanTaskHandler scanTaskHandler) {
+ this.scanTaskHandler = scanTaskHandler;
+ }
+
+ public ScanTaskHandler getScanTaskHandler() {
+ return scanTaskHandler;
+ }
+
+ private void abortScan(ScanError scanError) {
+ if (sd.scanAborted) {
+ return;
+ }
+
+ sd.scanAborted = true;
+ sd.scanError = scanError;
+ finish(ERROR_LOG_TAG + " " + ScanData.getErrorInfo(scanError));
+ }
+
+ @Override public void run() {
+ stateChanged("Starting Host Check");
+ sd.inetAddr = resolveHost(sd.host);
+ if (sd.isHostnameResolved() == false) {
+ abortScan(ScanError.FAILED_TO_RESOLVE_HOSTNAME);
+ return;
+ }
+ if (sd.host.equals(sd.inetAddr.getHostAddress()) == false) {
+ progress += 5.0f;
+ stateChanged("Resolved Hostname > " + sd.inetAddr.getHostAddress());
+ }
+ if (sd.scanAborted) {
+ return;
+ }
+
+ if (isPortReachable(sd.inetAddr.getHostAddress(), sd.port) == false) {
+ abortScan(ScanError.PORT_NOT_REACHABLE);
+ return;
+ }
+ progress += 5.0f;
+
+ int protocolsToTestCount = scanTaskHandler.getProtocolsToTestCount();
+ int cipherSuitesToTestCount = scanTaskHandler.getCipherSuitesToTestCount();
+
+ if (protocolsToTestCount == 0 && cipherSuitesToTestCount == 0) {
+ stateChanged("Port Reachable");
+ }
+ else {
+ stateChanged("Port Reachable, Starting Scan...");
+ }
+
+ // certificate collecting
+ if (scanTaskHandler.getCertCollectingEnabled()) {
+ startTimeoutTimer();
+ testFetchCertificate(sd.inetAddr.getHostAddress(), sd.port, scanTaskHandler.getCipherSuitsForCollectingCert());
+ if (sd.scanAborted) {
+ return;
+ }
+ }
+
+ float increment = (100.0f - progress) / (protocolsToTestCount + cipherSuitesToTestCount);
+
+ // protocol tests
+ if (protocolsToTestCount > 0) {
+ for (int i = 0; i < protocolsToTestCount; i++) {
+ startTimeoutTimer();
+ testProtocol(sd.inetAddr.getHostAddress(), sd.port, i);
+ if (sd.scanAborted) {
+ return;
+ }
+ progress += increment;
+ }
+ }
+
+ // cipher suite tests
+ if (cipherSuitesToTestCount > 0) {
+ for (int i = 0; i < cipherSuitesToTestCount; i++) {
+ startTimeoutTimer();
+ testCipherSuite(sd.inetAddr.getHostAddress(), sd.port, i);
+ if (sd.scanAborted) {
+ return;
+ }
+ progress += increment;
+ }
+ }
+
+ finish("Finished");
+ }
+
+ private void onSSLHandshakeTimeout() {
+ abortScan(ScanError.SSL_HANDSHAKE_TIMEOUT);
+ }
+
+ private void finish(String logMessage) {
+ cancelTimeoutTimer();
+ progress = 100.0f;
+ sd.scanFinished = true;
+
+ stateChanged(logMessage);
+ }
+
+ private void startTimeoutTimer() {
+ try {
+ if (timeoutTimer != null) {
+ timeoutTimer.cancel();
+ }
+
+ timeoutTimer = new Timer();
+ timeoutTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ onSSLHandshakeTimeout();
+ }
+ }, SSL_HANDSHAKE_TIMEOUT);
+ }
+ catch (Exception e) {
+ }
+ }
+
+ private void cancelTimeoutTimer() {
+ if (timeoutTimer == null) {
+ return;
+ }
+
+ try {
+ timeoutTimer.cancel();
+ }
+ catch (Exception e) {
+ }
+ }
+
+ private void setDefault(SSLParameters sslParams) {
+ sslParams.setAlgorithmConstraints(null);
+ sslParams.setCipherSuites(null);
+ sslParams.setEndpointIdentificationAlgorithm(null);
+ sslParams.setNeedClientAuth(false);
+ sslParams.setWantClientAuth(false);
+ }
+
+ private SSLSession trySSLHandshake(String hostIP, int port, SSLParameters sslParams) throws SSLHandshakeException {
+ SSLSocket socket = null;
+ try {
+ socket = (SSLSocket) scanTaskHandler.getSSLSocketFactory().createSocket(hostIP, port);
+ }
+ catch (Exception e) {
+ abortScan(ScanError.FAILED_TO_CREATE_SOCKET);
+ Util.close(socket);
+ return null;
+ }
+
+ try {
+ socket.setSSLParameters(sslParams);
+ }
+ catch (IllegalArgumentException iae) {
+ abortScan(ScanError.INVALID_SSL_PARAMETER);
+ return null;
+ }
+
+ try {
+ socket.startHandshake();
+ SSLSession sslSession = socket.getSession();
+ return sslSession;
+ }
+ catch (SSLHandshakeException sslhe) {
+ if (sslhe.getMessage().contains("ValidatorException")) {
+ abortScan(ScanError.INVALID_CERTIFICATE);
+ }
+
+ throw sslhe;
+ }
+ catch (Exception e) {
+ abortScan(ScanError.MAYBE_NO_SSL_SERVICE_RUNNING);
+ return null;
+ }
+ finally {
+ Util.close(socket);
+ }
+ }
+
+ private boolean isSSLSessionValid(SSLSession sslSession) {
+ return !sd.scanAborted && sslSession != null
+ && sslSession.getCipherSuite().equals("SSL_NULL_WITH_NULL_NULL") == false;
+ }
+
+ private void testFetchCertificate(String hostIP, int port, String[] availableCipherSuites) {
+ setDefault(sslParams);
+
+ if (availableCipherSuites != null && availableCipherSuites.length > 0) {
+ sslParams.setCipherSuites(availableCipherSuites);
+ }
+
+ try {
+ SSLSession sslSession = trySSLHandshake(hostIP, port, sslParams);
+
+ if (isSSLSessionValid(sslSession)) {
+ if (sd.getCertAvailable() == false) {
+ sd.certs = getCerts(sslSession);
+ sd.cipherSuiteChosenByServer = sslSession.getCipherSuite();
+ stateChanged("Fetched Certificates");
+ }
+ }
+ }
+ catch (SSLHandshakeException sslhe) {
+ abortScan(ScanError.FAILED_TO_FETCH_CERTIFICATE);
+ }
+ }
+
+ private void testProtocol(String hostIP, int port, int protocolIndex) {
+ setDefault(sslParams);
+
+ String protocol = scanTaskHandler.getProtocolToTest(protocolIndex);
+ sslParams.setProtocols(new String[] {protocol} );
+
+ try {
+ SSLSession sslSession = trySSLHandshake(hostIP, port, sslParams);
+
+ if (isSSLSessionValid(sslSession)) {
+ int index = scanTaskHandler.getProtocolToTestIndex(sslSession.getProtocol());
+ sd.protocolsTested.put(index, true);
+ stateChanged(sslSession.getProtocol() + " > SUPPORTED");
+ }
+ }
+ catch (SSLHandshakeException sslhe) {
+ int index = scanTaskHandler.getProtocolToTestIndex(protocol);
+ sd.protocolsTested.put(index, false);
+ stateChanged(null);
+ }
+ }
+
+ private void testCipherSuite(String hostIP, int port, int cipherSuiteIndex) {
+ setDefault(sslParams);
+
+ String cipherSuite = scanTaskHandler.getCipherSuiteToTest(cipherSuiteIndex);
+ sslParams.setCipherSuites(new String[] {cipherSuite} );
+
+ try {
+ SSLSession sslSession = trySSLHandshake(hostIP, port, sslParams);
+
+ if (isSSLSessionValid(sslSession)) {
+ int index = scanTaskHandler.getCipherSuiteToTestIndex(sslSession.getCipherSuite());
+ sd.cipherSuitesTested.put(index, true);
+ stateChanged(sslSession.getCipherSuite() + " > SUPPORTED");
+ }
+ }
+ catch (SSLHandshakeException sslhe) {
+ if (sslhe.getMessage().contains("ValidatorException") == false) {
+ sd.cipherSuitesTested.put(cipherSuiteIndex, false);
+ stateChanged(null);
+ }
+ }
+ }
+
+ private void stateChanged(String message) {
+ for (IScanTaskListener listener : listeners) {
+ String m = null;
+ if (message != null && message.equals("") == false) {
+ m = "[" + sd.host + ":" + sd.port + "] [" + message + "]";
+ }
+ listener.onScanTaskStateChanged(this, m);
+ }
+ }
+
+ private InetAddress resolveHost(String host) {
+ try {
+ InetAddress inetAddr = InetAddress.getByName(host);
+ return inetAddr;
+ }
+ catch (UnknownHostException uhe) {
+ return null;
+ }
+ }
+
+ private boolean isPortReachable(String host, int port) {
+ Socket socket = null;
+ try {
+ socket = new Socket();
+ socket.connect(new InetSocketAddress(host, port), PORT_REACHABLE_CHECK_TIMEOUT);
+ return true;
+ }
+ catch (Exception e) {
+ return false;
+ }
+ finally {
+ Util.close(socket);
+ }
+ }
+
+ private Cert[] getCerts(SSLSession sslSession) {
+ java.security.cert.Certificate[] servercerts;
+ try {
+ servercerts = sslSession.getPeerCertificates();
+ }
+ catch (SSLPeerUnverifiedException e) {
+ return null;
+ }
+
+ if (servercerts[0] instanceof java.security.cert.X509Certificate) {
+ Cert[] certs = new Cert[servercerts.length];
+ for (int i = 0; i < servercerts.length; i++) {
+ certs[i] = new Cert((java.security.cert.X509Certificate) servercerts[i]);
+ }
+
+ return certs;
+ }
+ else {
+ return null;
+ }
+ }
+
+ public static State getState(ScanTask scanTask) {
+ if (scanTask == null) {
+ return State.NONE;
+ }
+
+ if (scanTask.getScanStarted() == false) {
+ if (scanTask.getScanData().scanStoppedByUser) {
+ return State.STOPPED;
+ }
+ else {
+ return State.WAIT;
+ }
+ }
+ else {
+ if (scanTask.getScanFinished()) {
+ if (scanTask.getScanData().scanAborted) {
+ if (scanTask.getScanData().scanStoppedByUser) {
+ return State.STOPPED;
+ }
+ else {
+ return State.ERROR;
+ }
+ }
+ else {
+ return State.DONE;
+ }
+ }
+ else {
+ return State.RUNNING;
+ }
+ }
+ }
+
+ public static Color getStateColor(State state) {
+ switch (state) {
+ case NONE:
+ case WAIT:
+ return Color.WHITE;
+ case STOPPED:
+ return Color.LIGHT_GRAY;
+ case RUNNING:
+ return new Color(255, 255, 20);
+ case ERROR:
+ return new Color(230, 0, 0);
+ case DONE:
+ return new Color(50, 200, 50);
+ default:
+ return Color.WHITE;
+ }
+ }
+
+ public static String getStateTag(State state) {
+ switch (state) {
+ case NONE:
+ case WAIT:
+ return "[WAIT]";
+ case RUNNING:
+ return"[RUNNING]";
+ case ERROR:
+ return "[ERROR]";
+ case DONE:
+ return "[DONE]";
+ case STOPPED:
+ return "[STOPPED]";
+ default:
+ return "[WAIT]";
+ }
+ }
+}
diff --git a/src/org/bitbatzen/tlsserverscanner/scantask/ScanTaskHandler.java b/src/org/bitbatzen/tlsserverscanner/scantask/ScanTaskHandler.java
new file mode 100644
index 0000000..3181781
--- /dev/null
+++ b/src/org/bitbatzen/tlsserverscanner/scantask/ScanTaskHandler.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2015 Benjamin W. (bitbatzen@gmail.com)
+ *
+ * This file is part of TLSServerScanner.
+ *
+ * TLSServerScanner is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * TLSServerScanner is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TLSServerScanner. If not, see .
+ */
+
+package org.bitbatzen.tlsserverscanner.scantask;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.net.ssl.SSLSocketFactory;
+
+import org.bitbatzen.tlsserverscanner.Util;
+import org.bitbatzen.tlsserverscanner.scantask.ScanTask.State;
+
+
+public class ScanTaskHandler implements IScanTaskListener {
+
+ private int maxParallelScans = 10;
+
+ private long scanStartTime;
+
+ private List scanTasks;
+
+ private List cipherSuitesForCollectingCert;
+ private List cipherSuitesToTest;
+ private List protocolsToTest;
+
+ private boolean certAndHostnameVerificationEnabled;
+ private boolean certCollectingEnabled;
+ private SSLSocketFactory sslSocketFactory;
+
+ private SimpleDateFormat logDateFormat;
+
+ private List listeners;
+
+ private Timer finishMessageDelayTimer;
+
+ private final static int FINISH_MESSAGE_DELAY_TIME = 500;
+
+ private boolean scansStarted;
+ private boolean scansStoppedByUser;
+
+ private static final String LOG_PREFIX = "+++++++";
+
+
+ public ScanTaskHandler() {
+ scanStartTime = 0;
+ scanTasks = new ArrayList<>();
+ cipherSuitesForCollectingCert = new ArrayList<>();
+ cipherSuitesToTest = new ArrayList<>();
+ protocolsToTest = new ArrayList<>();
+
+ scansStarted = false;
+ scansStoppedByUser = false;
+
+ logDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+
+ listeners = new ArrayList<>();
+
+ sslSocketFactory = null;
+ }
+
+ public void init(boolean certCollectingEnabled, boolean certAndHostnameVerificationEnabled) {
+ this.certCollectingEnabled = certCollectingEnabled;
+ this.certAndHostnameVerificationEnabled = certAndHostnameVerificationEnabled;
+ sslSocketFactory = SSLUtil.getSSLSocketFactory(certAndHostnameVerificationEnabled);
+ }
+
+ public SSLSocketFactory getSSLSocketFactory() {
+ return sslSocketFactory;
+ }
+
+ public boolean getCertCollectingEnabled() {
+ return certCollectingEnabled;
+ }
+
+ public boolean getCertAndHostnameVerificationEnabled() {
+ return certAndHostnameVerificationEnabled;
+ }
+
+ public void addListener(IScanTaskHandlerListener listener) {
+ listeners.add(listener);
+ }
+
+ public void addScanTask(ScanTask scanTask) {
+ scanTask.addListener(this);
+ scanTask.setScanTaskHandler(this);
+ scanTasks.add(scanTask);
+ }
+
+ public void setCipherSuitesForCollectingCert(List cipherSuites) {
+ cipherSuitesForCollectingCert.clear();
+ for (String s : cipherSuites) {
+ cipherSuitesForCollectingCert.add(s);
+ }
+ }
+
+ public String[] getCipherSuitsForCollectingCert() {
+ String[] cipherSuites = new String[cipherSuitesForCollectingCert.size()];
+ for (int i = 0; i < cipherSuitesForCollectingCert.size(); i++) {
+ cipherSuites[i] = cipherSuitesForCollectingCert.get(i);
+ }
+
+ return cipherSuites;
+ }
+
+ public void setCipherSuitesToTest(List cipherSuites) {
+ cipherSuitesToTest.clear();
+ for (String s : cipherSuites) {
+ cipherSuitesToTest.add(s);
+ }
+ }
+ public String getCipherSuiteToTest(int index) {
+ return cipherSuitesToTest.get(index);
+ }
+ public int getCipherSuiteToTestIndex(String cipherSuite) {
+ return cipherSuitesToTest.indexOf(cipherSuite);
+ }
+ public int getCipherSuitesToTestCount() {
+ return cipherSuitesToTest.size();
+ }
+
+
+ public void setProtocolsToTest(List protocols) {
+ protocolsToTest.clear();
+ for (String s : protocols) {
+ protocolsToTest.add(s);
+ }
+ }
+ public String getProtocolToTest(int index) {
+ return protocolsToTest.get(index);
+ }
+ public int getProtocolToTestIndex(String protocol) {
+ return protocolsToTest.indexOf(protocol);
+ }
+ public int getProtocolsToTestCount() {
+ return protocolsToTest.size();
+ }
+
+ public List getScanTasks() {
+ return scanTasks;
+ }
+
+ public void startScans() {
+ if (sslSocketFactory == null) {
+ log(null, "InternalError: sslSocketFactory == null");
+ return;
+ }
+
+ scansStarted = true;
+ scansStoppedByUser = false;
+ scanStartTime = System.currentTimeMillis();
+
+ String hostString = (getScanTasksCount() == 1) ? "host" : "hosts";
+ log(null, LOG_PREFIX + " Starting scan for " + scanTasks.size() + " " + hostString);
+
+ checkStartScans();
+ }
+
+ public void stopScans() {
+ if (getScansFinished() == false) {
+ for (ScanTask st : scanTasks) {
+ st.stopThread();
+ }
+
+ log(null, LOG_PREFIX + " " + ScanTask.LOG_SCAN_STOPPED_BY_USER + Util.LF);
+ scansStoppedByUser = true;
+ }
+ }
+
+ public boolean getScansRunning() {
+ if (scansStarted) {
+ if (getScansFinished()) {
+ return false;
+ }
+ else {
+ return !scansStoppedByUser;
+ }
+ }
+ else {
+ return false;
+ }
+ }
+
+ public int getScansFailedCount() {
+ int counter = 0;
+ for (ScanTask st : scanTasks) {
+ if (st.getScanData().scanAborted && st.getScanData().scanStoppedByUser == false) {
+ counter++;
+ }
+ }
+
+ return counter;
+ }
+
+ public int getScansCompletedCount() {
+ int counter = 0;
+ for (ScanTask st : scanTasks) {
+ if (st.getScanFinished() && st.getScanData().scanAborted == false) {
+ counter++;
+ }
+ }
+
+ return counter;
+ }
+
+ public int getScansRunningCount() {
+ int runningScans = 0;
+ for (ScanTask st : scanTasks) {
+ if (st.getScanRunning()) {
+ runningScans++;
+ }
+ }
+
+ return runningScans;
+ }
+
+ public int getScanTasksCount() {
+ return scanTasks.size();
+ }
+
+ public ScanTask.State getState() {
+ if (scansStarted) {
+ if (getScansFinished() && !scansStoppedByUser) {
+ return State.DONE;
+ }
+ else if (scansStoppedByUser) {
+ return State.STOPPED;
+ }
+ else {
+ return State.RUNNING;
+ }
+ }
+ else {
+ return State.WAIT;
+ }
+ }
+
+ private void checkStartScans() {
+ int scansToStart = maxParallelScans - getScansRunningCount();
+ if (scansToStart <= 0) {
+ return;
+ }
+
+ for (ScanTask st : scanTasks) {
+ if (scansToStart > 0 && st.getScanStarted() == false && st.getScanFinished() == false) {
+ st.setScanTaskHandler(this);
+ st.startThread();
+ scansToStart--;
+ }
+ }
+ }
+
+ private boolean getScansFinished() {
+ for (ScanTask st : scanTasks) {
+ if (st.getScanFinished() == false) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public synchronized void onScanTaskStateChanged(ScanTask scanTask, String message) {
+ if (scansStoppedByUser) {
+ return;
+ }
+
+ if (message != null && message.equals("") == false) {
+ float progress = 0;
+ for (ScanTask st : scanTasks) {
+ progress += st.getProgress();
+ }
+
+ progress = (progress / (scanTasks.size() * 100.0f)) * 100.0f;
+ log(scanTask, getProgressString((int) progress) + message);
+ }
+
+ if (scanTask.getScanFinished() && scanTask.getScanData().scanStoppedByUser == false) {
+ checkStartScans();
+ }
+
+ if (getScansFinished()) {
+ sendAllScanTasksFinishedMessageWithDelay(FINISH_MESSAGE_DELAY_TIME);
+ }
+ }
+
+ private void sendAllScanTasksFinishedMessageWithDelay(int delayInMillis) {
+ try {
+ if (finishMessageDelayTimer != null) {
+ finishMessageDelayTimer.cancel();
+ }
+
+ finishMessageDelayTimer = new Timer();
+ finishMessageDelayTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ sendAllScanTasksFinishedMessage();
+ }
+ }, delayInMillis);
+ }
+ catch (Exception e) {
+ }
+ }
+
+ private void sendAllScanTasksFinishedMessage() {
+ if (scansStoppedByUser == false) {
+ String hostString = (getScanTasksCount() == 1) ? "host" : "hosts";
+ float duration = (System.currentTimeMillis() - scanStartTime - FINISH_MESSAGE_DELAY_TIME) / 1000.0f;
+ log(null, LOG_PREFIX + " Finished " + scanTasks.size() + " " + hostString + " in " + duration + " seconds " + Util.LF);
+ }
+ for (IScanTaskHandlerListener listener : listeners) {
+ listener.onScanTaskHandlerDone();
+ }
+ }
+
+ private String getProgressString(int progress) {
+ if (progress < 10) {
+ return "[" + (int) progress + " %] ";
+ }
+ else if (progress < 100) {
+ return "[" + (int) progress + " %] ";
+ }
+
+ return "[" + (int) progress + " %] ";
+ }
+
+ private void log(ScanTask scanTask, String message) {
+ if (message != null && message != "") {
+ for (IScanTaskHandlerListener listener : listeners) {
+ String date = logDateFormat.format(new Date());
+ String m = "[" + date + "] " + message;
+ listener.onScanTaskHandlerMessage(scanTask, m);
+ }
+ }
+ }
+}