From 5127cf168df94a582a41e828a0a59610931d7e48 Mon Sep 17 00:00:00 2001 From: Paul-Christian Volkmer Date: Fri, 3 Jan 2025 13:14:39 +0100 Subject: [PATCH] Initial commit --- Cargo.toml | 23 ++ LICENSE | 674 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 59 +++++ image.jpeg | Bin 0 -> 70187 bytes src/chain.rs | 353 +++++++++++++++++++++++++++ src/cli.rs | 54 +++++ src/main.rs | 153 ++++++++++++ 7 files changed, 1316 insertions(+) create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 image.jpeg create mode 100644 src/chain.rs create mode 100644 src/cli.rs create mode 100644 src/main.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6d9b914 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "cert-tools" +description = "Application to show and merge content of PEM files" +version = "0.1.0" +edition = "2021" +license = "GPL-3.0-or-later" +authors = ["Paul-Christian Volkmer"] + +[dependencies] +openssl = { version = "0.10"} +clap = { version = "4.5", features = ["std", "help", "usage", "derive", "error-context"], default-features = false } +console = "0.15" +itertools = "0.14" + +[target.'cfg(windows)'.dependencies] +openssl-sys = { version = "0.9", features = ["vendored"] } + +[profile.release] +opt-level = "s" +codegen-units = 1 +lto = "thin" +strip = true +panic = "abort" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + 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..253613b --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# Cert-Tools + +Application to show and merge content of PEM files + +## Usage + +This application provides two tools: `print` and `merge`. + +### Print content of PEM files + +This will print out some information about the file(s), checks if the certificate chain is valid and each certificate is +within its lifetime. + +If the argument `KEY` is present, the contained private key will be checked against the first certificates +public key. + +If the given certificate file only contains the certificate, but not the complete certificate chain, the optional +argument `--ca` can be used to print the whole chain. + +``` +Usage: cert-tools print [OPTIONS] [KEY] + +Arguments: + Datei mit Zertifikaten im PEM-Format + [KEY] Datei mit Private Key im PEM-Format (Optional) + +Options: + --ca Datei mit CA im PEM-Format (Optional) + -h, --help Print help + +``` + +![](image.jpeg) + +### Merge Cert and CA file + +This will print out a merged certificate chain. + +If the resulting PEM file contains certificates not in required order, the certificates will be sorted. +In case of certificates present in both files, the application ensures that each certificate only appears once. + +If the argument `CA` is missing, only the given certificate file will be processed (sorting, unique certificates). + +``` +Usage: cert-tools merge [CA] + +Arguments: + Datei mit Zertifikaten im PEM-Format + [CA] Datei mit CA im PEM-Format + +Options: + -h, --help Print help +``` + +**Example** + +```shell +cert-tools merge cert.pem ca.pem > chain.pem +``` \ No newline at end of file diff --git a/image.jpeg b/image.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..91bcf958dadfb9f4a71bb29cf8fccafc796115e9 GIT binary patch literal 70187 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
  • -e|L-&>fhEZ0NW+K;oMc#CYy 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 YZ!}_}Zm!RTyu`xQ6<@di0O)`J*)KCD&;S4c literal 0 HcmV?d00001 diff --git a/src/chain.rs b/src/chain.rs new file mode 100644 index 0000000..8fae9c0 --- /dev/null +++ b/src/chain.rs @@ -0,0 +1,353 @@ +/* + * This file is part of cert-tools + * + * Copyright (C) 2025 the original author or authors. + * + * 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 . + */ + +use console::style; +use openssl::asn1::Asn1Time; +use openssl::hash::MessageDigest; +use openssl::nid::Nid; +use openssl::pkey::{PKey, PKeyRef, Public}; +use openssl::x509::X509; +use std::fmt::Display; +use std::fs; +use std::hash::{Hash, Hasher}; +use std::path::Path; +use std::time::{SystemTime, UNIX_EPOCH}; + +pub fn print_cert(cert: &Certificate) { + println!( + "{} +Issuer: {} +Gültigkeit: Gültig von: {} bis: {} +SHA-1-Fingerprint: {} +SHA-256-Fingerprint: {} +Subject-Key-Id: {} +Authority-Key-Id: {}", + style(format!("Name: {}", cert.name())) + .bold() + .underlined(), + cert.issuer(), + if cert.is_valid_not_before(&SystemTime::now()) { + style(cert.not_before().to_string()) + } else { + style(cert.not_before().to_string()).red() + }, + if cert.is_valid_not_after(&SystemTime::now()) { + style(cert.not_after().to_string()) + } else { + style(cert.not_after().to_string()).red() + }, + cert.fingerprint().sha1, + cert.fingerprint().sha256, + cert.subject_key_id(), + cert.authority_key_id(), + ); + if !cert.dns_names().is_empty() { + println!( + "DNS Names: {}", + style(cert.dns_names().join(", ")) + ); + } +} + +pub fn hex_encode>(s: T) -> String { + s.as_ref() + .iter() + .map(|b| format!("{:02x}", b)) + .collect::>() + .join(":") + .to_ascii_uppercase() +} + +fn asn1time(time: &SystemTime) -> Asn1Time { + Asn1Time::from_unix( + time.duration_since(UNIX_EPOCH) + .expect("time not went backwards") + .as_secs() as i64, + ) + .unwrap() +} + +#[derive(PartialEq)] +pub enum StringValue { + Valid(String), + Invalid, + Empty, +} + +impl Display for StringValue { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + StringValue::Valid(val) => write!(f, "{}", val), + StringValue::Invalid => write!(f, "*Invalid*"), + StringValue::Empty => write!(f, "*Empty*"), + } + } +} + +impl From for StringValue { + fn from(value: String) -> Self { + if value.trim().is_empty() { + return StringValue::Empty; + } + StringValue::Valid(value) + } +} + +pub struct PrivateKey { + modulus: StringValue, +} + +impl PrivateKey { + pub fn read(path: &Path) -> Result { + let file = fs::read(path).map_err(|err| err.to_string())?; + let key = PKey::private_key_from_pem(&file).map_err(|err| err.to_string())?; + + Ok(Self { + modulus: hex_encode(key.rsa().unwrap().n().to_vec()).into(), + }) + } +} + +pub struct Fingerprint { + pub sha1: StringValue, + pub sha256: StringValue, +} + +#[derive(Clone, Eq, PartialEq)] +pub struct Certificate { + cert: X509, +} + +impl Certificate { + pub fn from_x509(x509: &X509) -> Result { + let result = Self { cert: x509.clone() }; + Ok(result) + } + + pub fn to_plain(&self) -> Result { + match self.cert.to_pem() { + Ok(pem) => String::from_utf8(pem).map_err(|_| ()), + Err(_) => Err(()), + } + } + + pub fn name(&self) -> StringValue { + match self + .cert + .subject_name() + .entries_by_nid(Nid::COMMONNAME) + .last() + { + None => StringValue::Invalid, + Some(cn) => match String::from_utf8(cn.data().as_slice().to_vec()) { + Ok(value) => StringValue::Valid(value), + _ => StringValue::Invalid, + }, + } + } + + pub fn fingerprint(&self) -> Fingerprint { + Fingerprint { + sha1: match self.cert.digest(MessageDigest::sha1()) { + Ok(value) => StringValue::Valid(hex_encode(value)), + _ => StringValue::Empty, + }, + sha256: match self.cert.digest(MessageDigest::sha256()) { + Ok(value) => StringValue::Valid(hex_encode(value)), + _ => StringValue::Empty, + }, + } + } + + pub fn issuer(&self) -> StringValue { + match self + .cert + .issuer_name() + .entries_by_nid(Nid::COMMONNAME) + .last() + { + None => StringValue::Invalid, + Some(cn) => match String::from_utf8(cn.data().as_slice().to_vec()) { + Ok(value) => StringValue::Valid(value), + _ => StringValue::Invalid, + }, + } + } + + pub fn not_before(&self) -> StringValue { + StringValue::Valid(self.cert.not_before().to_string()) + } + + pub fn is_valid_not_before(&self, time: &SystemTime) -> bool { + self.cert.not_before().lt(&asn1time(time)) + } + + pub fn not_after(&self) -> StringValue { + StringValue::Valid(self.cert.not_after().to_string()) + } + + pub fn is_valid_not_after(&self, time: &SystemTime) -> bool { + self.cert.not_after().gt(&asn1time(time)) + } + + pub fn dns_names(&self) -> Vec { + match self.cert.subject_alt_names() { + Some(names) => names + .iter() + .map(|name| name.dnsname().unwrap_or_default().to_string()) + .filter(|name| !name.trim().is_empty()) + .collect::>(), + _ => vec![], + } + } + + pub fn key_modulo(&self) -> StringValue { + match self.cert.public_key() { + Ok(key) => match key.rsa() { + Ok(rsa) => StringValue::Valid(hex_encode(rsa.n().to_vec())), + _ => StringValue::Invalid, + }, + _ => StringValue::Empty, + } + } + + pub fn subject_key_id(&self) -> StringValue { + match self.cert.subject_key_id() { + Some(id) => StringValue::Valid(hex_encode(id.as_slice())), + _ => StringValue::Empty, + } + } + + pub fn authority_key_id(&self) -> StringValue { + match self.cert.authority_key_id() { + Some(id) => StringValue::Valid(hex_encode(id.as_slice())), + _ => StringValue::Empty, + } + } + + fn public_key(&self) -> Result, ()> { + self.cert.public_key().map_err(drop) + } + + #[allow(dead_code)] + pub fn is_ca(&self) -> bool { + if let Some(text) = self.to_text() { + return text.contains("CA:TRUE"); + } + false + } + + fn verify(&self, key: &PKeyRef) -> bool { + if let Ok(value) = self.cert.verify(key) { + return value; + } + false + } + + pub fn within_timerange(&self, time: &SystemTime) -> bool { + self.is_valid_not_before(time) && self.is_valid_not_after(time) + } + + #[allow(dead_code)] + pub fn to_text(&self) -> Option { + match self.cert.to_text() { + Ok(text) => match String::from_utf8(text) { + Ok(value) => Some(value), + _ => None, + }, + _ => None, + } + } + + pub fn public_key_matches(&self, private_key: PrivateKey) -> bool { + if self.key_modulo().to_string() == private_key.modulus.to_string() { + return true; + } + false + } +} + +impl Hash for Certificate { + fn hash(&self, state: &mut H) { + self.fingerprint().sha256.to_string().hash(state); + } +} + +pub struct Chain { + certs: Vec, +} + +impl Chain { + pub fn read(path: &Path) -> Result { + let file = fs::read(path).map_err(|err| err.to_string())?; + let certs = X509::stack_from_pem(&file).map_err(|err| err.to_string())?; + + let certs = certs + .iter() + .map(|cert| Certificate::from_x509(cert).unwrap()) + .collect::>(); + + Ok(Self { certs }) + } + + pub fn from(certs: Vec) -> Self { + Self { certs } + } + + pub fn certs(&self) -> &Vec { + &self.certs + } + + pub fn to_vec(self) -> Vec { + self.certs + } + + pub fn push(&mut self, cert: Certificate) { + self.certs.push(cert); + } + + pub fn is_valid(&self) -> bool { + let mut x: Option> = None; + let mut time_issue = false; + for cert in self.certs.iter().rev() { + if !cert.within_timerange(&SystemTime::now()) { + time_issue = true; + } + if let Some(x) = &x { + if !cert.verify(x) { + return false; + } + } + x = match cert.public_key() { + Ok(public_key) => Some(public_key), + Err(_) => None, + } + } + !time_issue && !self.certs.is_empty() + } + + pub fn has_missing_tail(&self) -> bool { + match self.certs.last() { + Some(cert) => { + matches!(cert.authority_key_id(), StringValue::Valid(_)) + } + _ => false, + } + } +} diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..4e9e18f --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,54 @@ +/* + * This file is part of cert-tools + * + * Copyright (C) 2025 the original author or authors. + * + * 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 . + */ + +use clap::{Parser, Subcommand}; + +#[derive(Parser)] +#[command(author, version, about)] +#[command(arg_required_else_help(true))] +pub struct Cli { + #[command(subcommand)] + pub cmd: SubCommand, +} + +#[derive(Subcommand)] +pub enum SubCommand { + #[command( + name = "print", + about = "Gibt Übersicht zu den angegebenen Dateien aus" + )] + Print { + #[arg(help = "Datei mit Zertifikaten im PEM-Format")] + cert: String, + #[arg(help = "Datei mit Private Key im PEM-Format (Optional)")] + key: Option, + #[arg(long, help = "Datei mit CA im PEM-Format (Optional)")] + ca: Option, + }, + #[command( + name = "merge", + about = "Fügt Zertifikats- mit CA-Datei zusammen und sortiert die Zertifikate, wenn erforderlich" + )] + Merge { + #[arg(help = "Datei mit Zertifikaten im PEM-Format")] + cert: String, + #[arg(help = "Datei mit CA im PEM-Format")] + ca: Option, + }, +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..263f112 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,153 @@ +/* + * This file is part of cert-tools + * + * Copyright (C) 2025 the original author or authors. + * + * 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 . + */ + +mod chain; +mod cli; + +use crate::chain::{print_cert, Chain, PrivateKey}; +use crate::cli::{Cli, SubCommand}; +use clap::Parser; +use console::style; +use itertools::Itertools; +use std::cmp::Ordering; +use std::path::Path; + +fn main() -> Result<(), ()> { + let cli = Cli::parse(); + + match cli.cmd { + SubCommand::Print { cert, ca, key } => { + let chain = Chain::read(Path::new(&cert)); + + if let Ok(mut chain) = chain { + if let Some(ca) = ca { + if let Ok(ca_chain) = Chain::read(Path::new(&ca)) { + for ca_cert in ca_chain.to_vec() { + chain.push(ca_cert); + } + } else { + println!("{}", style(format!("Cannot read file: {}", ca)).red()); + return Err(()); + } + } + + for cert in chain.certs() { + print_cert(cert); + println!() + } + + if chain.has_missing_tail() { + println!( + "{}\n Self signed (CA-) Certificate? It might be required to import a self signed Root-CA manually for applications to use it.", + style("! Last Certificate points to another one that should be contained in chain.").yellow() + ); + } + + if chain.is_valid() { + println!("{}", style("✓ Chain is valid").green()); + } else { + println!( + "{}", + style("! Chain or some of its parts is not valid (anymore)").red() + ); + } + + if let Some(key) = key { + match PrivateKey::read(Path::new(&key)) { + Ok(private_key) => { + if let Some(cert) = chain.certs().first() { + if cert.public_key_matches(private_key) { + println!( + "{}", + style("✓ Private Key matches first Cert Public Key") + .green() + ) + } else { + println!("{}", style("! Private Key does not match the first Cert Public Key").red()) + } + } + } + _ => { + println!("{}", style("Could not read Private Key").red()) + } + } + } + } else { + println!("{}", style(format!("Cannot read file: {}", cert)).red()); + return Err(()); + } + } + SubCommand::Merge { cert, ca } => { + let chain = Chain::read(Path::new(&cert)); + + if let Ok(mut chain) = chain { + if let Some(ca) = ca { + if let Ok(ca_chain) = Chain::read(Path::new(&ca)) { + for ca_cert in ca_chain.to_vec() { + chain.push(ca_cert); + } + } else { + eprintln!("{}", style(format!("Cannot read file: {}", ca)).red()); + return Err(()); + } + } + if !chain.is_valid() { + eprintln!( + "{}", + style("Cannot merge files to valid chain - try to sort unique certs") + .yellow() + ); + } + let mut certs = chain.to_vec(); + certs.sort_by(|cert1, cert2| { + if cert1.subject_key_id() == cert2.authority_key_id() { + return Ordering::Greater; + } else { + return Ordering::Less; + } + }); + let chain = Chain::from(certs.into_iter().unique().collect::>()); + if !chain.is_valid() { + eprintln!( + "{}", + style("Cannot merge files to valid chain - giving up!").red() + ); + return Err(()); + } + for cert in chain.certs() { + match cert.to_plain() { + Ok(plain) => print!("{}", plain), + Err(_) => { + eprintln!( + "{}", + style("Cannot merge files to valid chain - Cert error!").red() + ); + return Err(()); + } + } + } + } else { + eprintln!("{}", style(format!("Cannot read file: {}", cert)).red()); + return Err(()); + } + eprintln!("{}", style("Success!").green()); + } + } + Ok(()) +}