├── INSTALL ├── LICENSE ├── README ├── jackal.1.0.c ├── jackal.1.1.c ├── jackal.1.2.c └── jackal.1.3.c /INSTALL: -------------------------------------------------------------------------------- 1 | jackal - certificate cloner - K Sheldrake 2 | 3 | Copyright (C) 2018 Kevin Sheldrake 4 | 5 | This file is part of jackal. 6 | 7 | Jackal is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU General Public License 9 | as published by the Free Software Foundation; either version 2 10 | of the License, or (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | 22 | Install 23 | ------- 24 | 25 | cc -o jackal jackal.1.2.c -lssl -lcrypto 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | jackal - certificate cloner - K Sheldrake 2 | 3 | Copyright (C) 2018 Kevin Sheldrake 4 | 5 | This file is part of jackal. 6 | 7 | Jackal is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU General Public License 9 | as published by the Free Software Foundation; either version 2 10 | of the License, or (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | 22 | Overview 23 | -------- 24 | 25 | jackal clones SSL certificates. The purpose is to automate and simplify a 26 | step in the SSL MITM process. 27 | 28 | SSL/TLS connections are established on trust provided by the certificates that 29 | are exchanged. Typically the server sends a certificate to the client for 30 | verification. All certificates are signed by another certificate. 31 | Self-signed certificates are signed by themselves; in all other cases, an end 32 | user cerficate is signed by an intermediate certificate authority, which is 33 | in-turn signed by other intermediates, the last of which is signed by a root 34 | certificate authority. The root certificate authority is self-signed and 35 | should be available to the tool or client that wants to verify the end user 36 | certificate. 37 | 38 | To verify an end user certificate a tool or client finds the certificate that 39 | signed it, and then recursively verifies that. If any of the certificates are 40 | contained in the CA store, the certificate is deemed to have a valid 41 | signature. There are other checks, such as validity dates and confirmation of 42 | the end user certificate details to ensure the right certificate is in use. 43 | 44 | Man-in-the-middle attacks are possible if the certificate verification 45 | routines are flawed or if it is possible to add a fake CA to the CA store. 46 | In order to effect such an attack, certificates that mimic the real 47 | certificates are required. This tool, jackal, makes fake certificates that 48 | are identical to the originals except for the change of keys. 49 | 50 | Supported platforms: 51 | * 32bit (Arch) linux 52 | * 64bit (Arch && Kali) linux 53 | Expecting it to work on any other platforms is very optimistic of you. 54 | 55 | 56 | Obtain certficate chain 57 | ----------------------- 58 | 59 | Obtain a certificate chain from a SSL server with something like: 60 | $ openssl s_client -connect anywhere.com:443 -showcerts < /dev/null > certs 61 | 62 | The file certs will then contain the s_client output including the certs in 63 | PEM format. Jackal can take this file and clone the certificates within it. 64 | 65 | 66 | Create a new CA 67 | --------------- 68 | 69 | Create a new CA with: 70 | $ openssl genrsa -des3 -out ca.key 2048 71 | $ openssl req -new -x509 -days 3650 -key ca.key -out ca.pem 72 | 73 | Check certificate with: 74 | $ openssl x509 -in certificate.pem -noout -text 75 | 76 | Verify certificate trust with: 77 | $ openssl verify -CAfile ca.pem -untrusted intermediatecas.pem certificate.pem 78 | 79 | 80 | Cloning options 81 | --------------- 82 | 83 | Jackal only deals with the certificates you supply; it will not go foraging 84 | for missing CA certificates. If the certificate chain you have obtained is 85 | lacking the CA certificate, then you may need to obtain it yourself from the 86 | trusted CA store and add it to the end of the file (depending on whether you 87 | also want to clone the chain's CA or not). The cloning options are: 88 | 89 | Leaf certificate (-sl): Jackal will clone the leaf certficate (the server 90 | certificate) and ignore the rest of the chain. If a CA is supplied (with key, 91 | obvs) then it will sign the new certificate with the supplied CA; otherwise, 92 | it will self-sign the cert. 93 | 94 | Chain (-sc): Jackal will clone the entire certificate chain. If a CA is 95 | supplied, it will then sign the root of the chain with the supplied CA; 96 | otherwise, it will self-sign the root of the chain. 97 | 98 | Resign (-sr): Jackal will clone the certificate chain, but will replace the 99 | root of the chain with the supplied CA. 100 | 101 | 102 | Output filename spec 103 | -------------------- 104 | 105 | Options -sl, -sc and -sr take an output filespec option, -o. 106 | 107 | Option -sl outputs one certificate and an associated key. It appends '.pem' 108 | and '.key' to the filespec. 109 | Options -sc and -sr output multiple certificates, each with associated keys. 110 | Jackal appends '.n.pem' and '.n.key' to the filespec, numbering each 111 | certificate in the new chain, starting with the leaf certificate as 0 and 112 | increasing with each certificate in the chain. 113 | 114 | 115 | Examples 116 | -------- 117 | 118 | $ jackal -sc -c certfile -o clonefile -C newca.pem -K newca.key 119 | 120 | Jackal will clone the certificate chain in certfile, signing it with newca, 121 | and output the certificates to clonefile.0.pem/key, clonefile.1.pem/key, etc. 122 | 123 | 124 | Command Line Arguments 125 | ---------------------- 126 | 127 | -c certificate 128 | 129 | Specifies the input certificate to clone. If no other options are given, 130 | jackal will simply display the leaf certificate in the file (not as nice as 131 | openssl x509 -text but hey). The certificate file must be in PEM format, but 132 | jackal will silently ignore anything it doesn't recognise so other guff in 133 | there is usually acceptable. 134 | 135 | 136 | -sl / -sc / -sr 137 | 138 | Specifies the kind of cloning to perform: leaf, chain, or resign. 139 | 140 | 141 | -o outputfilespec 142 | 143 | Specifies the prefix for the output clone certificate files. 144 | 145 | 146 | -C newCAcert / -K newCAkey 147 | 148 | Specifies the CA certificate and key to use in signing operations. 149 | 150 | 151 | Support / Patches 152 | ----------------- 153 | 154 | The latest version of the code can be found at rtfc.org.uk and github/rtfcode. 155 | 156 | No support is offered, but if you report bugs I may try to fix them. Patches 157 | are especially welcome. Feature requests are best accompanied with a working 158 | patch. I'm more likely to work on it if I have beer; see rtfc.org.uk for 159 | details. 160 | 161 | Changes 162 | ------- 163 | 164 | Version 1.3 has corrected version numbers, etc. 165 | Version 1.2 properly supports openssl 1.1.0; prev version was a half-arsed 166 | attempt at a fix/port and was mostly rubbish. This version actually compiles 167 | and appears to work. 168 | Version 1.1 supports openssl 1.1.0; someone sneakily took away ->extra_certs 169 | and that broke version 1.0. 170 | 171 | 172 | Contact details are at rtfc.org.uk. 173 | 174 | -------------------------------------------------------------------------------- /jackal.1.0.c: -------------------------------------------------------------------------------- 1 | /* jackal - certificate cloner - K Sheldrake 2 | ** 3 | ** Copyright (C) 2015 Kevin Sheldrake 4 | ** 5 | ** This file is part of jackal. 6 | ** 7 | ** Jackal is free software; you can redistribute it and/or 8 | ** modify it under the terms of the GNU General Public License 9 | ** as published by the Free Software Foundation; either version 2 10 | ** of the License, or (at your option) any later version. 11 | ** 12 | ** This program is distributed in the hope that it will be useful, 13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ** GNU General Public License for more details. 16 | ** 17 | ** You should have received a copy of the GNU General Public License 18 | ** along with this program; if not, write to the Free Software 19 | ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | ** 21 | ** 22 | ** jackal clones SSL certificates. The purpose is to automate and simplify a step in the SSL MITM process. 23 | ** 24 | ** SSL/TLS connections are established on trust provided by the certificates that are exchanged. Typically 25 | ** the server sends a certificate to the client for verification. All certificates are signed by another 26 | ** certificate. Self-signed certificates are signed by themselves; in all other cases, an end user cerficate 27 | ** is signed by an intermediate certificate authority, which is in-turn signed by other intermediates, the 28 | ** last of which is signed by a root certificate authority. The root certificate authority is self-signed 29 | ** and should be available to the tool or client that wants to verify the end user certificate. 30 | ** 31 | ** To verify an end user certificate a tool or client finds the certificate that signed it, and then recursively 32 | ** verifies that. If any of the certificates are contained in the CA store, the certificate is deemed to have 33 | ** a valid signature. There are other checks, such as validity dates and confirmation of the end user 34 | ** certificate details to ensure the right certificate is in use. 35 | ** 36 | ** Man-in-the-middle attacks are possible if the certificate verification routines are flawed or if it is 37 | ** possible to add a fake CA to the CA store. In order to effect such an attack, certificates that mimic 38 | ** the real certificates are required. This tool, jackal, makes fake certificates that are identical to 39 | ** the originals except for the change of keys. 40 | ** 41 | ** Create a new CA with: 42 | ** % openssl genrsa -des3 -out ca.key 2048 43 | ** % openssl req -new -x509 -days 3650 -key ca.key -out ca.pem 44 | ** 45 | ** Check certificate with: 46 | ** % openssl x509 -in certificate.pem -noout -text 47 | ** 48 | ** Verify certificate trust with: 49 | ** % openssl verify -CAfile ca.pem -untrusted intermediatecas.pem certificate.pem 50 | ** 51 | ** 52 | ** Compile with: 53 | ** cc -o jackal jackal.c -lssl -lcrypto 54 | ** 55 | ** History: 56 | ** 57 | ** v1.0 - 8/11/2014 58 | ** 59 | ** Uplifted to v1.0 for release. 60 | ** Tidied welcome message. 61 | ** Added quote from The Day of the Jackal. :) 62 | ** 63 | ** v0.3 - 8/11/2014 64 | ** 65 | ** Now clones whole certificate chains. 66 | ** 67 | ** v0.2 - 30/10/2014 68 | ** 69 | ** Rewrite of version 1, same functionality. 70 | ** 71 | ** v0.1 - 30/10/2014 72 | ** 73 | ** Proof of concept. 74 | ** Takes one certificate and one CA and makes a new certificate with a new key that matches the original 75 | ** and is signed by the supplied CA. 76 | ** 77 | */ 78 | 79 | #include 80 | #include 81 | #include 82 | #include 83 | #include 84 | 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | 91 | #define HEX_PER_ROW 16 92 | #define ALGO_LEN 64 93 | #define OBJSTR_LEN 1024 94 | 95 | void usage(char *msg) 96 | { 97 | printf("ONLY USE FOR TESTING (don't be a muppet)\n\n"); 98 | printf("jackal -c certificate\n"); 99 | printf(" displays certificate details\n"); 100 | printf("jackal -sl -c certificate(s) -o outputfilespec [-C newCAcert -K newCAkey]\n"); 101 | printf(" signs the leaf certificate with the CA if specified or self-signs it if not\n"); 102 | printf("jackal -sc -c certificates -o outputfilespec [-C newCAcert -K newCAkey]\n"); 103 | printf(" signs the certificate chain with the CA if specified or self-signs the root if not\n"); 104 | printf("jackal -sr -c certificates -o outputfilesepc -C newCAcert -K newCAkey\n"); 105 | printf(" signs the certificate chain, replacing the current root with the specified CA\n"); 106 | printf("\n"); 107 | printf("outputfilespec will be appended with .pem/.key with -sl and .n.pem/.key with -sc/r\n"); 108 | printf("\n"); 109 | 110 | if (msg) { 111 | printf("%s\n", msg); 112 | exit(1); 113 | } 114 | 115 | exit(0); 116 | } 117 | 118 | 119 | void hexdump(unsigned char *data, int data_len, int indent) 120 | { 121 | int i, j; 122 | 123 | for (i=0; isignature, NULL); 172 | hash = OPENSSL_malloc(hashlength); 173 | 174 | if ((!algorithm) || (!pubalgo) || (!hash)) { 175 | printf("print_cert malloc error\n"); 176 | exit(1); 177 | } 178 | 179 | if (!i2t_ASN1_OBJECT(algorithm, ALGO_LEN, cert->sig_alg->algorithm)) { 180 | printf("print_cert i2t_ASN1_OBJECT failed\n"); 181 | exit(1); 182 | } 183 | 184 | if (!i2t_ASN1_OBJECT(pubalgo, ALGO_LEN, cert->cert_info->key->algor->algorithm)) { 185 | printf("print_cert i2t_ASN1_OBJECT failed\n"); 186 | exit(1); 187 | } 188 | 189 | certkey = X509_get_pubkey(cert); 190 | modulus = BN_bn2hex(certkey->pkey.rsa->n); 191 | 192 | temp = hash; 193 | if (!i2c_ASN1_BIT_STRING(cert->signature, &temp)) { 194 | printf("print_cert i2c_ASN1_BIT_STRING failed\n"); 195 | exit(1); 196 | } 197 | 198 | temp = serialnumber; 199 | if (!i2c_ASN1_INTEGER(X509_get_serialNumber(cert), &temp)) { 200 | printf("print_cert i2c_ASN1_INTEGER failed\n"); 201 | exit(1); 202 | } 203 | 204 | issuer = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); 205 | subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); 206 | 207 | time = X509_get_notBefore(cert); 208 | strncpy(notBefore, (const char *)time->data, ALGO_LEN); 209 | notBefore[ALGO_LEN - 1] = 0x00; 210 | time = X509_get_notAfter(cert); 211 | strncpy(notAfter, (const char *)time->data, ALGO_LEN); 212 | notAfter[ALGO_LEN - 1] = 0x00; 213 | 214 | printf("Certificate:\n"); 215 | printf(" Data:\n"); 216 | printf(" Version: 0x%0lx\n", X509_get_version(cert)); 217 | printf(" Serial Number:\n"); 218 | hexdump(serialnumber, seriallength, 3); 219 | printf(" Signature Algorithm: %s\n", algorithm); 220 | printf(" Issuer: %s\n", issuer); 221 | printf(" Validity\n"); 222 | printf(" Not Before: %s\n", notBefore); 223 | printf(" Not After : %s\n", notAfter); 224 | printf(" Subject: %s\n", subject); 225 | printf(" Subject Public Key Info:\n"); 226 | printf(" Public Key Algorithm: %s\n", pubalgo); 227 | printf(" Public-Key: (%d bit)\n", EVP_PKEY_bits(certkey)); 228 | printf(" Modulus:%s\n", modulus); 229 | printf(" Signature Algorithm: %s\n", algorithm); 230 | hexdump(hash, hashlength, 2); 231 | 232 | } 233 | 234 | 235 | X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x) 236 | { 237 | int i; 238 | X509 *issuer; 239 | 240 | if ((!ctx) || (!sk) || (!x)) { 241 | printf("find_issuer invalid params\n"); 242 | exit(1); 243 | } 244 | 245 | for (i=0; i < sk_X509_num(sk); i++) { 246 | issuer = sk_X509_value(sk, i); 247 | if (ctx->check_issued(ctx, x, issuer)) 248 | return issuer; 249 | } 250 | return NULL; 251 | } 252 | 253 | 254 | void get_cert_from_file(SSL_CTX **sslctx, SSL **ssl, X509 **crt, char *certfile) 255 | { 256 | if ((!sslctx) || (!ssl) || (!crt) || (!certfile)) { 257 | printf("get_cert_from_file invalid params\n"); 258 | exit(1); 259 | } 260 | 261 | /* open certificate file */ 262 | *sslctx = SSL_CTX_new(TLSv1_server_method()); 263 | if (!*sslctx) { 264 | printf("get_cert_from_file SSL_CTX_new failed\n"); 265 | exit(1); 266 | } 267 | if (!SSL_CTX_use_certificate_chain_file(*sslctx, certfile)) { 268 | printf("get_cert_from_file SSL_CTX_use_chain_certificate_file failed\n"); 269 | exit(1); 270 | } 271 | 272 | *ssl = SSL_new(*sslctx); 273 | if (!*ssl) { 274 | printf("get_cert_from_file SSL_new failed\n"); 275 | exit(1); 276 | } 277 | *crt = SSL_get_certificate(*ssl); 278 | if (!*crt) { 279 | printf("get_cert_from_file SSL_get_certifcate failed\n"); 280 | exit(1); 281 | } 282 | } 283 | 284 | STACK_OF(X509) *get_extra_certs(SSL_CTX *sslctx) 285 | { 286 | STACK_OF(X509) *ca_stack; 287 | 288 | if (!sslctx) { 289 | printf("get_extra_certs invalid params\n"); 290 | exit(1); 291 | } 292 | 293 | ca_stack = sslctx->extra_certs; 294 | if (!ca_stack) { 295 | printf("Cert file contains 1 certificate\n"); 296 | } else { 297 | printf("Cert file contains >1 certificates\n"); 298 | } 299 | 300 | return ca_stack; 301 | } 302 | 303 | 304 | void get_key_from_file(SSL *cassl, char *cakeyfile) 305 | { 306 | if ((!cassl) || (!cakeyfile)) { 307 | printf("get_key_from_file invalid params\n"); 308 | exit(1); 309 | } 310 | 311 | printf("Loading CA key\n"); 312 | 313 | /* read CA key file */ 314 | if (!SSL_use_PrivateKey_file(cassl, cakeyfile, SSL_FILETYPE_PEM)) { 315 | printf("get_key_from_file SSL_use_PrivateKey_file failed\n"); 316 | exit(1); 317 | } 318 | } 319 | 320 | 321 | int get_signing_algo(X509 *crt) 322 | { 323 | int nid; 324 | 325 | if (!crt) { 326 | printf("get_signing_algo invalid params\n"); 327 | exit(1); 328 | } 329 | 330 | /* get signing algorithm */ 331 | nid = OBJ_obj2nid(crt->sig_alg->algorithm); 332 | if (!nid) { 333 | printf("get_signing_algo OBJ_obj2nid failed\n"); 334 | exit(1); 335 | } 336 | 337 | return nid; 338 | } 339 | 340 | 341 | char *get_public_algo(X509 *crt) 342 | { 343 | char *pubalgo = OPENSSL_malloc(ALGO_LEN); 344 | 345 | if (!crt) { 346 | printf("get_public_algo invalid params\n"); 347 | exit(1); 348 | } 349 | 350 | if (!pubalgo) { 351 | printf("get_public_algo malloc error\n"); 352 | exit(1); 353 | } 354 | 355 | if (!i2t_ASN1_OBJECT(pubalgo, ALGO_LEN, crt->cert_info->key->algor->algorithm)) { 356 | printf("i2d_ASN1_OBJECT failed\n"); 357 | exit(1); 358 | } 359 | 360 | return pubalgo; 361 | } 362 | 363 | 364 | int get_pub_key_size(X509 *crt) 365 | { 366 | EVP_PKEY *certkey; 367 | 368 | if (!crt) { 369 | printf("get_pub_key_size invalid params\n"); 370 | exit(1); 371 | } 372 | 373 | certkey = X509_get_pubkey(crt); 374 | 375 | if (!certkey) { 376 | printf("get_pub_key_size X509_get_pubkey failed\n"); 377 | exit(1); 378 | } 379 | 380 | return EVP_PKEY_bits(certkey); 381 | } 382 | 383 | 384 | EVP_PKEY *gen_new_key(int certkeysize) 385 | { 386 | RSA *newrsa; 387 | EVP_PKEY *newkey; 388 | 389 | if (certkeysize <= 0) { 390 | printf("gen_new_key invalid params\n"); 391 | exit(1); 392 | } 393 | 394 | newrsa = RSA_generate_key(certkeysize, 65537, 0, 0); 395 | if (!newrsa) { 396 | printf("gen_new_key RSA_generate_key failed\n"); 397 | exit(1); 398 | } 399 | newkey = EVP_PKEY_new(); 400 | if (!newkey) { 401 | printf("gen_new_key EVP_PKEY_new failed\n"); 402 | exit(1); 403 | } 404 | 405 | if (!EVP_PKEY_assign_RSA(newkey, newrsa)) { 406 | printf("gen_new_key EVP_PKEY_assign_RSA failed\n"); 407 | exit(1); 408 | } 409 | 410 | return newkey; 411 | } 412 | 413 | 414 | void assign_key_to_cert(X509 *crt, EVP_PKEY *newkey) 415 | { 416 | if ((!crt) || (!newkey)) { 417 | printf("assign_key_to_cert invalid params\n"); 418 | exit(1); 419 | } 420 | 421 | if (!X509_set_pubkey(crt, newkey)) { 422 | printf("assign_key_to_cert X509_set_pubkey failed\n"); 423 | exit(1); 424 | } 425 | } 426 | 427 | 428 | void copy_subject_to_issuer(X509 *cacrt, X509 *crt) 429 | { 430 | X509_NAME *caname; 431 | 432 | if ((!cacrt) || (!crt)) { 433 | printf("copy_subject_to_issuer invalid params\n"); 434 | exit(1); 435 | } 436 | 437 | caname = X509_get_subject_name(cacrt); 438 | if (!caname) { 439 | printf("copy_subject_to_issuer X509_get_subject_name failed\n"); 440 | exit(1); 441 | } 442 | 443 | printf("ca subject is '%s'\n", X509_NAME_oneline(caname, NULL, 0)); 444 | 445 | if (!X509_set_issuer_name(crt, caname)) { 446 | printf("copy_subject_to_issuer X509_set_issuer_name failed\n"); 447 | exit(1); 448 | } 449 | } 450 | 451 | 452 | int ext_exists(X509 *crt, int nid) 453 | { 454 | int num_exts; 455 | STACK_OF(X509_EXTENSION) *exts; 456 | X509_EXTENSION *ex; 457 | ASN1_OBJECT *obj; 458 | int i; 459 | 460 | if ((!crt) || (!nid)) { 461 | printf("ext_exists invalid params\n"); 462 | exit(1); 463 | } 464 | 465 | exts = crt->cert_info->extensions; 466 | if (exts) { 467 | num_exts = sk_X509_EXTENSION_num(exts); 468 | } else { 469 | num_exts = 0; 470 | } 471 | 472 | for (i=0; ilength < 1) { 780 | printf("set_CA_flags key usage length < 1\n"); 781 | exit(1); 782 | } 783 | usage_val = usage->data[0]; 784 | if (usage->length > 1) { 785 | usage_val = usage_val | (usage->data[1] << 8); 786 | } 787 | 788 | if (!(usage_val & X509v3_KU_KEY_CERT_SIGN)) { 789 | /* add key cert sign flag */ 790 | newusage = ASN1_BIT_STRING_new(); 791 | if (!newusage) { 792 | printf("set_CA_flags ASN1_BIT_STRING_new failed\n"); 793 | exit(1); 794 | } 795 | usage_val = usage_val | X509v3_KU_KEY_CERT_SIGN; 796 | newusage_data[0] = (unsigned char) (usage_val & 0xff); 797 | newusage_data[1] = (unsigned char) ((usage_val >> 8) & 0xff); 798 | delete_extension(crt, NID_key_usage); 799 | if (!ASN1_BIT_STRING_set(newusage, newusage_data, 2)) { 800 | printf("set_CA_flags ASN1_BIT_STRING_set failed\n"); 801 | exit(1); 802 | } 803 | add_extension_raw(crt, NID_key_usage, newusage, critical); 804 | } 805 | } else { 806 | /* add a key usage extension */ 807 | newusage = ASN1_BIT_STRING_new(); 808 | if (!newusage) { 809 | printf("set_CA_flags ASN1_BIT_STRING_new failed\n"); 810 | exit(1); 811 | } 812 | usage_val = X509v3_KU_KEY_CERT_SIGN; 813 | newusage_data[0] = (unsigned char) (usage_val & 0xff); 814 | newusage_data[1] = (unsigned char) ((usage_val >> 8) & 0xff); 815 | if (!ASN1_BIT_STRING_set(newusage, newusage_data, 2)) { 816 | printf("set_CA_flags ASN1_BIT_STRING_set failed\n"); 817 | exit(1); 818 | } 819 | add_extension_raw(crt, NID_key_usage, newusage, critical); 820 | } 821 | 822 | break; 823 | default: 824 | printf("Invalid certificate version: %ld\n", ver); 825 | exit(1); 826 | } 827 | } 828 | 829 | 830 | /* clone_certificate */ 831 | EVP_PKEY *clone_certificate(X509 *crt, X509 *issuercrt, EVP_PKEY *issuercertkey, char *outputfile, int depth) 832 | { 833 | char outputcert[256]; 834 | char outputkey[256]; 835 | 836 | EVP_PKEY *newkey = NULL; 837 | int certkeysize = 0; 838 | X509V3_CTX extctx; 839 | int algorithm = 0; 840 | int issuerversion = 0; 841 | 842 | FILE *pemfp; 843 | FILE *keyfp; 844 | 845 | if ((!crt) || (!outputfile) || (depth < 0)) { 846 | printf("clone_certificate invalid params\n"); 847 | exit(1); 848 | } 849 | 850 | /* get issuer certificate version */ 851 | if (issuercrt) { 852 | issuerversion = X509_get_version(issuercrt); 853 | } else { 854 | issuerversion = X509_get_version(crt); 855 | } 856 | 857 | printf("\n### %s ### v%ld\n", X509_NAME_oneline(X509_get_subject_name(crt), NULL, 0), X509_get_version(crt) + 1); 858 | 859 | /* get signing algorithm */ 860 | algorithm = get_signing_algo(crt); 861 | 862 | printf("sig hash type = %d\n", algorithm); 863 | 864 | /* get public key size */ 865 | certkeysize = get_pub_key_size(crt); 866 | 867 | printf("public key size = %d\n", certkeysize); 868 | 869 | /* generate new key */ 870 | newkey = gen_new_key(certkeysize); 871 | 872 | /* assign key to certificate */ 873 | assign_key_to_cert(crt, newkey); 874 | 875 | /* copy the subject name of the issuer to the issuer name of the cert */ 876 | if (issuercrt) { 877 | copy_subject_to_issuer(issuercrt, crt); 878 | } else { 879 | copy_subject_to_issuer(crt, crt); 880 | } 881 | 882 | /* create an extension context linking the CA cert to the subject cert */ 883 | if (issuercrt) { 884 | create_ext_ctx(issuercrt, crt, &extctx); 885 | } else { 886 | create_ext_ctx(crt, crt, &extctx); 887 | } 888 | 889 | /* correct the subject key identifier if it exists */ 890 | if (ext_exists(crt, NID_subject_key_identifier)) { 891 | delete_extension(crt, NID_subject_key_identifier); 892 | add_extension(crt, &extctx, NID_subject_key_identifier, "hash"); 893 | printf("subject key identifier changed\n"); 894 | } 895 | 896 | if (issuerversion == 2) { 897 | 898 | /* correct the authority key identifier if it exists */ 899 | if (ext_exists(crt, NID_authority_key_identifier)) { 900 | delete_extension(crt, NID_authority_key_identifier); 901 | add_extension(crt, &extctx, NID_authority_key_identifier, "keyid:always"); 902 | printf("authority key identifier changed\n"); 903 | } 904 | } 905 | 906 | /* set CA flags for self-signed certs (no issuer) and where depth > 1 (intermediate or CA) */ 907 | if ((!issuercrt) || (depth)) { 908 | set_CA_flags(crt, &extctx); 909 | } 910 | 911 | /* sign the cert */ 912 | if (issuercrt) { 913 | sign_cert(crt, issuercertkey, algorithm); 914 | } else { 915 | sign_cert(crt, newkey, algorithm); 916 | } 917 | 918 | /* create output file names */ 919 | snprintf(outputcert, 256, "%s.pem", outputfile); 920 | snprintf(outputkey, 256, "%s.key", outputfile); 921 | 922 | /* open output files */ 923 | pemfp = open_output_file(outputcert); 924 | keyfp = open_output_file(outputkey); 925 | 926 | /* write the cert */ 927 | write_cert(crt, pemfp); 928 | 929 | /* write the new private key */ 930 | write_key(newkey, keyfp); 931 | 932 | /* close the output files */ 933 | fclose(pemfp); 934 | fclose(keyfp); 935 | 936 | return newkey; 937 | } 938 | 939 | 940 | /* clone_chain */ 941 | EVP_PKEY *clone_chain(X509 *crt, STACK_OF(X509) *ca_stack, X509 *cacrt, EVP_PKEY *cakey, char *outputfile, int depth, char command) 942 | { 943 | EVP_PKEY *key = NULL; 944 | X509_STORE_CTX *storectx = NULL; 945 | X509 *issuer = NULL; 946 | 947 | if ((!crt) || (!ca_stack) || (!outputfile)) { 948 | printf("clone_chain invalid params\n"); 949 | exit(1); 950 | } 951 | 952 | /* create context for chain */ 953 | storectx = X509_STORE_CTX_new(); 954 | if (!storectx) { 955 | printf("clone_chain X509_STORE_CTX_new failed\n"); 956 | exit(1); 957 | } 958 | 959 | if (!X509_STORE_CTX_init(storectx, NULL, crt, ca_stack)) { 960 | printf("clone_chain X509_STORE_init failed\n"); 961 | exit(1); 962 | } 963 | 964 | /* retrieve issuer key */ 965 | issuer = find_issuer(storectx, ca_stack, crt); 966 | 967 | if ((issuer) && (X509_name_cmp(X509_get_subject_name(crt), X509_get_issuer_name(crt)))) { 968 | /* not at end of chain */ 969 | 970 | /* clone rest of chain */ 971 | key = clone_chain(issuer, ca_stack, cacrt, cakey, outputfile, depth + 1, command); 972 | 973 | /* fix up output file name */ 974 | outputfile[strlen(outputfile) - 1] = depth + '0'; 975 | 976 | /* sign this cert and return the key */ 977 | if (key != cakey) { 978 | key = clone_certificate(crt, issuer, key, outputfile, depth); 979 | } else { 980 | key = clone_certificate(crt, cacrt, cakey, outputfile, depth); 981 | } 982 | 983 | } else { 984 | /* end of chain */ 985 | 986 | /* fix up output file name */ 987 | outputfile[strlen(outputfile) - 1] = depth + '0'; 988 | 989 | if (command == 'c') { 990 | /* sign this cert and return the key */ 991 | key = clone_certificate(crt, cacrt, cakey, outputfile, depth); 992 | } else if (command == 'r') { 993 | /* return the ca key */ 994 | key = cakey; 995 | } else { 996 | printf("clone_chain invalid command\n"); 997 | exit(1); 998 | } 999 | } 1000 | 1001 | return key; 1002 | } 1003 | 1004 | 1005 | 1006 | 1007 | int main(int argc, char *argv[]) 1008 | { 1009 | char c; 1010 | 1011 | char *certfile = NULL; 1012 | char *cacertfile = NULL; 1013 | char *cakeyfile = NULL; 1014 | char *outputfile = NULL; 1015 | char *outputchainfile = NULL; 1016 | 1017 | SSL_CTX *sslctx = NULL; 1018 | SSL *ssl = NULL; 1019 | X509 *crt = NULL; 1020 | STACK_OF(X509) *ca_stack = NULL; 1021 | X509 *tempcert = NULL; 1022 | SSL_CTX *casslctx = NULL; 1023 | SSL *cassl = NULL; 1024 | X509 *cacrt = NULL; 1025 | X509_STORE_CTX *storectx = NULL; 1026 | EVP_PKEY *issuerkey = NULL; 1027 | 1028 | char command = ' '; 1029 | int haveCA = 0; 1030 | 1031 | printf("Jackal v1.0 - Certificate Cloner - K Sheldrake\n"); 1032 | printf("----------------------------------------------\n\n"); 1033 | printf("Jackal comes with ABSOLUTELY NO WARRANTY.\n"); 1034 | printf("This is free software, and you are welcome\n"); 1035 | printf("to redistribute it under certain conditions.\n"); 1036 | printf("Jackal is provided under GPLv2.\n"); 1037 | printf("See http://www.gnu.org/licenses/gpl-2.0.html\n"); 1038 | printf("for details.\n\n"); 1039 | printf("\"Certainly, the Jackal masqueraded as an Englishman,\n"); 1040 | printf("but he also masqueraded as a Dane and as a Frenchman.\n"); 1041 | printf("So there's no way of proving his identity at all.\"\n"); 1042 | printf(" - The Day of the Jackal\n\n\n"); 1043 | 1044 | /* sort options */ 1045 | opterr = 0; 1046 | 1047 | while((c = getopt(argc, argv, "hc:C:K:o:s:")) != -1) { 1048 | switch(c) { 1049 | case 'h': 1050 | usage(NULL); 1051 | break; 1052 | case 'c': 1053 | if (strlen(optarg) > 0) { 1054 | certfile = optarg; 1055 | } 1056 | break; 1057 | case 'C': 1058 | if (strlen(optarg) > 0) { 1059 | cacertfile = optarg; 1060 | } 1061 | break; 1062 | case 'K': 1063 | if (strlen(optarg) > 0) { 1064 | cakeyfile = optarg; 1065 | } 1066 | break; 1067 | case 'o': 1068 | if (strlen(optarg) > 0) { 1069 | outputfile = optarg; 1070 | } 1071 | break; 1072 | case 's': 1073 | if (strlen(optarg) == 1) { 1074 | command = *optarg; 1075 | } 1076 | break; 1077 | default: 1078 | usage("unknown option"); 1079 | } 1080 | } 1081 | 1082 | if (!certfile) { 1083 | usage(NULL); 1084 | } 1085 | 1086 | haveCA = 0; 1087 | if ((cacertfile) && (cakeyfile)) { 1088 | haveCA = 1; 1089 | } else if ((cacertfile) || (cakeyfile)) { 1090 | usage("Specify either both -C and -K, or specify neither"); 1091 | } 1092 | 1093 | if ((command == ' ') && ((haveCA) || (outputfile))) { 1094 | usage("You need to specify a command wtih -s"); 1095 | } 1096 | 1097 | if (! strchr(" lcr", command)) { 1098 | usage("Command must be -sl, -sc, -sr or unspecified to print certificate details"); 1099 | } 1100 | 1101 | if ((strchr("lcr", command)) && (!outputfile)) { 1102 | usage("You must specify an output file specification"); 1103 | } 1104 | 1105 | if ((command == 'r') && (!haveCA)) { 1106 | usage("You need to specify a CA with -sr\n"); 1107 | } 1108 | 1109 | /* set up openssl */ 1110 | SSL_load_error_strings(); 1111 | ERR_load_BIO_strings(); 1112 | SSL_library_init(); 1113 | 1114 | /* open certificate file */ 1115 | get_cert_from_file(&sslctx, &ssl, &crt, certfile); 1116 | 1117 | if (haveCA) { 1118 | /* open CA certificate file */ 1119 | get_cert_from_file(&casslctx, &cassl, &cacrt, cacertfile); 1120 | 1121 | /* read CA key file */ 1122 | get_key_from_file(cassl, cakeyfile); 1123 | 1124 | issuerkey = get_private_key(cassl); 1125 | } 1126 | 1127 | /* get any extra certs */ 1128 | ca_stack = get_extra_certs(sslctx); 1129 | 1130 | /* if only a certificate file, display and exit */ 1131 | if (command == ' ') { 1132 | print_cert(crt); 1133 | 1134 | if (ca_stack) { 1135 | storectx = X509_STORE_CTX_new(); 1136 | if (!storectx) { 1137 | printf("main X509_STORE_CTX_new failed\n"); 1138 | exit(1); 1139 | } 1140 | 1141 | if (!X509_STORE_CTX_init(storectx, NULL, crt, ca_stack)) { 1142 | printf("main X509_STORE_init failed\n"); 1143 | exit(1); 1144 | } 1145 | 1146 | tempcert = crt; 1147 | 1148 | while (tempcert) { 1149 | printf("%s\n", X509_NAME_oneline(X509_get_subject_name(tempcert), NULL, 0)); 1150 | if (!X509_name_cmp(X509_get_subject_name(tempcert), X509_get_issuer_name(tempcert))) { 1151 | tempcert = NULL; 1152 | } else { 1153 | tempcert = find_issuer(storectx, ca_stack, tempcert); 1154 | } 1155 | } 1156 | 1157 | } 1158 | 1159 | exit(0); 1160 | } 1161 | 1162 | switch(command) { 1163 | case 'l': 1164 | if (haveCA) { 1165 | clone_certificate(crt, cacrt, issuerkey, outputfile, 0); 1166 | } else { 1167 | clone_certificate(crt, NULL, NULL, outputfile, 0); 1168 | } 1169 | break; 1170 | case 'c': 1171 | case 'r': 1172 | outputchainfile = malloc(strlen(outputfile) + 3); 1173 | if (!outputchainfile) { 1174 | printf("main malloc error\n"); 1175 | exit(1); 1176 | } 1177 | sprintf(outputchainfile, "%s.1", outputfile); 1178 | if (haveCA) { 1179 | if (!clone_chain(crt, ca_stack, cacrt, issuerkey, outputchainfile, 0, command)) { 1180 | printf("main clone_chain failed\n"); 1181 | exit(1); 1182 | } 1183 | } else { 1184 | if (!clone_chain(crt, ca_stack, NULL, NULL, outputchainfile, 0, command)) { 1185 | printf("main clone_chain failed\n"); 1186 | exit(1); 1187 | } 1188 | } 1189 | break; 1190 | 1191 | } 1192 | 1193 | 1194 | printf("\nSuccess!\n\n"); 1195 | 1196 | return 0; 1197 | 1198 | } 1199 | 1200 | 1201 | -------------------------------------------------------------------------------- /jackal.1.1.c: -------------------------------------------------------------------------------- 1 | /* jackal - certificate cloner - K Sheldrake 2 | ** 3 | ** Copyright (C) 2015 Kevin Sheldrake 4 | ** 5 | ** This file is part of jackal. 6 | ** 7 | ** Jackal is free software; you can redistribute it and/or 8 | ** modify it under the terms of the GNU General Public License 9 | ** as published by the Free Software Foundation; either version 2 10 | ** of the License, or (at your option) any later version. 11 | ** 12 | ** This program is distributed in the hope that it will be useful, 13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ** GNU General Public License for more details. 16 | ** 17 | ** You should have received a copy of the GNU General Public License 18 | ** along with this program; if not, write to the Free Software 19 | ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | ** 21 | ** 22 | ** jackal clones SSL certificates. The purpose is to automate and simplify a step in the SSL MITM process. 23 | ** 24 | ** SSL/TLS connections are established on trust provided by the certificates that are exchanged. Typically 25 | ** the server sends a certificate to the client for verification. All certificates are signed by another 26 | ** certificate. Self-signed certificates are signed by themselves; in all other cases, an end user cerficate 27 | ** is signed by an intermediate certificate authority, which is in-turn signed by other intermediates, the 28 | ** last of which is signed by a root certificate authority. The root certificate authority is self-signed 29 | ** and should be available to the tool or client that wants to verify the end user certificate. 30 | ** 31 | ** To verify an end user certificate a tool or client finds the certificate that signed it, and then recursively 32 | ** verifies that. If any of the certificates are contained in the CA store, the certificate is deemed to have 33 | ** a valid signature. There are other checks, such as validity dates and confirmation of the end user 34 | ** certificate details to ensure the right certificate is in use. 35 | ** 36 | ** Man-in-the-middle attacks are possible if the certificate verification routines are flawed or if it is 37 | ** possible to add a fake CA to the CA store. In order to effect such an attack, certificates that mimic 38 | ** the real certificates are required. This tool, jackal, makes fake certificates that are identical to 39 | ** the originals except for the change of keys. 40 | ** 41 | ** Create a new CA with: 42 | ** % openssl genrsa -des3 -out ca.key 2048 43 | ** % openssl req -new -x509 -days 3650 -key ca.key -out ca.pem 44 | ** 45 | ** Check certificate with: 46 | ** % openssl x509 -in certificate.pem -noout -text 47 | ** 48 | ** Verify certificate trust with: 49 | ** % openssl verify -CAfile ca.pem -untrusted intermediatecas.pem certificate.pem 50 | ** 51 | ** 52 | ** Compile with: 53 | ** cc -o jackal jackal.c -lssl -lcrypto 54 | ** 55 | ** History: 56 | ** 57 | ** v1.0 - 8/11/2014 58 | ** 59 | ** Uplifted to v1.0 for release. 60 | ** Tidied welcome message. 61 | ** Added quote from The Day of the Jackal. :) 62 | ** 63 | ** v0.3 - 8/11/2014 64 | ** 65 | ** Now clones whole certificate chains. 66 | ** 67 | ** v0.2 - 30/10/2014 68 | ** 69 | ** Rewrite of version 1, same functionality. 70 | ** 71 | ** v0.1 - 30/10/2014 72 | ** 73 | ** Proof of concept. 74 | ** Takes one certificate and one CA and makes a new certificate with a new key that matches the original 75 | ** and is signed by the supplied CA. 76 | ** 77 | */ 78 | 79 | #include 80 | #include 81 | #include 82 | #include 83 | #include 84 | 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | 91 | #define HEX_PER_ROW 16 92 | #define ALGO_LEN 64 93 | #define OBJSTR_LEN 1024 94 | 95 | void usage(char *msg) 96 | { 97 | printf("ONLY USE FOR TESTING (don't be a muppet)\n\n"); 98 | printf("jackal -c certificate\n"); 99 | printf(" displays certificate details\n"); 100 | printf("jackal -sl -c certificate(s) -o outputfilespec [-C newCAcert -K newCAkey]\n"); 101 | printf(" signs the leaf certificate with the CA if specified or self-signs it if not\n"); 102 | printf("jackal -sc -c certificates -o outputfilespec [-C newCAcert -K newCAkey]\n"); 103 | printf(" signs the certificate chain with the CA if specified or self-signs the root if not\n"); 104 | printf("jackal -sr -c certificates -o outputfilesepc -C newCAcert -K newCAkey\n"); 105 | printf(" signs the certificate chain, replacing the current root with the specified CA\n"); 106 | printf("\n"); 107 | printf("outputfilespec will be appended with .pem/.key with -sl and .n.pem/.key with -sc/r\n"); 108 | printf("\n"); 109 | 110 | if (msg) { 111 | printf("%s\n", msg); 112 | exit(1); 113 | } 114 | 115 | exit(0); 116 | } 117 | 118 | 119 | void hexdump(unsigned char *data, int data_len, int indent) 120 | { 121 | int i, j; 122 | 123 | for (i=0; isignature, NULL); 172 | hash = OPENSSL_malloc(hashlength); 173 | 174 | if ((!algorithm) || (!pubalgo) || (!hash)) { 175 | printf("print_cert malloc error\n"); 176 | exit(1); 177 | } 178 | 179 | if (!i2t_ASN1_OBJECT(algorithm, ALGO_LEN, cert->sig_alg->algorithm)) { 180 | printf("print_cert i2t_ASN1_OBJECT failed\n"); 181 | exit(1); 182 | } 183 | 184 | if (!i2t_ASN1_OBJECT(pubalgo, ALGO_LEN, cert->cert_info->key->algor->algorithm)) { 185 | printf("print_cert i2t_ASN1_OBJECT failed\n"); 186 | exit(1); 187 | } 188 | 189 | certkey = X509_get_pubkey(cert); 190 | modulus = BN_bn2hex(certkey->pkey.rsa->n); 191 | 192 | temp = hash; 193 | if (!i2c_ASN1_BIT_STRING(cert->signature, &temp)) { 194 | printf("print_cert i2c_ASN1_BIT_STRING failed\n"); 195 | exit(1); 196 | } 197 | 198 | temp = serialnumber; 199 | if (!i2c_ASN1_INTEGER(X509_get_serialNumber(cert), &temp)) { 200 | printf("print_cert i2c_ASN1_INTEGER failed\n"); 201 | exit(1); 202 | } 203 | 204 | issuer = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); 205 | subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); 206 | 207 | time = X509_get_notBefore(cert); 208 | strncpy(notBefore, (const char *)time->data, ALGO_LEN); 209 | notBefore[ALGO_LEN - 1] = 0x00; 210 | time = X509_get_notAfter(cert); 211 | strncpy(notAfter, (const char *)time->data, ALGO_LEN); 212 | notAfter[ALGO_LEN - 1] = 0x00; 213 | 214 | printf("Certificate:\n"); 215 | printf(" Data:\n"); 216 | printf(" Version: 0x%0lx\n", X509_get_version(cert)); 217 | printf(" Serial Number:\n"); 218 | hexdump(serialnumber, seriallength, 3); 219 | printf(" Signature Algorithm: %s\n", algorithm); 220 | printf(" Issuer: %s\n", issuer); 221 | printf(" Validity\n"); 222 | printf(" Not Before: %s\n", notBefore); 223 | printf(" Not After : %s\n", notAfter); 224 | printf(" Subject: %s\n", subject); 225 | printf(" Subject Public Key Info:\n"); 226 | printf(" Public Key Algorithm: %s\n", pubalgo); 227 | printf(" Public-Key: (%d bit)\n", EVP_PKEY_bits(certkey)); 228 | printf(" Modulus:%s\n", modulus); 229 | printf(" Signature Algorithm: %s\n", algorithm); 230 | hexdump(hash, hashlength, 2); 231 | 232 | } 233 | 234 | 235 | X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x) 236 | { 237 | int i; 238 | X509 *issuer; 239 | 240 | if ((!ctx) || (!sk) || (!x)) { 241 | printf("find_issuer invalid params\n"); 242 | exit(1); 243 | } 244 | 245 | for (i=0; i < sk_X509_num(sk); i++) { 246 | issuer = sk_X509_value(sk, i); 247 | if (ctx->check_issued(ctx, x, issuer)) 248 | return issuer; 249 | } 250 | return NULL; 251 | } 252 | 253 | 254 | void get_cert_from_file(SSL_CTX **sslctx, SSL **ssl, X509 **crt, char *certfile) 255 | { 256 | if ((!sslctx) || (!ssl) || (!crt) || (!certfile)) { 257 | printf("get_cert_from_file invalid params\n"); 258 | exit(1); 259 | } 260 | 261 | /* open certificate file */ 262 | *sslctx = SSL_CTX_new(TLSv1_server_method()); 263 | if (!*sslctx) { 264 | printf("get_cert_from_file SSL_CTX_new failed\n"); 265 | exit(1); 266 | } 267 | if (!SSL_CTX_use_certificate_chain_file(*sslctx, certfile)) { 268 | printf("get_cert_from_file SSL_CTX_use_chain_certificate_file failed\n"); 269 | exit(1); 270 | } 271 | 272 | *ssl = SSL_new(*sslctx); 273 | if (!*ssl) { 274 | printf("get_cert_from_file SSL_new failed\n"); 275 | exit(1); 276 | } 277 | *crt = SSL_get_certificate(*ssl); 278 | if (!*crt) { 279 | printf("get_cert_from_file SSL_get_certifcate failed\n"); 280 | exit(1); 281 | } 282 | } 283 | 284 | STACK_OF(X509) *get_extra_certs(SSL_CTX *sslctx) 285 | { 286 | STACK_OF(X509) *ca_stack; 287 | 288 | if (!sslctx) { 289 | printf("get_extra_certs invalid params\n"); 290 | exit(1); 291 | } 292 | 293 | // ca_stack = sslctx->extra_certs; 294 | if (SSL_CTX_get_extra_chain_certs(sslctx, &ca_stack)) { 295 | // printf("SSL_CTX_get_extra_chain_certs_only success\n"); 296 | } else { 297 | printf("SSL_CTX_get_extra_chain_certs_only failed\n"); 298 | exit(0); 299 | } 300 | 301 | if (!ca_stack) { 302 | printf("Cert file contains 1 certificate\n"); 303 | } else { 304 | printf("Cert file contains >1 certificates\n"); 305 | } 306 | 307 | return ca_stack; 308 | } 309 | 310 | 311 | void get_key_from_file(SSL *cassl, char *cakeyfile) 312 | { 313 | if ((!cassl) || (!cakeyfile)) { 314 | printf("get_key_from_file invalid params\n"); 315 | exit(1); 316 | } 317 | 318 | printf("Loading CA key\n"); 319 | 320 | /* read CA key file */ 321 | if (!SSL_use_PrivateKey_file(cassl, cakeyfile, SSL_FILETYPE_PEM)) { 322 | printf("get_key_from_file SSL_use_PrivateKey_file failed\n"); 323 | exit(1); 324 | } 325 | } 326 | 327 | 328 | int get_signing_algo(X509 *crt) 329 | { 330 | int nid; 331 | 332 | if (!crt) { 333 | printf("get_signing_algo invalid params\n"); 334 | exit(1); 335 | } 336 | 337 | /* get signing algorithm */ 338 | nid = OBJ_obj2nid(crt->sig_alg->algorithm); 339 | if (!nid) { 340 | printf("get_signing_algo OBJ_obj2nid failed\n"); 341 | exit(1); 342 | } 343 | 344 | return nid; 345 | } 346 | 347 | 348 | char *get_public_algo(X509 *crt) 349 | { 350 | char *pubalgo = OPENSSL_malloc(ALGO_LEN); 351 | 352 | if (!crt) { 353 | printf("get_public_algo invalid params\n"); 354 | exit(1); 355 | } 356 | 357 | if (!pubalgo) { 358 | printf("get_public_algo malloc error\n"); 359 | exit(1); 360 | } 361 | 362 | if (!i2t_ASN1_OBJECT(pubalgo, ALGO_LEN, crt->cert_info->key->algor->algorithm)) { 363 | printf("i2d_ASN1_OBJECT failed\n"); 364 | exit(1); 365 | } 366 | 367 | return pubalgo; 368 | } 369 | 370 | 371 | int get_pub_key_size(X509 *crt) 372 | { 373 | EVP_PKEY *certkey; 374 | 375 | if (!crt) { 376 | printf("get_pub_key_size invalid params\n"); 377 | exit(1); 378 | } 379 | 380 | certkey = X509_get_pubkey(crt); 381 | 382 | if (!certkey) { 383 | printf("get_pub_key_size X509_get_pubkey failed\n"); 384 | exit(1); 385 | } 386 | 387 | return EVP_PKEY_bits(certkey); 388 | } 389 | 390 | 391 | EVP_PKEY *gen_new_key(int certkeysize) 392 | { 393 | RSA *newrsa; 394 | EVP_PKEY *newkey; 395 | 396 | if (certkeysize <= 0) { 397 | printf("gen_new_key invalid params\n"); 398 | exit(1); 399 | } 400 | 401 | newrsa = RSA_generate_key(certkeysize, 65537, 0, 0); 402 | if (!newrsa) { 403 | printf("gen_new_key RSA_generate_key failed\n"); 404 | exit(1); 405 | } 406 | newkey = EVP_PKEY_new(); 407 | if (!newkey) { 408 | printf("gen_new_key EVP_PKEY_new failed\n"); 409 | exit(1); 410 | } 411 | 412 | if (!EVP_PKEY_assign_RSA(newkey, newrsa)) { 413 | printf("gen_new_key EVP_PKEY_assign_RSA failed\n"); 414 | exit(1); 415 | } 416 | 417 | return newkey; 418 | } 419 | 420 | 421 | void assign_key_to_cert(X509 *crt, EVP_PKEY *newkey) 422 | { 423 | if ((!crt) || (!newkey)) { 424 | printf("assign_key_to_cert invalid params\n"); 425 | exit(1); 426 | } 427 | 428 | if (!X509_set_pubkey(crt, newkey)) { 429 | printf("assign_key_to_cert X509_set_pubkey failed\n"); 430 | exit(1); 431 | } 432 | } 433 | 434 | 435 | void copy_subject_to_issuer(X509 *cacrt, X509 *crt) 436 | { 437 | X509_NAME *caname; 438 | 439 | if ((!cacrt) || (!crt)) { 440 | printf("copy_subject_to_issuer invalid params\n"); 441 | exit(1); 442 | } 443 | 444 | caname = X509_get_subject_name(cacrt); 445 | if (!caname) { 446 | printf("copy_subject_to_issuer X509_get_subject_name failed\n"); 447 | exit(1); 448 | } 449 | 450 | printf("ca subject is '%s'\n", X509_NAME_oneline(caname, NULL, 0)); 451 | 452 | if (!X509_set_issuer_name(crt, caname)) { 453 | printf("copy_subject_to_issuer X509_set_issuer_name failed\n"); 454 | exit(1); 455 | } 456 | } 457 | 458 | 459 | int ext_exists(X509 *crt, int nid) 460 | { 461 | int num_exts; 462 | STACK_OF(X509_EXTENSION) *exts; 463 | X509_EXTENSION *ex; 464 | ASN1_OBJECT *obj; 465 | int i; 466 | 467 | if ((!crt) || (!nid)) { 468 | printf("ext_exists invalid params\n"); 469 | exit(1); 470 | } 471 | 472 | exts = crt->cert_info->extensions; 473 | if (exts) { 474 | num_exts = sk_X509_EXTENSION_num(exts); 475 | } else { 476 | num_exts = 0; 477 | } 478 | 479 | for (i=0; ilength < 1) { 787 | printf("set_CA_flags key usage length < 1\n"); 788 | exit(1); 789 | } 790 | usage_val = usage->data[0]; 791 | if (usage->length > 1) { 792 | usage_val = usage_val | (usage->data[1] << 8); 793 | } 794 | 795 | if (!(usage_val & X509v3_KU_KEY_CERT_SIGN)) { 796 | /* add key cert sign flag */ 797 | newusage = ASN1_BIT_STRING_new(); 798 | if (!newusage) { 799 | printf("set_CA_flags ASN1_BIT_STRING_new failed\n"); 800 | exit(1); 801 | } 802 | usage_val = usage_val | X509v3_KU_KEY_CERT_SIGN; 803 | newusage_data[0] = (unsigned char) (usage_val & 0xff); 804 | newusage_data[1] = (unsigned char) ((usage_val >> 8) & 0xff); 805 | delete_extension(crt, NID_key_usage); 806 | if (!ASN1_BIT_STRING_set(newusage, newusage_data, 2)) { 807 | printf("set_CA_flags ASN1_BIT_STRING_set failed\n"); 808 | exit(1); 809 | } 810 | add_extension_raw(crt, NID_key_usage, newusage, critical); 811 | } 812 | } else { 813 | /* add a key usage extension */ 814 | newusage = ASN1_BIT_STRING_new(); 815 | if (!newusage) { 816 | printf("set_CA_flags ASN1_BIT_STRING_new failed\n"); 817 | exit(1); 818 | } 819 | usage_val = X509v3_KU_KEY_CERT_SIGN; 820 | newusage_data[0] = (unsigned char) (usage_val & 0xff); 821 | newusage_data[1] = (unsigned char) ((usage_val >> 8) & 0xff); 822 | if (!ASN1_BIT_STRING_set(newusage, newusage_data, 2)) { 823 | printf("set_CA_flags ASN1_BIT_STRING_set failed\n"); 824 | exit(1); 825 | } 826 | add_extension_raw(crt, NID_key_usage, newusage, critical); 827 | } 828 | 829 | break; 830 | default: 831 | printf("Invalid certificate version: %ld\n", ver); 832 | exit(1); 833 | } 834 | } 835 | 836 | 837 | /* clone_certificate */ 838 | EVP_PKEY *clone_certificate(X509 *crt, X509 *issuercrt, EVP_PKEY *issuercertkey, char *outputfile, int depth) 839 | { 840 | char outputcert[256]; 841 | char outputkey[256]; 842 | 843 | EVP_PKEY *newkey = NULL; 844 | int certkeysize = 0; 845 | X509V3_CTX extctx; 846 | int algorithm = 0; 847 | int issuerversion = 0; 848 | 849 | FILE *pemfp; 850 | FILE *keyfp; 851 | 852 | if ((!crt) || (!outputfile) || (depth < 0)) { 853 | printf("clone_certificate invalid params\n"); 854 | exit(1); 855 | } 856 | 857 | /* get issuer certificate version */ 858 | if (issuercrt) { 859 | issuerversion = X509_get_version(issuercrt); 860 | } else { 861 | issuerversion = X509_get_version(crt); 862 | } 863 | 864 | printf("\n### %s ### v%ld\n", X509_NAME_oneline(X509_get_subject_name(crt), NULL, 0), X509_get_version(crt) + 1); 865 | 866 | /* get signing algorithm */ 867 | algorithm = get_signing_algo(crt); 868 | 869 | printf("sig hash type = %d\n", algorithm); 870 | 871 | /* get public key size */ 872 | certkeysize = get_pub_key_size(crt); 873 | 874 | printf("public key size = %d\n", certkeysize); 875 | 876 | /* generate new key */ 877 | newkey = gen_new_key(certkeysize); 878 | 879 | /* assign key to certificate */ 880 | assign_key_to_cert(crt, newkey); 881 | 882 | /* copy the subject name of the issuer to the issuer name of the cert */ 883 | if (issuercrt) { 884 | copy_subject_to_issuer(issuercrt, crt); 885 | } else { 886 | copy_subject_to_issuer(crt, crt); 887 | } 888 | 889 | /* create an extension context linking the CA cert to the subject cert */ 890 | if (issuercrt) { 891 | create_ext_ctx(issuercrt, crt, &extctx); 892 | } else { 893 | create_ext_ctx(crt, crt, &extctx); 894 | } 895 | 896 | /* correct the subject key identifier if it exists */ 897 | if (ext_exists(crt, NID_subject_key_identifier)) { 898 | delete_extension(crt, NID_subject_key_identifier); 899 | add_extension(crt, &extctx, NID_subject_key_identifier, "hash"); 900 | printf("subject key identifier changed\n"); 901 | } 902 | 903 | if (issuerversion == 2) { 904 | 905 | /* correct the authority key identifier if it exists */ 906 | if (ext_exists(crt, NID_authority_key_identifier)) { 907 | delete_extension(crt, NID_authority_key_identifier); 908 | add_extension(crt, &extctx, NID_authority_key_identifier, "keyid:always"); 909 | printf("authority key identifier changed\n"); 910 | } 911 | } 912 | 913 | /* set CA flags for self-signed certs (no issuer) and where depth > 1 (intermediate or CA) */ 914 | if ((!issuercrt) || (depth)) { 915 | set_CA_flags(crt, &extctx); 916 | } 917 | 918 | /* sign the cert */ 919 | if (issuercrt) { 920 | sign_cert(crt, issuercertkey, algorithm); 921 | } else { 922 | sign_cert(crt, newkey, algorithm); 923 | } 924 | 925 | /* create output file names */ 926 | snprintf(outputcert, 256, "%s.pem", outputfile); 927 | snprintf(outputkey, 256, "%s.key", outputfile); 928 | 929 | /* open output files */ 930 | pemfp = open_output_file(outputcert); 931 | keyfp = open_output_file(outputkey); 932 | 933 | /* write the cert */ 934 | write_cert(crt, pemfp); 935 | 936 | /* write the new private key */ 937 | write_key(newkey, keyfp); 938 | 939 | /* close the output files */ 940 | fclose(pemfp); 941 | fclose(keyfp); 942 | 943 | return newkey; 944 | } 945 | 946 | 947 | /* clone_chain */ 948 | EVP_PKEY *clone_chain(X509 *crt, STACK_OF(X509) *ca_stack, X509 *cacrt, EVP_PKEY *cakey, char *outputfile, int depth, char command) 949 | { 950 | EVP_PKEY *key = NULL; 951 | X509_STORE_CTX *storectx = NULL; 952 | X509 *issuer = NULL; 953 | 954 | if ((!crt) || (!ca_stack) || (!outputfile)) { 955 | printf("clone_chain invalid params\n"); 956 | exit(1); 957 | } 958 | 959 | /* create context for chain */ 960 | storectx = X509_STORE_CTX_new(); 961 | if (!storectx) { 962 | printf("clone_chain X509_STORE_CTX_new failed\n"); 963 | exit(1); 964 | } 965 | 966 | if (!X509_STORE_CTX_init(storectx, NULL, crt, ca_stack)) { 967 | printf("clone_chain X509_STORE_init failed\n"); 968 | exit(1); 969 | } 970 | 971 | /* retrieve issuer key */ 972 | issuer = find_issuer(storectx, ca_stack, crt); 973 | 974 | if ((issuer) && (X509_name_cmp(X509_get_subject_name(crt), X509_get_issuer_name(crt)))) { 975 | /* not at end of chain */ 976 | 977 | /* clone rest of chain */ 978 | key = clone_chain(issuer, ca_stack, cacrt, cakey, outputfile, depth + 1, command); 979 | 980 | /* fix up output file name */ 981 | outputfile[strlen(outputfile) - 1] = depth + '0'; 982 | 983 | /* sign this cert and return the key */ 984 | if (key != cakey) { 985 | key = clone_certificate(crt, issuer, key, outputfile, depth); 986 | } else { 987 | key = clone_certificate(crt, cacrt, cakey, outputfile, depth); 988 | } 989 | 990 | } else { 991 | /* end of chain */ 992 | 993 | /* fix up output file name */ 994 | outputfile[strlen(outputfile) - 1] = depth + '0'; 995 | 996 | if (command == 'c') { 997 | /* sign this cert and return the key */ 998 | key = clone_certificate(crt, cacrt, cakey, outputfile, depth); 999 | } else if (command == 'r') { 1000 | /* return the ca key */ 1001 | key = cakey; 1002 | } else { 1003 | printf("clone_chain invalid command\n"); 1004 | exit(1); 1005 | } 1006 | } 1007 | 1008 | return key; 1009 | } 1010 | 1011 | 1012 | 1013 | 1014 | int main(int argc, char *argv[]) 1015 | { 1016 | char c; 1017 | 1018 | char *certfile = NULL; 1019 | char *cacertfile = NULL; 1020 | char *cakeyfile = NULL; 1021 | char *outputfile = NULL; 1022 | char *outputchainfile = NULL; 1023 | 1024 | SSL_CTX *sslctx = NULL; 1025 | SSL *ssl = NULL; 1026 | X509 *crt = NULL; 1027 | STACK_OF(X509) *ca_stack = NULL; 1028 | X509 *tempcert = NULL; 1029 | SSL_CTX *casslctx = NULL; 1030 | SSL *cassl = NULL; 1031 | X509 *cacrt = NULL; 1032 | X509_STORE_CTX *storectx = NULL; 1033 | EVP_PKEY *issuerkey = NULL; 1034 | 1035 | char command = ' '; 1036 | int haveCA = 0; 1037 | 1038 | printf("Jackal v1.0 - Certificate Cloner - K Sheldrake\n"); 1039 | printf("----------------------------------------------\n\n"); 1040 | printf("Jackal comes with ABSOLUTELY NO WARRANTY.\n"); 1041 | printf("This is free software, and you are welcome\n"); 1042 | printf("to redistribute it under certain conditions.\n"); 1043 | printf("Jackal is provided under GPLv2.\n"); 1044 | printf("See http://www.gnu.org/licenses/gpl-2.0.html\n"); 1045 | printf("for details.\n\n"); 1046 | printf("\"Certainly, the Jackal masqueraded as an Englishman,\n"); 1047 | printf("but he also masqueraded as a Dane and as a Frenchman.\n"); 1048 | printf("So there's no way of proving his identity at all.\"\n"); 1049 | printf(" - The Day of the Jackal\n\n\n"); 1050 | 1051 | /* sort options */ 1052 | opterr = 0; 1053 | 1054 | while((c = getopt(argc, argv, "hc:C:K:o:s:")) != -1) { 1055 | switch(c) { 1056 | case 'h': 1057 | usage(NULL); 1058 | break; 1059 | case 'c': 1060 | if (strlen(optarg) > 0) { 1061 | certfile = optarg; 1062 | } 1063 | break; 1064 | case 'C': 1065 | if (strlen(optarg) > 0) { 1066 | cacertfile = optarg; 1067 | } 1068 | break; 1069 | case 'K': 1070 | if (strlen(optarg) > 0) { 1071 | cakeyfile = optarg; 1072 | } 1073 | break; 1074 | case 'o': 1075 | if (strlen(optarg) > 0) { 1076 | outputfile = optarg; 1077 | } 1078 | break; 1079 | case 's': 1080 | if (strlen(optarg) == 1) { 1081 | command = *optarg; 1082 | } 1083 | break; 1084 | default: 1085 | usage("unknown option"); 1086 | } 1087 | } 1088 | 1089 | if (!certfile) { 1090 | usage(NULL); 1091 | } 1092 | 1093 | haveCA = 0; 1094 | if ((cacertfile) && (cakeyfile)) { 1095 | haveCA = 1; 1096 | } else if ((cacertfile) || (cakeyfile)) { 1097 | usage("Specify either both -C and -K, or specify neither"); 1098 | } 1099 | 1100 | if ((command == ' ') && ((haveCA) || (outputfile))) { 1101 | usage("You need to specify a command wtih -s"); 1102 | } 1103 | 1104 | if (! strchr(" lcr", command)) { 1105 | usage("Command must be -sl, -sc, -sr or unspecified to print certificate details"); 1106 | } 1107 | 1108 | if ((strchr("lcr", command)) && (!outputfile)) { 1109 | usage("You must specify an output file specification"); 1110 | } 1111 | 1112 | if ((command == 'r') && (!haveCA)) { 1113 | usage("You need to specify a CA with -sr\n"); 1114 | } 1115 | 1116 | /* set up openssl */ 1117 | SSL_load_error_strings(); 1118 | ERR_load_BIO_strings(); 1119 | SSL_library_init(); 1120 | 1121 | /* open certificate file */ 1122 | get_cert_from_file(&sslctx, &ssl, &crt, certfile); 1123 | 1124 | if (haveCA) { 1125 | /* open CA certificate file */ 1126 | get_cert_from_file(&casslctx, &cassl, &cacrt, cacertfile); 1127 | 1128 | /* read CA key file */ 1129 | get_key_from_file(cassl, cakeyfile); 1130 | 1131 | issuerkey = get_private_key(cassl); 1132 | } 1133 | 1134 | /* get any extra certs */ 1135 | ca_stack = get_extra_certs(sslctx); 1136 | 1137 | /* if only a certificate file, display and exit */ 1138 | if (command == ' ') { 1139 | print_cert(crt); 1140 | 1141 | if (ca_stack) { 1142 | storectx = X509_STORE_CTX_new(); 1143 | if (!storectx) { 1144 | printf("main X509_STORE_CTX_new failed\n"); 1145 | exit(1); 1146 | } 1147 | 1148 | if (!X509_STORE_CTX_init(storectx, NULL, crt, ca_stack)) { 1149 | printf("main X509_STORE_init failed\n"); 1150 | exit(1); 1151 | } 1152 | 1153 | tempcert = crt; 1154 | 1155 | while (tempcert) { 1156 | printf("%s\n", X509_NAME_oneline(X509_get_subject_name(tempcert), NULL, 0)); 1157 | if (!X509_name_cmp(X509_get_subject_name(tempcert), X509_get_issuer_name(tempcert))) { 1158 | tempcert = NULL; 1159 | } else { 1160 | tempcert = find_issuer(storectx, ca_stack, tempcert); 1161 | } 1162 | } 1163 | 1164 | } 1165 | 1166 | exit(0); 1167 | } 1168 | 1169 | switch(command) { 1170 | case 'l': 1171 | if (haveCA) { 1172 | clone_certificate(crt, cacrt, issuerkey, outputfile, 0); 1173 | } else { 1174 | clone_certificate(crt, NULL, NULL, outputfile, 0); 1175 | } 1176 | break; 1177 | case 'c': 1178 | case 'r': 1179 | outputchainfile = malloc(strlen(outputfile) + 3); 1180 | if (!outputchainfile) { 1181 | printf("main malloc error\n"); 1182 | exit(1); 1183 | } 1184 | sprintf(outputchainfile, "%s.1", outputfile); 1185 | if (haveCA) { 1186 | if (!clone_chain(crt, ca_stack, cacrt, issuerkey, outputchainfile, 0, command)) { 1187 | printf("main clone_chain failed\n"); 1188 | exit(1); 1189 | } 1190 | } else { 1191 | if (!clone_chain(crt, ca_stack, NULL, NULL, outputchainfile, 0, command)) { 1192 | printf("main clone_chain failed\n"); 1193 | exit(1); 1194 | } 1195 | } 1196 | break; 1197 | 1198 | } 1199 | 1200 | 1201 | printf("\nSuccess!\n\n"); 1202 | 1203 | return 0; 1204 | 1205 | } 1206 | 1207 | 1208 | -------------------------------------------------------------------------------- /jackal.1.2.c: -------------------------------------------------------------------------------- 1 | /* jackal - certificate cloner - K Sheldrake 2 | ** 3 | ** Copyright (C) 2015 Kevin Sheldrake 4 | ** 5 | ** This file is part of jackal. 6 | ** 7 | ** Jackal is free software; you can redistribute it and/or 8 | ** modify it under the terms of the GNU General Public License 9 | ** as published by the Free Software Foundation; either version 2 10 | ** of the License, or (at your option) any later version. 11 | ** 12 | ** This program is distributed in the hope that it will be useful, 13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ** GNU General Public License for more details. 16 | ** 17 | ** You should have received a copy of the GNU General Public License 18 | ** along with this program; if not, write to the Free Software 19 | ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | ** 21 | ** 22 | ** jackal clones SSL certificates. The purpose is to automate and simplify a step in the SSL MITM process. 23 | ** 24 | ** SSL/TLS connections are established on trust provided by the certificates that are exchanged. Typically 25 | ** the server sends a certificate to the client for verification. All certificates are signed by another 26 | ** certificate. Self-signed certificates are signed by themselves; in all other cases, an end user cerficate 27 | ** is signed by an intermediate certificate authority, which is in-turn signed by other intermediates, the 28 | ** last of which is signed by a root certificate authority. The root certificate authority is self-signed 29 | ** and should be available to the tool or client that wants to verify the end user certificate. 30 | ** 31 | ** To verify an end user certificate a tool or client finds the certificate that signed it, and then recursively 32 | ** verifies that. If any of the certificates are contained in the CA store, the certificate is deemed to have 33 | ** a valid signature. There are other checks, such as validity dates and confirmation of the end user 34 | ** certificate details to ensure the right certificate is in use. 35 | ** 36 | ** Man-in-the-middle attacks are possible if the certificate verification routines are flawed or if it is 37 | ** possible to add a fake CA to the CA store. In order to effect such an attack, certificates that mimic 38 | ** the real certificates are required. This tool, jackal, makes fake certificates that are identical to 39 | ** the originals except for the change of keys. 40 | ** 41 | ** Create a new CA with: 42 | ** % openssl genrsa -des3 -out ca.key 2048 43 | ** % openssl req -new -x509 -days 3650 -key ca.key -out ca.pem 44 | ** 45 | ** Check certificate with: 46 | ** % openssl x509 -in certificate.pem -noout -text 47 | ** 48 | ** Verify certificate trust with: 49 | ** % openssl verify -CAfile ca.pem -untrusted intermediatecas.pem certificate.pem 50 | ** 51 | ** 52 | ** Compile with: 53 | ** cc -o jackal jackal.c -lssl -lcrypto 54 | ** 55 | ** History: 56 | ** 57 | ** v1.0 - 8/11/2014 58 | ** 59 | ** Uplifted to v1.0 for release. 60 | ** Tidied welcome message. 61 | ** Added quote from The Day of the Jackal. :) 62 | ** 63 | ** v0.3 - 8/11/2014 64 | ** 65 | ** Now clones whole certificate chains. 66 | ** 67 | ** v0.2 - 30/10/2014 68 | ** 69 | ** Rewrite of version 1, same functionality. 70 | ** 71 | ** v0.1 - 30/10/2014 72 | ** 73 | ** Proof of concept. 74 | ** Takes one certificate and one CA and makes a new certificate with a new key that matches the original 75 | ** and is signed by the supplied CA. 76 | ** 77 | */ 78 | 79 | #include 80 | #include 81 | #include 82 | #include 83 | #include 84 | 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | 91 | #define HEX_PER_ROW 16 92 | #define ALGO_LEN 64 93 | #define OBJSTR_LEN 1024 94 | 95 | void usage(char *msg) 96 | { 97 | printf("ONLY USE FOR TESTING (don't be a muppet)\n\n"); 98 | printf("jackal -c certificate\n"); 99 | printf(" displays certificate details\n"); 100 | printf("jackal -sl -c certificate(s) -o outputfilespec [-C newCAcert -K newCAkey]\n"); 101 | printf(" signs the leaf certificate with the CA if specified or self-signs it if not\n"); 102 | printf("jackal -sc -c certificates -o outputfilespec [-C newCAcert -K newCAkey]\n"); 103 | printf(" signs the certificate chain with the CA if specified or self-signs the root if not\n"); 104 | printf("jackal -sr -c certificates -o outputfilesepc -C newCAcert -K newCAkey\n"); 105 | printf(" signs the certificate chain, replacing the current root with the specified CA\n"); 106 | printf("\n"); 107 | printf("outputfilespec will be appended with .pem/.key with -sl and .n.pem/.key with -sc/r\n"); 108 | printf("\n"); 109 | 110 | if (msg) { 111 | printf("%s\n", msg); 112 | exit(1); 113 | } 114 | 115 | exit(0); 116 | } 117 | 118 | 119 | void hexdump(unsigned char *data, int data_len, int indent) 120 | { 121 | int i, j; 122 | 123 | for (i=0; idata, ALGO_LEN); 190 | notBefore[ALGO_LEN - 1] = 0x00; 191 | time = X509_get_notAfter(cert); 192 | strncpy(notAfter, (const char *)time->data, ALGO_LEN); 193 | notAfter[ALGO_LEN - 1] = 0x00; 194 | 195 | printf("Certificate:\n"); 196 | printf(" Data:\n"); 197 | printf(" Version: 0x%0lx\n", X509_get_version(cert)); 198 | printf(" Serial Number:\n"); 199 | hexdump((unsigned char *)serialnumber, seriallength, 3); 200 | printf(" Signature Algorithm: %s\n", algorithm); 201 | printf(" Issuer: %s\n", issuer); 202 | printf(" Validity\n"); 203 | printf(" Not Before: %s\n", notBefore); 204 | printf(" Not After : %s\n", notAfter); 205 | printf(" Subject: %s\n", subject); 206 | printf(" Subject Public Key Info:\n"); 207 | printf(" Public Key Algorithm: %s\n", pubalgo); 208 | printf(" Public-Key: (%d bit)\n", EVP_PKEY_bits(certkey)); 209 | printf(" Modulus:%s\n", modulus); 210 | printf(" Signature Algorithm: %s\n", algorithm); 211 | hexdump((unsigned char *)hash, hashlength, 2); 212 | 213 | } 214 | 215 | 216 | X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x) 217 | { 218 | int i; 219 | X509 *issuer; 220 | X509_STORE_CTX_check_issued_fn check_issued; 221 | 222 | if ((!ctx) || (!sk) || (!x)) { 223 | printf("find_issuer invalid params\n"); 224 | exit(1); 225 | } 226 | 227 | check_issued = X509_STORE_CTX_get_check_issued(ctx); 228 | 229 | for (i=0; i < sk_X509_num(sk); i++) { 230 | issuer = sk_X509_value(sk, i); 231 | if (check_issued(ctx, x, issuer)) 232 | return issuer; 233 | } 234 | return NULL; 235 | } 236 | 237 | 238 | void get_cert_from_file(SSL_CTX **sslctx, SSL **ssl, X509 **crt, char *certfile) 239 | { 240 | if ((!sslctx) || (!ssl) || (!crt) || (!certfile)) { 241 | printf("get_cert_from_file invalid params\n"); 242 | exit(1); 243 | } 244 | 245 | /* open certificate file */ 246 | *sslctx = SSL_CTX_new(TLS_server_method()); 247 | if (!*sslctx) { 248 | printf("get_cert_from_file SSL_CTX_new failed\n"); 249 | exit(1); 250 | } 251 | if (!SSL_CTX_use_certificate_chain_file(*sslctx, certfile)) { 252 | printf("get_cert_from_file SSL_CTX_use_chain_certificate_file failed\n"); 253 | exit(1); 254 | } 255 | 256 | *ssl = SSL_new(*sslctx); 257 | if (!*ssl) { 258 | printf("get_cert_from_file SSL_new failed\n"); 259 | exit(1); 260 | } 261 | *crt = SSL_get_certificate(*ssl); 262 | if (!*crt) { 263 | printf("get_cert_from_file SSL_get_certifcate failed\n"); 264 | exit(1); 265 | } 266 | } 267 | 268 | STACK_OF(X509) *get_extra_certs(SSL_CTX *sslctx) 269 | { 270 | STACK_OF(X509) *ca_stack; 271 | 272 | if (!sslctx) { 273 | printf("get_extra_certs invalid params\n"); 274 | exit(1); 275 | } 276 | 277 | if (SSL_CTX_get_extra_chain_certs(sslctx, &ca_stack)) { 278 | // printf("SSL_CTX_get_extra_chain_certs_only success\n"); 279 | } else { 280 | printf("SSL_CTX_get_extra_chain_certs_only failed\n"); 281 | exit(0); 282 | } 283 | 284 | if (!ca_stack) { 285 | printf("Cert file contains 1 certificate\n"); 286 | } else { 287 | printf("Cert file contains >1 certificates\n"); 288 | } 289 | 290 | return ca_stack; 291 | } 292 | 293 | 294 | void get_key_from_file(SSL *cassl, char *cakeyfile) 295 | { 296 | if ((!cassl) || (!cakeyfile)) { 297 | printf("get_key_from_file invalid params\n"); 298 | exit(1); 299 | } 300 | 301 | printf("Loading CA key\n"); 302 | 303 | /* read CA key file */ 304 | if (!SSL_use_PrivateKey_file(cassl, cakeyfile, SSL_FILETYPE_PEM)) { 305 | printf("get_key_from_file SSL_use_PrivateKey_file failed\n"); 306 | exit(1); 307 | } 308 | } 309 | 310 | 311 | int get_signing_algo(X509 *crt) 312 | { 313 | int nid; 314 | 315 | if (!crt) { 316 | printf("get_signing_algo invalid params\n"); 317 | exit(1); 318 | } 319 | 320 | /* get signing algorithm */ 321 | nid = X509_get_signature_nid(crt); 322 | if (!nid) { 323 | printf("get_signing_algo OBJ_obj2nid failed\n"); 324 | exit(1); 325 | } 326 | 327 | return nid; 328 | } 329 | 330 | 331 | const char *get_public_algo(X509 *crt) 332 | { 333 | const char *pubalgo = OPENSSL_malloc(ALGO_LEN); 334 | EVP_PKEY *certkey; 335 | 336 | if (!crt) { 337 | printf("get_public_algo invalid params\n"); 338 | exit(1); 339 | } 340 | 341 | if (!pubalgo) { 342 | printf("get_public_algo malloc error\n"); 343 | exit(1); 344 | } 345 | 346 | certkey = X509_get_pubkey(crt); 347 | pubalgo = OBJ_nid2ln(EVP_PKEY_base_id(certkey)); 348 | 349 | return pubalgo; 350 | } 351 | 352 | 353 | int get_pub_key_size(X509 *crt) 354 | { 355 | EVP_PKEY *certkey; 356 | 357 | if (!crt) { 358 | printf("get_pub_key_size invalid params\n"); 359 | exit(1); 360 | } 361 | 362 | certkey = X509_get_pubkey(crt); 363 | 364 | if (!certkey) { 365 | printf("get_pub_key_size X509_get_pubkey failed\n"); 366 | exit(1); 367 | } 368 | 369 | return EVP_PKEY_bits(certkey); 370 | } 371 | 372 | 373 | EVP_PKEY *gen_new_key(int certkeysize) 374 | { 375 | RSA *newrsa = NULL; 376 | EVP_PKEY *newkey = NULL; 377 | BIGNUM *e = NULL; 378 | 379 | if (certkeysize <= 0) { 380 | printf("gen_new_key invalid params\n"); 381 | exit(1); 382 | } 383 | 384 | if (!BN_dec2bn(&e, "65537")) { 385 | printf("gen_new_key BN_dec2bn failed\n"); 386 | exit(1); 387 | } 388 | newrsa = RSA_new(); 389 | if (!newrsa) { 390 | printf("gen_new_key RSA_new failed\n"); 391 | exit(1); 392 | } 393 | if (!RSA_generate_key_ex(newrsa, certkeysize, e, 0)) { 394 | printf("gen_new_key RSA_generate_key_ex failed\n"); 395 | exit(1); 396 | } 397 | newkey = EVP_PKEY_new(); 398 | if (!newkey) { 399 | printf("gen_new_key EVP_PKEY_new failed\n"); 400 | exit(1); 401 | } 402 | 403 | if (!EVP_PKEY_assign_RSA(newkey, newrsa)) { 404 | printf("gen_new_key EVP_PKEY_assign_RSA failed\n"); 405 | exit(1); 406 | } 407 | 408 | return newkey; 409 | } 410 | 411 | 412 | void assign_key_to_cert(X509 *crt, EVP_PKEY *newkey) 413 | { 414 | if ((!crt) || (!newkey)) { 415 | printf("assign_key_to_cert invalid params\n"); 416 | exit(1); 417 | } 418 | 419 | if (!X509_set_pubkey(crt, newkey)) { 420 | printf("assign_key_to_cert X509_set_pubkey failed\n"); 421 | exit(1); 422 | } 423 | } 424 | 425 | 426 | void copy_subject_to_issuer(X509 *cacrt, X509 *crt) 427 | { 428 | X509_NAME *caname; 429 | 430 | if ((!cacrt) || (!crt)) { 431 | printf("copy_subject_to_issuer invalid params\n"); 432 | exit(1); 433 | } 434 | 435 | caname = X509_get_subject_name(cacrt); 436 | if (!caname) { 437 | printf("copy_subject_to_issuer X509_get_subject_name failed\n"); 438 | exit(1); 439 | } 440 | 441 | printf("ca subject is '%s'\n", X509_NAME_oneline(caname, NULL, 0)); 442 | 443 | if (!X509_set_issuer_name(crt, caname)) { 444 | printf("copy_subject_to_issuer X509_set_issuer_name failed\n"); 445 | exit(1); 446 | } 447 | } 448 | 449 | 450 | int ext_exists(X509 *crt, int nid) 451 | { 452 | int num_exts; 453 | const STACK_OF(X509_EXTENSION) *exts; 454 | X509_EXTENSION *ex; 455 | ASN1_OBJECT *obj; 456 | int i; 457 | 458 | if ((!crt) || (!nid)) { 459 | printf("ext_exists invalid params\n"); 460 | exit(1); 461 | } 462 | 463 | exts = X509_get0_extensions(crt); 464 | if (exts) { 465 | num_exts = sk_X509_EXTENSION_num(exts); 466 | } else { 467 | num_exts = 0; 468 | } 469 | 470 | for (i=0; ilength < 1) { 778 | printf("set_CA_flags key usage length < 1\n"); 779 | exit(1); 780 | } 781 | usage_val = usage->data[0]; 782 | if (usage->length > 1) { 783 | usage_val = usage_val | (usage->data[1] << 8); 784 | } 785 | 786 | if (!(usage_val & X509v3_KU_KEY_CERT_SIGN)) { 787 | /* add key cert sign flag */ 788 | newusage = ASN1_BIT_STRING_new(); 789 | if (!newusage) { 790 | printf("set_CA_flags ASN1_BIT_STRING_new failed\n"); 791 | exit(1); 792 | } 793 | usage_val = usage_val | X509v3_KU_KEY_CERT_SIGN; 794 | newusage_data[0] = (unsigned char) (usage_val & 0xff); 795 | newusage_data[1] = (unsigned char) ((usage_val >> 8) & 0xff); 796 | delete_extension(crt, NID_key_usage); 797 | if (!ASN1_BIT_STRING_set(newusage, newusage_data, 2)) { 798 | printf("set_CA_flags ASN1_BIT_STRING_set failed\n"); 799 | exit(1); 800 | } 801 | add_extension_raw(crt, NID_key_usage, newusage, critical); 802 | } 803 | } else { 804 | /* add a key usage extension */ 805 | newusage = ASN1_BIT_STRING_new(); 806 | if (!newusage) { 807 | printf("set_CA_flags ASN1_BIT_STRING_new failed\n"); 808 | exit(1); 809 | } 810 | usage_val = X509v3_KU_KEY_CERT_SIGN; 811 | newusage_data[0] = (unsigned char) (usage_val & 0xff); 812 | newusage_data[1] = (unsigned char) ((usage_val >> 8) & 0xff); 813 | if (!ASN1_BIT_STRING_set(newusage, newusage_data, 2)) { 814 | printf("set_CA_flags ASN1_BIT_STRING_set failed\n"); 815 | exit(1); 816 | } 817 | add_extension_raw(crt, NID_key_usage, newusage, critical); 818 | } 819 | 820 | break; 821 | default: 822 | printf("Invalid certificate version: %ld\n", ver); 823 | exit(1); 824 | } 825 | } 826 | 827 | 828 | /* clone_certificate */ 829 | EVP_PKEY *clone_certificate(X509 *crt, X509 *issuercrt, EVP_PKEY *issuercertkey, char *outputfile, int depth) 830 | { 831 | char outputcert[256]; 832 | char outputkey[256]; 833 | 834 | EVP_PKEY *newkey = NULL; 835 | int certkeysize = 0; 836 | X509V3_CTX extctx; 837 | int algorithm = 0; 838 | int issuerversion = 0; 839 | 840 | FILE *pemfp; 841 | FILE *keyfp; 842 | 843 | if ((!crt) || (!outputfile) || (depth < 0)) { 844 | printf("clone_certificate invalid params\n"); 845 | exit(1); 846 | } 847 | 848 | /* get issuer certificate version */ 849 | if (issuercrt) { 850 | issuerversion = X509_get_version(issuercrt); 851 | } else { 852 | issuerversion = X509_get_version(crt); 853 | } 854 | 855 | printf("\n### %s ### v%ld\n", X509_NAME_oneline(X509_get_subject_name(crt), NULL, 0), X509_get_version(crt) + 1); 856 | 857 | /* get signing algorithm */ 858 | algorithm = get_signing_algo(crt); 859 | 860 | printf("sig hash type = %d\n", algorithm); 861 | 862 | /* get public key size */ 863 | certkeysize = get_pub_key_size(crt); 864 | 865 | printf("public key size = %d\n", certkeysize); 866 | 867 | /* generate new key */ 868 | newkey = gen_new_key(certkeysize); 869 | 870 | /* assign key to certificate */ 871 | assign_key_to_cert(crt, newkey); 872 | 873 | /* copy the subject name of the issuer to the issuer name of the cert */ 874 | if (issuercrt) { 875 | copy_subject_to_issuer(issuercrt, crt); 876 | } else { 877 | copy_subject_to_issuer(crt, crt); 878 | } 879 | 880 | /* create an extension context linking the CA cert to the subject cert */ 881 | if (issuercrt) { 882 | create_ext_ctx(issuercrt, crt, &extctx); 883 | } else { 884 | create_ext_ctx(crt, crt, &extctx); 885 | } 886 | 887 | /* correct the subject key identifier if it exists */ 888 | if (ext_exists(crt, NID_subject_key_identifier)) { 889 | delete_extension(crt, NID_subject_key_identifier); 890 | add_extension(crt, &extctx, NID_subject_key_identifier, "hash"); 891 | printf("subject key identifier changed\n"); 892 | } 893 | 894 | if (issuerversion == 2) { 895 | 896 | /* correct the authority key identifier if it exists */ 897 | if (ext_exists(crt, NID_authority_key_identifier)) { 898 | delete_extension(crt, NID_authority_key_identifier); 899 | add_extension(crt, &extctx, NID_authority_key_identifier, "keyid:always"); 900 | printf("authority key identifier changed\n"); 901 | } 902 | } 903 | 904 | /* set CA flags for self-signed certs (no issuer) and where depth > 1 (intermediate or CA) */ 905 | if ((!issuercrt) || (depth)) { 906 | set_CA_flags(crt, &extctx); 907 | } 908 | 909 | /* sign the cert */ 910 | if (issuercrt) { 911 | sign_cert(crt, issuercertkey, algorithm); 912 | } else { 913 | sign_cert(crt, newkey, algorithm); 914 | } 915 | 916 | /* create output file names */ 917 | snprintf(outputcert, 256, "%s.pem", outputfile); 918 | snprintf(outputkey, 256, "%s.key", outputfile); 919 | 920 | /* open output files */ 921 | pemfp = open_output_file(outputcert); 922 | keyfp = open_output_file(outputkey); 923 | 924 | /* write the cert */ 925 | write_cert(crt, pemfp); 926 | 927 | /* write the new private key */ 928 | write_key(newkey, keyfp); 929 | 930 | /* close the output files */ 931 | fclose(pemfp); 932 | fclose(keyfp); 933 | 934 | return newkey; 935 | } 936 | 937 | 938 | /* clone_chain */ 939 | EVP_PKEY *clone_chain(X509 *crt, STACK_OF(X509) *ca_stack, X509 *cacrt, EVP_PKEY *cakey, char *outputfile, int depth, char command) 940 | { 941 | EVP_PKEY *key = NULL; 942 | X509_STORE_CTX *storectx = NULL; 943 | X509 *issuer = NULL; 944 | 945 | if ((!crt) || (!ca_stack) || (!outputfile)) { 946 | printf("clone_chain invalid params\n"); 947 | exit(1); 948 | } 949 | 950 | /* create context for chain */ 951 | storectx = X509_STORE_CTX_new(); 952 | if (!storectx) { 953 | printf("clone_chain X509_STORE_CTX_new failed\n"); 954 | exit(1); 955 | } 956 | 957 | if (!X509_STORE_CTX_init(storectx, NULL, crt, ca_stack)) { 958 | printf("clone_chain X509_STORE_init failed\n"); 959 | exit(1); 960 | } 961 | 962 | /* retrieve issuer key */ 963 | issuer = find_issuer(storectx, ca_stack, crt); 964 | 965 | if ((issuer) && (X509_name_cmp(X509_get_subject_name(crt), X509_get_issuer_name(crt)))) { 966 | /* not at end of chain */ 967 | 968 | /* clone rest of chain */ 969 | key = clone_chain(issuer, ca_stack, cacrt, cakey, outputfile, depth + 1, command); 970 | 971 | /* fix up output file name */ 972 | outputfile[strlen(outputfile) - 1] = depth + '0'; 973 | 974 | /* sign this cert and return the key */ 975 | if (key != cakey) { 976 | key = clone_certificate(crt, issuer, key, outputfile, depth); 977 | } else { 978 | key = clone_certificate(crt, cacrt, cakey, outputfile, depth); 979 | } 980 | 981 | } else { 982 | /* end of chain */ 983 | 984 | /* fix up output file name */ 985 | outputfile[strlen(outputfile) - 1] = depth + '0'; 986 | 987 | if (command == 'c') { 988 | /* sign this cert and return the key */ 989 | key = clone_certificate(crt, cacrt, cakey, outputfile, depth); 990 | } else if (command == 'r') { 991 | /* return the ca key */ 992 | key = cakey; 993 | } else { 994 | printf("clone_chain invalid command\n"); 995 | exit(1); 996 | } 997 | } 998 | 999 | return key; 1000 | } 1001 | 1002 | 1003 | 1004 | 1005 | int main(int argc, char *argv[]) 1006 | { 1007 | char c; 1008 | 1009 | char *certfile = NULL; 1010 | char *cacertfile = NULL; 1011 | char *cakeyfile = NULL; 1012 | char *outputfile = NULL; 1013 | char *outputchainfile = NULL; 1014 | 1015 | SSL_CTX *sslctx = NULL; 1016 | SSL *ssl = NULL; 1017 | X509 *crt = NULL; 1018 | STACK_OF(X509) *ca_stack = NULL; 1019 | X509 *tempcert = NULL; 1020 | SSL_CTX *casslctx = NULL; 1021 | SSL *cassl = NULL; 1022 | X509 *cacrt = NULL; 1023 | X509_STORE_CTX *storectx = NULL; 1024 | EVP_PKEY *issuerkey = NULL; 1025 | 1026 | char command = ' '; 1027 | int haveCA = 0; 1028 | 1029 | printf("Jackal v1.0 - Certificate Cloner - K Sheldrake\n"); 1030 | printf("----------------------------------------------\n\n"); 1031 | printf("Jackal comes with ABSOLUTELY NO WARRANTY.\n"); 1032 | printf("This is free software, and you are welcome\n"); 1033 | printf("to redistribute it under certain conditions.\n"); 1034 | printf("Jackal is provided under GPLv2.\n"); 1035 | printf("See http://www.gnu.org/licenses/gpl-2.0.html\n"); 1036 | printf("for details.\n\n"); 1037 | printf("\"Certainly, the Jackal masqueraded as an Englishman,\n"); 1038 | printf("but he also masqueraded as a Dane and as a Frenchman.\n"); 1039 | printf("So there's no way of proving his identity at all.\"\n"); 1040 | printf(" - The Day of the Jackal\n\n\n"); 1041 | 1042 | /* sort options */ 1043 | opterr = 0; 1044 | 1045 | while((c = getopt(argc, argv, "hc:C:K:o:s:")) != -1) { 1046 | switch(c) { 1047 | case 'h': 1048 | usage(NULL); 1049 | break; 1050 | case 'c': 1051 | if (strlen(optarg) > 0) { 1052 | certfile = optarg; 1053 | } 1054 | break; 1055 | case 'C': 1056 | if (strlen(optarg) > 0) { 1057 | cacertfile = optarg; 1058 | } 1059 | break; 1060 | case 'K': 1061 | if (strlen(optarg) > 0) { 1062 | cakeyfile = optarg; 1063 | } 1064 | break; 1065 | case 'o': 1066 | if (strlen(optarg) > 0) { 1067 | outputfile = optarg; 1068 | } 1069 | break; 1070 | case 's': 1071 | if (strlen(optarg) == 1) { 1072 | command = *optarg; 1073 | } 1074 | break; 1075 | default: 1076 | usage("unknown option"); 1077 | } 1078 | } 1079 | 1080 | if (!certfile) { 1081 | usage(NULL); 1082 | } 1083 | 1084 | haveCA = 0; 1085 | if ((cacertfile) && (cakeyfile)) { 1086 | haveCA = 1; 1087 | } else if ((cacertfile) || (cakeyfile)) { 1088 | usage("Specify either both -C and -K, or specify neither"); 1089 | } 1090 | 1091 | if ((command == ' ') && ((haveCA) || (outputfile))) { 1092 | usage("You need to specify a command wtih -s"); 1093 | } 1094 | 1095 | if (! strchr(" lcr", command)) { 1096 | usage("Command must be -sl, -sc, -sr or unspecified to print certificate details"); 1097 | } 1098 | 1099 | if ((strchr("lcr", command)) && (!outputfile)) { 1100 | usage("You must specify an output file specification"); 1101 | } 1102 | 1103 | if ((command == 'r') && (!haveCA)) { 1104 | usage("You need to specify a CA with -sr\n"); 1105 | } 1106 | 1107 | /* set up openssl */ 1108 | SSL_load_error_strings(); 1109 | ERR_load_BIO_strings(); 1110 | SSL_library_init(); 1111 | 1112 | /* open certificate file */ 1113 | get_cert_from_file(&sslctx, &ssl, &crt, certfile); 1114 | 1115 | if (haveCA) { 1116 | /* open CA certificate file */ 1117 | get_cert_from_file(&casslctx, &cassl, &cacrt, cacertfile); 1118 | 1119 | /* read CA key file */ 1120 | get_key_from_file(cassl, cakeyfile); 1121 | 1122 | issuerkey = get_private_key(cassl); 1123 | } 1124 | 1125 | /* get any extra certs */ 1126 | ca_stack = get_extra_certs(sslctx); 1127 | 1128 | /* if only a certificate file, display and exit */ 1129 | if (command == ' ') { 1130 | print_cert(crt); 1131 | 1132 | if (ca_stack) { 1133 | storectx = X509_STORE_CTX_new(); 1134 | if (!storectx) { 1135 | printf("main X509_STORE_CTX_new failed\n"); 1136 | exit(1); 1137 | } 1138 | 1139 | if (!X509_STORE_CTX_init(storectx, NULL, crt, ca_stack)) { 1140 | printf("main X509_STORE_init failed\n"); 1141 | exit(1); 1142 | } 1143 | 1144 | tempcert = crt; 1145 | 1146 | while (tempcert) { 1147 | printf("%s\n", X509_NAME_oneline(X509_get_subject_name(tempcert), NULL, 0)); 1148 | if (!X509_name_cmp(X509_get_subject_name(tempcert), X509_get_issuer_name(tempcert))) { 1149 | tempcert = NULL; 1150 | } else { 1151 | tempcert = find_issuer(storectx, ca_stack, tempcert); 1152 | } 1153 | } 1154 | 1155 | } 1156 | 1157 | exit(0); 1158 | } 1159 | 1160 | switch(command) { 1161 | case 'l': 1162 | if (haveCA) { 1163 | clone_certificate(crt, cacrt, issuerkey, outputfile, 0); 1164 | } else { 1165 | clone_certificate(crt, NULL, NULL, outputfile, 0); 1166 | } 1167 | break; 1168 | case 'c': 1169 | case 'r': 1170 | outputchainfile = malloc(strlen(outputfile) + 3); 1171 | if (!outputchainfile) { 1172 | printf("main malloc error\n"); 1173 | exit(1); 1174 | } 1175 | sprintf(outputchainfile, "%s.1", outputfile); 1176 | if (haveCA) { 1177 | if (!clone_chain(crt, ca_stack, cacrt, issuerkey, outputchainfile, 0, command)) { 1178 | printf("main clone_chain failed\n"); 1179 | exit(1); 1180 | } 1181 | } else { 1182 | if (!clone_chain(crt, ca_stack, NULL, NULL, outputchainfile, 0, command)) { 1183 | printf("main clone_chain failed\n"); 1184 | exit(1); 1185 | } 1186 | } 1187 | break; 1188 | 1189 | } 1190 | 1191 | 1192 | printf("\nSuccess!\n\n"); 1193 | 1194 | return 0; 1195 | 1196 | } 1197 | 1198 | 1199 | -------------------------------------------------------------------------------- /jackal.1.3.c: -------------------------------------------------------------------------------- 1 | /* jackal - certificate cloner - K Sheldrake 2 | ** 3 | ** Copyright (C) 2018 Kevin Sheldrake 4 | ** 5 | ** This file is part of jackal. 6 | ** 7 | ** Jackal is free software; you can redistribute it and/or 8 | ** modify it under the terms of the GNU General Public License 9 | ** as published by the Free Software Foundation; either version 2 10 | ** of the License, or (at your option) any later version. 11 | ** 12 | ** This program is distributed in the hope that it will be useful, 13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ** GNU General Public License for more details. 16 | ** 17 | ** You should have received a copy of the GNU General Public License 18 | ** along with this program; if not, write to the Free Software 19 | ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | ** 21 | ** 22 | ** jackal clones SSL certificates. The purpose is to automate and simplify a step in the SSL MITM process. 23 | ** 24 | ** SSL/TLS connections are established on trust provided by the certificates that are exchanged. Typically 25 | ** the server sends a certificate to the client for verification. All certificates are signed by another 26 | ** certificate. Self-signed certificates are signed by themselves; in all other cases, an end user cerficate 27 | ** is signed by an intermediate certificate authority, which is in-turn signed by other intermediates, the 28 | ** last of which is signed by a root certificate authority. The root certificate authority is self-signed 29 | ** and should be available to the tool or client that wants to verify the end user certificate. 30 | ** 31 | ** To verify an end user certificate a tool or client finds the certificate that signed it, and then recursively 32 | ** verifies that. If any of the certificates are contained in the CA store, the certificate is deemed to have 33 | ** a valid signature. There are other checks, such as validity dates and confirmation of the end user 34 | ** certificate details to ensure the right certificate is in use. 35 | ** 36 | ** Man-in-the-middle attacks are possible if the certificate verification routines are flawed or if it is 37 | ** possible to add a fake CA to the CA store. In order to effect such an attack, certificates that mimic 38 | ** the real certificates are required. This tool, jackal, makes fake certificates that are identical to 39 | ** the originals except for the change of keys. 40 | ** 41 | ** Create a new CA with: 42 | ** % openssl genrsa -des3 -out ca.key 2048 43 | ** % openssl req -new -x509 -days 3650 -key ca.key -out ca.pem 44 | ** 45 | ** Check certificate with: 46 | ** % openssl x509 -in certificate.pem -noout -text 47 | ** 48 | ** Verify certificate trust with: 49 | ** % openssl verify -CAfile ca.pem -untrusted intermediatecas.pem certificate.pem 50 | ** 51 | ** 52 | ** Compile with: 53 | ** cc -o jackal jackal.c -lssl -lcrypto 54 | ** 55 | ** History: 56 | ** 57 | ** v1.3 - 25/1/2018 58 | ** 59 | ** Edited the comments and display messages to reflect the version number correctly. 60 | ** 61 | ** v1.2 - 6/12/2017 62 | ** 63 | ** (Hopefully) (working) support for OpenSSL 1.1.0. 64 | ** 65 | ** v1.1 - 24/10/2017 66 | ** 67 | ** Attempt at port to OpenSSL 1.1.0. (This didn't really work very well.) 68 | ** 69 | ** v1.0 - 8/11/2014 70 | ** 71 | ** Uplifted to v1.0 for release. 72 | ** Tidied welcome message. 73 | ** Added quote from The Day of the Jackal. :) 74 | ** 75 | ** v0.3 - 8/11/2014 76 | ** 77 | ** Now clones whole certificate chains. 78 | ** 79 | ** v0.2 - 30/10/2014 80 | ** 81 | ** Rewrite of version 1, same functionality. 82 | ** 83 | ** v0.1 - 30/10/2014 84 | ** 85 | ** Proof of concept. 86 | ** Takes one certificate and one CA and makes a new certificate with a new key that matches the original 87 | ** and is signed by the supplied CA. 88 | ** 89 | */ 90 | 91 | #include 92 | #include 93 | #include 94 | #include 95 | #include 96 | 97 | #include 98 | #include 99 | #include 100 | #include 101 | #include 102 | 103 | #define HEX_PER_ROW 16 104 | #define ALGO_LEN 64 105 | #define OBJSTR_LEN 1024 106 | 107 | void usage(char *msg) 108 | { 109 | printf("ONLY USE FOR TESTING (don't be a muppet)\n\n"); 110 | printf("jackal -c certificate\n"); 111 | printf(" displays certificate details\n"); 112 | printf("jackal -sl -c certificate(s) -o outputfilespec [-C newCAcert -K newCAkey]\n"); 113 | printf(" signs the leaf certificate with the CA if specified or self-signs it if not\n"); 114 | printf("jackal -sc -c certificates -o outputfilespec [-C newCAcert -K newCAkey]\n"); 115 | printf(" signs the certificate chain with the CA if specified or self-signs the root if not\n"); 116 | printf("jackal -sr -c certificates -o outputfilesepc -C newCAcert -K newCAkey\n"); 117 | printf(" signs the certificate chain, replacing the current root with the specified CA\n"); 118 | printf("\n"); 119 | printf("outputfilespec will be appended with .pem/.key with -sl and .n.pem/.key with -sc/r\n"); 120 | printf("\n"); 121 | 122 | if (msg) { 123 | printf("%s\n", msg); 124 | exit(1); 125 | } 126 | 127 | exit(0); 128 | } 129 | 130 | 131 | void hexdump(unsigned char *data, int data_len, int indent) 132 | { 133 | int i, j; 134 | 135 | for (i=0; idata, ALGO_LEN); 202 | notBefore[ALGO_LEN - 1] = 0x00; 203 | time = X509_get_notAfter(cert); 204 | strncpy(notAfter, (const char *)time->data, ALGO_LEN); 205 | notAfter[ALGO_LEN - 1] = 0x00; 206 | 207 | printf("Certificate:\n"); 208 | printf(" Data:\n"); 209 | printf(" Version: 0x%0lx\n", X509_get_version(cert)); 210 | printf(" Serial Number:\n"); 211 | hexdump((unsigned char *)serialnumber, seriallength, 3); 212 | printf(" Signature Algorithm: %s\n", algorithm); 213 | printf(" Issuer: %s\n", issuer); 214 | printf(" Validity\n"); 215 | printf(" Not Before: %s\n", notBefore); 216 | printf(" Not After : %s\n", notAfter); 217 | printf(" Subject: %s\n", subject); 218 | printf(" Subject Public Key Info:\n"); 219 | printf(" Public Key Algorithm: %s\n", pubalgo); 220 | printf(" Public-Key: (%d bit)\n", EVP_PKEY_bits(certkey)); 221 | printf(" Modulus:%s\n", modulus); 222 | printf(" Signature Algorithm: %s\n", algorithm); 223 | hexdump((unsigned char *)hash, hashlength, 2); 224 | 225 | } 226 | 227 | 228 | X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x) 229 | { 230 | int i; 231 | X509 *issuer; 232 | X509_STORE_CTX_check_issued_fn check_issued; 233 | 234 | if ((!ctx) || (!sk) || (!x)) { 235 | printf("find_issuer invalid params\n"); 236 | exit(1); 237 | } 238 | 239 | check_issued = X509_STORE_CTX_get_check_issued(ctx); 240 | 241 | for (i=0; i < sk_X509_num(sk); i++) { 242 | issuer = sk_X509_value(sk, i); 243 | if (check_issued(ctx, x, issuer)) 244 | return issuer; 245 | } 246 | return NULL; 247 | } 248 | 249 | 250 | void get_cert_from_file(SSL_CTX **sslctx, SSL **ssl, X509 **crt, char *certfile) 251 | { 252 | if ((!sslctx) || (!ssl) || (!crt) || (!certfile)) { 253 | printf("get_cert_from_file invalid params\n"); 254 | exit(1); 255 | } 256 | 257 | /* open certificate file */ 258 | *sslctx = SSL_CTX_new(TLS_server_method()); 259 | if (!*sslctx) { 260 | printf("get_cert_from_file SSL_CTX_new failed\n"); 261 | exit(1); 262 | } 263 | if (!SSL_CTX_use_certificate_chain_file(*sslctx, certfile)) { 264 | printf("get_cert_from_file SSL_CTX_use_chain_certificate_file failed\n"); 265 | exit(1); 266 | } 267 | 268 | *ssl = SSL_new(*sslctx); 269 | if (!*ssl) { 270 | printf("get_cert_from_file SSL_new failed\n"); 271 | exit(1); 272 | } 273 | *crt = SSL_get_certificate(*ssl); 274 | if (!*crt) { 275 | printf("get_cert_from_file SSL_get_certifcate failed\n"); 276 | exit(1); 277 | } 278 | } 279 | 280 | STACK_OF(X509) *get_extra_certs(SSL_CTX *sslctx) 281 | { 282 | STACK_OF(X509) *ca_stack; 283 | 284 | if (!sslctx) { 285 | printf("get_extra_certs invalid params\n"); 286 | exit(1); 287 | } 288 | 289 | if (SSL_CTX_get_extra_chain_certs(sslctx, &ca_stack)) { 290 | // printf("SSL_CTX_get_extra_chain_certs_only success\n"); 291 | } else { 292 | printf("SSL_CTX_get_extra_chain_certs_only failed\n"); 293 | exit(0); 294 | } 295 | 296 | if (!ca_stack) { 297 | printf("Cert file contains 1 certificate\n"); 298 | } else { 299 | printf("Cert file contains >1 certificates\n"); 300 | } 301 | 302 | return ca_stack; 303 | } 304 | 305 | 306 | void get_key_from_file(SSL *cassl, char *cakeyfile) 307 | { 308 | if ((!cassl) || (!cakeyfile)) { 309 | printf("get_key_from_file invalid params\n"); 310 | exit(1); 311 | } 312 | 313 | printf("Loading CA key\n"); 314 | 315 | /* read CA key file */ 316 | if (!SSL_use_PrivateKey_file(cassl, cakeyfile, SSL_FILETYPE_PEM)) { 317 | printf("get_key_from_file SSL_use_PrivateKey_file failed\n"); 318 | exit(1); 319 | } 320 | } 321 | 322 | 323 | int get_signing_algo(X509 *crt) 324 | { 325 | int nid; 326 | 327 | if (!crt) { 328 | printf("get_signing_algo invalid params\n"); 329 | exit(1); 330 | } 331 | 332 | /* get signing algorithm */ 333 | nid = X509_get_signature_nid(crt); 334 | if (!nid) { 335 | printf("get_signing_algo OBJ_obj2nid failed\n"); 336 | exit(1); 337 | } 338 | 339 | return nid; 340 | } 341 | 342 | 343 | const char *get_public_algo(X509 *crt) 344 | { 345 | const char *pubalgo = OPENSSL_malloc(ALGO_LEN); 346 | EVP_PKEY *certkey; 347 | 348 | if (!crt) { 349 | printf("get_public_algo invalid params\n"); 350 | exit(1); 351 | } 352 | 353 | if (!pubalgo) { 354 | printf("get_public_algo malloc error\n"); 355 | exit(1); 356 | } 357 | 358 | certkey = X509_get_pubkey(crt); 359 | pubalgo = OBJ_nid2ln(EVP_PKEY_base_id(certkey)); 360 | 361 | return pubalgo; 362 | } 363 | 364 | 365 | int get_pub_key_size(X509 *crt) 366 | { 367 | EVP_PKEY *certkey; 368 | 369 | if (!crt) { 370 | printf("get_pub_key_size invalid params\n"); 371 | exit(1); 372 | } 373 | 374 | certkey = X509_get_pubkey(crt); 375 | 376 | if (!certkey) { 377 | printf("get_pub_key_size X509_get_pubkey failed\n"); 378 | exit(1); 379 | } 380 | 381 | return EVP_PKEY_bits(certkey); 382 | } 383 | 384 | 385 | EVP_PKEY *gen_new_key(int certkeysize) 386 | { 387 | RSA *newrsa = NULL; 388 | EVP_PKEY *newkey = NULL; 389 | BIGNUM *e = NULL; 390 | 391 | if (certkeysize <= 0) { 392 | printf("gen_new_key invalid params\n"); 393 | exit(1); 394 | } 395 | 396 | if (!BN_dec2bn(&e, "65537")) { 397 | printf("gen_new_key BN_dec2bn failed\n"); 398 | exit(1); 399 | } 400 | newrsa = RSA_new(); 401 | if (!newrsa) { 402 | printf("gen_new_key RSA_new failed\n"); 403 | exit(1); 404 | } 405 | if (!RSA_generate_key_ex(newrsa, certkeysize, e, 0)) { 406 | printf("gen_new_key RSA_generate_key_ex failed\n"); 407 | exit(1); 408 | } 409 | newkey = EVP_PKEY_new(); 410 | if (!newkey) { 411 | printf("gen_new_key EVP_PKEY_new failed\n"); 412 | exit(1); 413 | } 414 | 415 | if (!EVP_PKEY_assign_RSA(newkey, newrsa)) { 416 | printf("gen_new_key EVP_PKEY_assign_RSA failed\n"); 417 | exit(1); 418 | } 419 | 420 | return newkey; 421 | } 422 | 423 | 424 | void assign_key_to_cert(X509 *crt, EVP_PKEY *newkey) 425 | { 426 | if ((!crt) || (!newkey)) { 427 | printf("assign_key_to_cert invalid params\n"); 428 | exit(1); 429 | } 430 | 431 | if (!X509_set_pubkey(crt, newkey)) { 432 | printf("assign_key_to_cert X509_set_pubkey failed\n"); 433 | exit(1); 434 | } 435 | } 436 | 437 | 438 | void copy_subject_to_issuer(X509 *cacrt, X509 *crt) 439 | { 440 | X509_NAME *caname; 441 | 442 | if ((!cacrt) || (!crt)) { 443 | printf("copy_subject_to_issuer invalid params\n"); 444 | exit(1); 445 | } 446 | 447 | caname = X509_get_subject_name(cacrt); 448 | if (!caname) { 449 | printf("copy_subject_to_issuer X509_get_subject_name failed\n"); 450 | exit(1); 451 | } 452 | 453 | printf("ca subject is '%s'\n", X509_NAME_oneline(caname, NULL, 0)); 454 | 455 | if (!X509_set_issuer_name(crt, caname)) { 456 | printf("copy_subject_to_issuer X509_set_issuer_name failed\n"); 457 | exit(1); 458 | } 459 | } 460 | 461 | 462 | int ext_exists(X509 *crt, int nid) 463 | { 464 | int num_exts; 465 | const STACK_OF(X509_EXTENSION) *exts; 466 | X509_EXTENSION *ex; 467 | ASN1_OBJECT *obj; 468 | int i; 469 | 470 | if ((!crt) || (!nid)) { 471 | printf("ext_exists invalid params\n"); 472 | exit(1); 473 | } 474 | 475 | exts = X509_get0_extensions(crt); 476 | if (exts) { 477 | num_exts = sk_X509_EXTENSION_num(exts); 478 | } else { 479 | num_exts = 0; 480 | } 481 | 482 | for (i=0; ilength < 1) { 790 | printf("set_CA_flags key usage length < 1\n"); 791 | exit(1); 792 | } 793 | usage_val = usage->data[0]; 794 | if (usage->length > 1) { 795 | usage_val = usage_val | (usage->data[1] << 8); 796 | } 797 | 798 | if (!(usage_val & X509v3_KU_KEY_CERT_SIGN)) { 799 | /* add key cert sign flag */ 800 | newusage = ASN1_BIT_STRING_new(); 801 | if (!newusage) { 802 | printf("set_CA_flags ASN1_BIT_STRING_new failed\n"); 803 | exit(1); 804 | } 805 | usage_val = usage_val | X509v3_KU_KEY_CERT_SIGN; 806 | newusage_data[0] = (unsigned char) (usage_val & 0xff); 807 | newusage_data[1] = (unsigned char) ((usage_val >> 8) & 0xff); 808 | delete_extension(crt, NID_key_usage); 809 | if (!ASN1_BIT_STRING_set(newusage, newusage_data, 2)) { 810 | printf("set_CA_flags ASN1_BIT_STRING_set failed\n"); 811 | exit(1); 812 | } 813 | add_extension_raw(crt, NID_key_usage, newusage, critical); 814 | } 815 | } else { 816 | /* add a key usage extension */ 817 | newusage = ASN1_BIT_STRING_new(); 818 | if (!newusage) { 819 | printf("set_CA_flags ASN1_BIT_STRING_new failed\n"); 820 | exit(1); 821 | } 822 | usage_val = X509v3_KU_KEY_CERT_SIGN; 823 | newusage_data[0] = (unsigned char) (usage_val & 0xff); 824 | newusage_data[1] = (unsigned char) ((usage_val >> 8) & 0xff); 825 | if (!ASN1_BIT_STRING_set(newusage, newusage_data, 2)) { 826 | printf("set_CA_flags ASN1_BIT_STRING_set failed\n"); 827 | exit(1); 828 | } 829 | add_extension_raw(crt, NID_key_usage, newusage, critical); 830 | } 831 | 832 | break; 833 | default: 834 | printf("Invalid certificate version: %ld\n", ver); 835 | exit(1); 836 | } 837 | } 838 | 839 | 840 | /* clone_certificate */ 841 | EVP_PKEY *clone_certificate(X509 *crt, X509 *issuercrt, EVP_PKEY *issuercertkey, char *outputfile, int depth) 842 | { 843 | char outputcert[256]; 844 | char outputkey[256]; 845 | 846 | EVP_PKEY *newkey = NULL; 847 | int certkeysize = 0; 848 | X509V3_CTX extctx; 849 | int algorithm = 0; 850 | int issuerversion = 0; 851 | 852 | FILE *pemfp; 853 | FILE *keyfp; 854 | 855 | if ((!crt) || (!outputfile) || (depth < 0)) { 856 | printf("clone_certificate invalid params\n"); 857 | exit(1); 858 | } 859 | 860 | /* get issuer certificate version */ 861 | if (issuercrt) { 862 | issuerversion = X509_get_version(issuercrt); 863 | } else { 864 | issuerversion = X509_get_version(crt); 865 | } 866 | 867 | printf("\n### %s ### v%ld\n", X509_NAME_oneline(X509_get_subject_name(crt), NULL, 0), X509_get_version(crt) + 1); 868 | 869 | /* get signing algorithm */ 870 | algorithm = get_signing_algo(crt); 871 | 872 | printf("sig hash type = %d\n", algorithm); 873 | 874 | /* get public key size */ 875 | certkeysize = get_pub_key_size(crt); 876 | 877 | printf("public key size = %d\n", certkeysize); 878 | 879 | /* generate new key */ 880 | newkey = gen_new_key(certkeysize); 881 | 882 | /* assign key to certificate */ 883 | assign_key_to_cert(crt, newkey); 884 | 885 | /* copy the subject name of the issuer to the issuer name of the cert */ 886 | if (issuercrt) { 887 | copy_subject_to_issuer(issuercrt, crt); 888 | } else { 889 | copy_subject_to_issuer(crt, crt); 890 | } 891 | 892 | /* create an extension context linking the CA cert to the subject cert */ 893 | if (issuercrt) { 894 | create_ext_ctx(issuercrt, crt, &extctx); 895 | } else { 896 | create_ext_ctx(crt, crt, &extctx); 897 | } 898 | 899 | /* correct the subject key identifier if it exists */ 900 | if (ext_exists(crt, NID_subject_key_identifier)) { 901 | delete_extension(crt, NID_subject_key_identifier); 902 | add_extension(crt, &extctx, NID_subject_key_identifier, "hash"); 903 | printf("subject key identifier changed\n"); 904 | } 905 | 906 | if (issuerversion == 2) { 907 | 908 | /* correct the authority key identifier if it exists */ 909 | if (ext_exists(crt, NID_authority_key_identifier)) { 910 | delete_extension(crt, NID_authority_key_identifier); 911 | add_extension(crt, &extctx, NID_authority_key_identifier, "keyid:always"); 912 | printf("authority key identifier changed\n"); 913 | } 914 | } 915 | 916 | /* set CA flags for self-signed certs (no issuer) and where depth > 1 (intermediate or CA) */ 917 | if ((!issuercrt) || (depth)) { 918 | set_CA_flags(crt, &extctx); 919 | } 920 | 921 | /* sign the cert */ 922 | if (issuercrt) { 923 | sign_cert(crt, issuercertkey, algorithm); 924 | } else { 925 | sign_cert(crt, newkey, algorithm); 926 | } 927 | 928 | /* create output file names */ 929 | snprintf(outputcert, 256, "%s.pem", outputfile); 930 | snprintf(outputkey, 256, "%s.key", outputfile); 931 | 932 | /* open output files */ 933 | pemfp = open_output_file(outputcert); 934 | keyfp = open_output_file(outputkey); 935 | 936 | /* write the cert */ 937 | write_cert(crt, pemfp); 938 | 939 | /* write the new private key */ 940 | write_key(newkey, keyfp); 941 | 942 | /* close the output files */ 943 | fclose(pemfp); 944 | fclose(keyfp); 945 | 946 | return newkey; 947 | } 948 | 949 | 950 | /* clone_chain */ 951 | EVP_PKEY *clone_chain(X509 *crt, STACK_OF(X509) *ca_stack, X509 *cacrt, EVP_PKEY *cakey, char *outputfile, int depth, char command) 952 | { 953 | EVP_PKEY *key = NULL; 954 | X509_STORE_CTX *storectx = NULL; 955 | X509 *issuer = NULL; 956 | 957 | if ((!crt) || (!ca_stack) || (!outputfile)) { 958 | printf("clone_chain invalid params\n"); 959 | exit(1); 960 | } 961 | 962 | /* create context for chain */ 963 | storectx = X509_STORE_CTX_new(); 964 | if (!storectx) { 965 | printf("clone_chain X509_STORE_CTX_new failed\n"); 966 | exit(1); 967 | } 968 | 969 | if (!X509_STORE_CTX_init(storectx, NULL, crt, ca_stack)) { 970 | printf("clone_chain X509_STORE_init failed\n"); 971 | exit(1); 972 | } 973 | 974 | /* retrieve issuer key */ 975 | issuer = find_issuer(storectx, ca_stack, crt); 976 | 977 | if ((issuer) && (X509_name_cmp(X509_get_subject_name(crt), X509_get_issuer_name(crt)))) { 978 | /* not at end of chain */ 979 | 980 | /* clone rest of chain */ 981 | key = clone_chain(issuer, ca_stack, cacrt, cakey, outputfile, depth + 1, command); 982 | 983 | /* fix up output file name */ 984 | outputfile[strlen(outputfile) - 1] = depth + '0'; 985 | 986 | /* sign this cert and return the key */ 987 | if (key != cakey) { 988 | key = clone_certificate(crt, issuer, key, outputfile, depth); 989 | } else { 990 | key = clone_certificate(crt, cacrt, cakey, outputfile, depth); 991 | } 992 | 993 | } else { 994 | /* end of chain */ 995 | 996 | /* fix up output file name */ 997 | outputfile[strlen(outputfile) - 1] = depth + '0'; 998 | 999 | if (command == 'c') { 1000 | /* sign this cert and return the key */ 1001 | key = clone_certificate(crt, cacrt, cakey, outputfile, depth); 1002 | } else if (command == 'r') { 1003 | /* return the ca key */ 1004 | key = cakey; 1005 | } else { 1006 | printf("clone_chain invalid command\n"); 1007 | exit(1); 1008 | } 1009 | } 1010 | 1011 | return key; 1012 | } 1013 | 1014 | 1015 | 1016 | 1017 | int main(int argc, char *argv[]) 1018 | { 1019 | char c; 1020 | 1021 | char *certfile = NULL; 1022 | char *cacertfile = NULL; 1023 | char *cakeyfile = NULL; 1024 | char *outputfile = NULL; 1025 | char *outputchainfile = NULL; 1026 | 1027 | SSL_CTX *sslctx = NULL; 1028 | SSL *ssl = NULL; 1029 | X509 *crt = NULL; 1030 | STACK_OF(X509) *ca_stack = NULL; 1031 | X509 *tempcert = NULL; 1032 | SSL_CTX *casslctx = NULL; 1033 | SSL *cassl = NULL; 1034 | X509 *cacrt = NULL; 1035 | X509_STORE_CTX *storectx = NULL; 1036 | EVP_PKEY *issuerkey = NULL; 1037 | 1038 | char command = ' '; 1039 | int haveCA = 0; 1040 | 1041 | printf("Jackal v1.3 - Certificate Cloner - K Sheldrake\n"); 1042 | printf("----------------------------------------------\n\n"); 1043 | printf("Jackal comes with ABSOLUTELY NO WARRANTY.\n"); 1044 | printf("This is free software, and you are welcome\n"); 1045 | printf("to redistribute it under certain conditions.\n"); 1046 | printf("Jackal is provided under GPLv2.\n"); 1047 | printf("See http://www.gnu.org/licenses/gpl-2.0.html\n"); 1048 | printf("for details.\n\n"); 1049 | printf("\"Certainly, the Jackal masqueraded as an Englishman,\n"); 1050 | printf("but he also masqueraded as a Dane and as a Frenchman.\n"); 1051 | printf("So there's no way of proving his identity at all.\"\n"); 1052 | printf(" - The Day of the Jackal\n\n"); 1053 | printf("rtfc.org.uk / github/rtfcode/jackal\n\n\n"); 1054 | 1055 | /* sort options */ 1056 | opterr = 0; 1057 | 1058 | while((c = getopt(argc, argv, "hc:C:K:o:s:")) != -1) { 1059 | switch(c) { 1060 | case 'h': 1061 | usage(NULL); 1062 | break; 1063 | case 'c': 1064 | if (strlen(optarg) > 0) { 1065 | certfile = optarg; 1066 | } 1067 | break; 1068 | case 'C': 1069 | if (strlen(optarg) > 0) { 1070 | cacertfile = optarg; 1071 | } 1072 | break; 1073 | case 'K': 1074 | if (strlen(optarg) > 0) { 1075 | cakeyfile = optarg; 1076 | } 1077 | break; 1078 | case 'o': 1079 | if (strlen(optarg) > 0) { 1080 | outputfile = optarg; 1081 | } 1082 | break; 1083 | case 's': 1084 | if (strlen(optarg) == 1) { 1085 | command = *optarg; 1086 | } 1087 | break; 1088 | default: 1089 | usage("unknown option"); 1090 | } 1091 | } 1092 | 1093 | if (!certfile) { 1094 | usage(NULL); 1095 | } 1096 | 1097 | haveCA = 0; 1098 | if ((cacertfile) && (cakeyfile)) { 1099 | haveCA = 1; 1100 | } else if ((cacertfile) || (cakeyfile)) { 1101 | usage("Specify either both -C and -K, or specify neither"); 1102 | } 1103 | 1104 | if ((command == ' ') && ((haveCA) || (outputfile))) { 1105 | usage("You need to specify a command wtih -s"); 1106 | } 1107 | 1108 | if (! strchr(" lcr", command)) { 1109 | usage("Command must be -sl, -sc, -sr or unspecified to print certificate details"); 1110 | } 1111 | 1112 | if ((strchr("lcr", command)) && (!outputfile)) { 1113 | usage("You must specify an output file specification"); 1114 | } 1115 | 1116 | if ((command == 'r') && (!haveCA)) { 1117 | usage("You need to specify a CA with -sr\n"); 1118 | } 1119 | 1120 | /* set up openssl */ 1121 | SSL_load_error_strings(); 1122 | ERR_load_BIO_strings(); 1123 | SSL_library_init(); 1124 | 1125 | /* open certificate file */ 1126 | get_cert_from_file(&sslctx, &ssl, &crt, certfile); 1127 | 1128 | if (haveCA) { 1129 | /* open CA certificate file */ 1130 | get_cert_from_file(&casslctx, &cassl, &cacrt, cacertfile); 1131 | 1132 | /* read CA key file */ 1133 | get_key_from_file(cassl, cakeyfile); 1134 | 1135 | issuerkey = get_private_key(cassl); 1136 | } 1137 | 1138 | /* get any extra certs */ 1139 | ca_stack = get_extra_certs(sslctx); 1140 | 1141 | /* if only a certificate file, display and exit */ 1142 | if (command == ' ') { 1143 | print_cert(crt); 1144 | 1145 | if (ca_stack) { 1146 | storectx = X509_STORE_CTX_new(); 1147 | if (!storectx) { 1148 | printf("main X509_STORE_CTX_new failed\n"); 1149 | exit(1); 1150 | } 1151 | 1152 | if (!X509_STORE_CTX_init(storectx, NULL, crt, ca_stack)) { 1153 | printf("main X509_STORE_init failed\n"); 1154 | exit(1); 1155 | } 1156 | 1157 | tempcert = crt; 1158 | 1159 | while (tempcert) { 1160 | printf("%s\n", X509_NAME_oneline(X509_get_subject_name(tempcert), NULL, 0)); 1161 | if (!X509_name_cmp(X509_get_subject_name(tempcert), X509_get_issuer_name(tempcert))) { 1162 | tempcert = NULL; 1163 | } else { 1164 | tempcert = find_issuer(storectx, ca_stack, tempcert); 1165 | } 1166 | } 1167 | 1168 | } 1169 | 1170 | exit(0); 1171 | } 1172 | 1173 | switch(command) { 1174 | case 'l': 1175 | if (haveCA) { 1176 | clone_certificate(crt, cacrt, issuerkey, outputfile, 0); 1177 | } else { 1178 | clone_certificate(crt, NULL, NULL, outputfile, 0); 1179 | } 1180 | break; 1181 | case 'c': 1182 | case 'r': 1183 | outputchainfile = malloc(strlen(outputfile) + 3); 1184 | if (!outputchainfile) { 1185 | printf("main malloc error\n"); 1186 | exit(1); 1187 | } 1188 | sprintf(outputchainfile, "%s.1", outputfile); 1189 | if (haveCA) { 1190 | if (!clone_chain(crt, ca_stack, cacrt, issuerkey, outputchainfile, 0, command)) { 1191 | printf("main clone_chain failed\n"); 1192 | exit(1); 1193 | } 1194 | } else { 1195 | if (!clone_chain(crt, ca_stack, NULL, NULL, outputchainfile, 0, command)) { 1196 | printf("main clone_chain failed\n"); 1197 | exit(1); 1198 | } 1199 | } 1200 | break; 1201 | 1202 | } 1203 | 1204 | 1205 | printf("\nSuccess!\n\n"); 1206 | 1207 | return 0; 1208 | 1209 | } 1210 | 1211 | 1212 | --------------------------------------------------------------------------------