├── .gitignore ├── .proverc ├── .travis.yml ├── Build.PL ├── Changes ├── LICENSE ├── META.json ├── README.md ├── cpanfile ├── lib └── JSON │ ├── WebToken.pm │ └── WebToken │ ├── Constants.pm │ ├── Crypt.pm │ ├── Crypt │ ├── HMAC.pm │ └── RSA.pm │ └── Exception.pm ├── minil.toml ├── t ├── 00_compile.t ├── 01_basic.t ├── 02_rsa.t ├── 03_add_signing_algorithm.t ├── 04_secret_cb.t ├── 99_exception.t ├── Util.pm └── spec │ ├── draft-ietf-jose-json-web-signature-08-A1.hmac_sha256.t │ ├── draft-ietf-jose-json-web-signature-08-A2.rsa_sha256.t │ └── draft-ietf-oauth-json-web-token-06-3.1.example.t └── tmp ├── MEMO └── spec.t /.gitignore: -------------------------------------------------------------------------------- 1 | cover_db 2 | MYMETA.* 3 | META.yml 4 | Makefile 5 | blib 6 | inc 7 | pm_to_blib 8 | MANIFEST 9 | MANIFEST.bak 10 | Makefile.old 11 | nytprof* 12 | ppport.h 13 | xs/*c 14 | xs/*o 15 | xs/*obj 16 | *.bs 17 | *.def 18 | *.old 19 | dll* 20 | *~ 21 | inc/ 22 | *.sw[po] 23 | *.bak 24 | Build 25 | _build/ 26 | xshelper.h 27 | tags 28 | blib/ 29 | README 30 | ^inc/ 31 | JSON-WebToken-*/ 32 | /JSON-WebToken-* 33 | /.build 34 | /_build_params 35 | /Build 36 | !Build/ 37 | !META.json 38 | -------------------------------------------------------------------------------- /.proverc: -------------------------------------------------------------------------------- 1 | --exec "perl -Ilib -MTest::Flatten -MTest::Name::FromLine" 2 | --timer 3 | --merge 4 | --trap 5 | --color 6 | -w 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: perl 2 | perl: 3 | - 5.10 4 | - 5.12 5 | - 5.14 6 | - 5.16 7 | -------------------------------------------------------------------------------- /Build.PL: -------------------------------------------------------------------------------- 1 | # ========================================================================= 2 | # THIS FILE IS AUTOMATICALLY GENERATED BY MINILLA. 3 | # DO NOT EDIT DIRECTLY. 4 | # ========================================================================= 5 | 6 | use 5.008_001; 7 | 8 | use strict; 9 | use warnings; 10 | use utf8; 11 | 12 | use Module::Build; 13 | use File::Basename; 14 | use File::Spec; 15 | 16 | my %args = ( 17 | license => 'perl', 18 | dynamic_config => 0, 19 | 20 | configure_requires => { 21 | 'Module::Build' => 0.38, 22 | }, 23 | 24 | name => 'JSON-WebToken', 25 | module_name => 'JSON::WebToken', 26 | allow_pureperl => 0, 27 | 28 | script_files => [glob('script/*'), glob('bin/*')], 29 | c_source => [qw()], 30 | PL_files => {}, 31 | 32 | test_files => ((-d '.git' || $ENV{RELEASE_TESTING}) && -d 'xt') ? 't/ xt/' : 't/', 33 | recursive_test_files => 1, 34 | 35 | 36 | ); 37 | if (-d 'share') { 38 | $args{share_dir} = 'share'; 39 | } 40 | 41 | my $builder = Module::Build->subclass( 42 | class => 'MyBuilder', 43 | code => q{ 44 | sub ACTION_distmeta { 45 | die "Do not run distmeta. Install Minilla and `minil install` instead.\n"; 46 | } 47 | sub ACTION_installdeps { 48 | die "Do not run installdeps. Run `cpanm --installdeps .` instead.\n"; 49 | } 50 | } 51 | )->new(%args); 52 | $builder->create_build_script(); 53 | 54 | use File::Copy; 55 | 56 | print "cp META.json MYMETA.json\n"; 57 | copy("META.json","MYMETA.json") or die "Copy failed(META.json): $!"; 58 | 59 | if (-f 'META.yml') { 60 | print "cp META.yml MYMETA.yml\n"; 61 | copy("META.yml","MYMETA.yml") or die "Copy failed(META.yml): $!"; 62 | } else { 63 | print "There is no META.yml... You may install this module from the repository...\n"; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /Changes: -------------------------------------------------------------------------------- 1 | Revision history for Perl extension JSON::WebToken 2 | 3 | {{$NEXT}} 4 | 5 | 0.10 2015-03-17T08:21:12Z 6 | - #9 Added acceptable headers args (maedama++) 7 | 8 | 0.09 2014-12-20T02:19:51Z 9 | - #8 JWS implementations must not accept header with { "alg": "none" } by default. (zentooo++) 10 | 11 | 0.08 2014-02-21T05:45:26Z 12 | - added exception class (zentooo++) 13 | 14 | 0.07 2013-04-19T17:48:46Z 15 | - repackaging 16 | 17 | 0.06 2013-04-11T09:51:23Z 18 | - reduced the dependence (suggested by maedama, bayashi) 19 | 20 | 0.05 Sun Feb 10 01:32:14 2013 21 | - added signature check in case of alg is none (ritou++) 22 | 23 | 0.04 Sat Jan 19 21:54:05 2013 24 | - added SEE ALSO 25 | 26 | 0.03 Sat Jan 19 21:50:17 2013 27 | - removed draft version 28 | 29 | 0.02 Sat Jan 19 21:40:56 2013 30 | - first release 31 | 32 | 0.01 Tue Jun 26 22:28:08 2012 33 | - original version 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is copyright (c) 2013 by xaicron Exaicron@cpan.orgE. 2 | 3 | This is free software; you can redistribute it and/or modify it under 4 | the same terms as the Perl 5 programming language system itself. 5 | 6 | Terms of the Perl programming language system itself 7 | 8 | a) the GNU General Public License as published by the Free 9 | Software Foundation; either version 1, or (at your option) any 10 | later version, or 11 | b) the "Artistic License" 12 | 13 | --- The GNU General Public License, Version 1, February 1989 --- 14 | 15 | This software is Copyright (c) 2013 by xaicron Exaicron@cpan.orgE. 16 | 17 | This is free software, licensed under: 18 | 19 | The GNU General Public License, Version 1, February 1989 20 | 21 | GNU GENERAL PUBLIC LICENSE 22 | Version 1, February 1989 23 | 24 | Copyright (C) 1989 Free Software Foundation, Inc. 25 | 51 Franklin St, Suite 500, Boston, MA 02110-1335 USA 26 | 27 | Everyone is permitted to copy and distribute verbatim copies 28 | of this license document, but changing it is not allowed. 29 | 30 | Preamble 31 | 32 | The license agreements of most software companies try to keep users 33 | at the mercy of those companies. By contrast, our General Public 34 | License is intended to guarantee your freedom to share and change free 35 | software--to make sure the software is free for all its users. The 36 | General Public License applies to the Free Software Foundation's 37 | software and to any other program whose authors commit to using it. 38 | You can use it for your programs, too. 39 | 40 | When we speak of free software, we are referring to freedom, not 41 | price. Specifically, the General Public License is designed to make 42 | sure that you have the freedom to give away or sell copies of free 43 | software, that you receive source code or can get it if you want it, 44 | that you can change the software or use pieces of it in new free 45 | programs; and that you know you can do these things. 46 | 47 | To protect your rights, we need to make restrictions that forbid 48 | anyone to deny you these rights or to ask you to surrender the rights. 49 | These restrictions translate to certain responsibilities for you if you 50 | distribute copies of the software, or if you modify it. 51 | 52 | For example, if you distribute copies of a such a program, whether 53 | gratis or for a fee, you must give the recipients all the rights that 54 | you have. You must make sure that they, too, receive or can get the 55 | source code. And you must tell them their rights. 56 | 57 | We protect your rights with two steps: (1) copyright the software, and 58 | (2) offer you this license which gives you legal permission to copy, 59 | distribute and/or modify the software. 60 | 61 | Also, for each author's protection and ours, we want to make certain 62 | that everyone understands that there is no warranty for this free 63 | software. If the software is modified by someone else and passed on, we 64 | want its recipients to know that what they have is not the original, so 65 | that any problems introduced by others will not reflect on the original 66 | authors' reputations. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | GNU GENERAL PUBLIC LICENSE 72 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 73 | 74 | 0. This License Agreement applies to any program or other work which 75 | contains a notice placed by the copyright holder saying it may be 76 | distributed under the terms of this General Public License. The 77 | "Program", below, refers to any such program or work, and a "work based 78 | on the Program" means either the Program or any work containing the 79 | Program or a portion of it, either verbatim or with modifications. Each 80 | licensee is addressed as "you". 81 | 82 | 1. You may copy and distribute verbatim copies of the Program's source 83 | code as you receive it, in any medium, provided that you conspicuously and 84 | appropriately publish on each copy an appropriate copyright notice and 85 | disclaimer of warranty; keep intact all the notices that refer to this 86 | General Public License and to the absence of any warranty; and give any 87 | other recipients of the Program a copy of this General Public License 88 | along with the Program. You may charge a fee for the physical act of 89 | transferring a copy. 90 | 91 | 2. You may modify your copy or copies of the Program or any portion of 92 | it, and copy and distribute such modifications under the terms of Paragraph 93 | 1 above, provided that you also do the following: 94 | 95 | a) cause the modified files to carry prominent notices stating that 96 | you changed the files and the date of any change; and 97 | 98 | b) cause the whole of any work that you distribute or publish, that 99 | in whole or in part contains the Program or any part thereof, either 100 | with or without modifications, to be licensed at no charge to all 101 | third parties under the terms of this General Public License (except 102 | that you may choose to grant warranty protection to some or all 103 | third parties, at your option). 104 | 105 | c) If the modified program normally reads commands interactively when 106 | run, you must cause it, when started running for such interactive use 107 | in the simplest and most usual way, to print or display an 108 | announcement including an appropriate copyright notice and a notice 109 | that there is no warranty (or else, saying that you provide a 110 | warranty) and that users may redistribute the program under these 111 | conditions, and telling the user how to view a copy of this General 112 | Public License. 113 | 114 | d) You may charge a fee for the physical act of transferring a 115 | copy, and you may at your option offer warranty protection in 116 | exchange for a fee. 117 | 118 | Mere aggregation of another independent work with the Program (or its 119 | derivative) on a volume of a storage or distribution medium does not bring 120 | the other work under the scope of these terms. 121 | 122 | 3. You may copy and distribute the Program (or a portion or derivative of 123 | it, under Paragraph 2) in object code or executable form under the terms of 124 | Paragraphs 1 and 2 above provided that you also do one of the following: 125 | 126 | a) accompany it with the complete corresponding machine-readable 127 | source code, which must be distributed under the terms of 128 | Paragraphs 1 and 2 above; or, 129 | 130 | b) accompany it with a written offer, valid for at least three 131 | years, to give any third party free (except for a nominal charge 132 | for the cost of distribution) a complete machine-readable copy of the 133 | corresponding source code, to be distributed under the terms of 134 | Paragraphs 1 and 2 above; or, 135 | 136 | c) accompany it with the information you received as to where the 137 | corresponding source code may be obtained. (This alternative is 138 | allowed only for noncommercial distribution and only if you 139 | received the program in object code or executable form alone.) 140 | 141 | Source code for a work means the preferred form of the work for making 142 | modifications to it. For an executable file, complete source code means 143 | all the source code for all modules it contains; but, as a special 144 | exception, it need not include source code for modules which are standard 145 | libraries that accompany the operating system on which the executable 146 | file runs, or for standard header files or definitions files that 147 | accompany that operating system. 148 | 149 | 4. You may not copy, modify, sublicense, distribute or transfer the 150 | Program except as expressly provided under this General Public License. 151 | Any attempt otherwise to copy, modify, sublicense, distribute or transfer 152 | the Program is void, and will automatically terminate your rights to use 153 | the Program under this License. However, parties who have received 154 | copies, or rights to use copies, from you under this General Public 155 | License will not have their licenses terminated so long as such parties 156 | remain in full compliance. 157 | 158 | 5. By copying, distributing or modifying the Program (or any work based 159 | on the Program) you indicate your acceptance of this license to do so, 160 | and all its terms and conditions. 161 | 162 | 6. Each time you redistribute the Program (or any work based on the 163 | Program), the recipient automatically receives a license from the original 164 | licensor to copy, distribute or modify the Program subject to these 165 | terms and conditions. You may not impose any further restrictions on the 166 | recipients' exercise of the rights granted herein. 167 | 168 | 7. The Free Software Foundation may publish revised and/or new versions 169 | of the General Public License from time to time. Such new versions will 170 | be similar in spirit to the present version, but may differ in detail to 171 | address new problems or concerns. 172 | 173 | Each version is given a distinguishing version number. If the Program 174 | specifies a version number of the license which applies to it and "any 175 | later version", you have the option of following the terms and conditions 176 | either of that version or of any later version published by the Free 177 | Software Foundation. If the Program does not specify a version number of 178 | the license, you may choose any version ever published by the Free Software 179 | Foundation. 180 | 181 | 8. If you wish to incorporate parts of the Program into other free 182 | programs whose distribution conditions are different, write to the author 183 | to ask for permission. For software which is copyrighted by the Free 184 | Software Foundation, write to the Free Software Foundation; we sometimes 185 | make exceptions for this. Our decision will be guided by the two goals 186 | of preserving the free status of all derivatives of our free software and 187 | of promoting the sharing and reuse of software generally. 188 | 189 | NO WARRANTY 190 | 191 | 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 192 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 193 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 194 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 195 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 196 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 197 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 198 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 199 | REPAIR OR CORRECTION. 200 | 201 | 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 202 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 203 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 204 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 205 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 206 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 207 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 208 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 209 | POSSIBILITY OF SUCH DAMAGES. 210 | 211 | END OF TERMS AND CONDITIONS 212 | 213 | Appendix: How to Apply These Terms to Your New Programs 214 | 215 | If you develop a new program, and you want it to be of the greatest 216 | possible use to humanity, the best way to achieve this is to make it 217 | free software which everyone can redistribute and change under these 218 | terms. 219 | 220 | To do so, attach the following notices to the program. It is safest to 221 | attach them to the start of each source file to most effectively convey 222 | the exclusion of warranty; and each file should have at least the 223 | "copyright" line and a pointer to where the full notice is found. 224 | 225 | 226 | Copyright (C) 19yy 227 | 228 | This program is free software; you can redistribute it and/or modify 229 | it under the terms of the GNU General Public License as published by 230 | the Free Software Foundation; either version 1, or (at your option) 231 | any later version. 232 | 233 | This program is distributed in the hope that it will be useful, 234 | but WITHOUT ANY WARRANTY; without even the implied warranty of 235 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 236 | GNU General Public License for more details. 237 | 238 | You should have received a copy of the GNU General Public License 239 | along with this program; if not, write to the Free Software 240 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA 241 | 242 | 243 | Also add information on how to contact you by electronic and paper mail. 244 | 245 | If the program is interactive, make it output a short notice like this 246 | when it starts in an interactive mode: 247 | 248 | Gnomovision version 69, Copyright (C) 19xx name of author 249 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 250 | This is free software, and you are welcome to redistribute it 251 | under certain conditions; type `show c' for details. 252 | 253 | The hypothetical commands `show w' and `show c' should show the 254 | appropriate parts of the General Public License. Of course, the 255 | commands you use may be called something other than `show w' and `show 256 | c'; they could even be mouse-clicks or menu items--whatever suits your 257 | program. 258 | 259 | You should also get your employer (if you work as a programmer) or your 260 | school, if any, to sign a "copyright disclaimer" for the program, if 261 | necessary. Here a sample; alter the names: 262 | 263 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 264 | program `Gnomovision' (a program to direct compilers to make passes 265 | at assemblers) written by James Hacker. 266 | 267 | , 1 April 1989 268 | Ty Coon, President of Vice 269 | 270 | That's all there is to it! 271 | 272 | 273 | --- The Artistic License 1.0 --- 274 | 275 | This software is Copyright (c) 2013 by xaicron Exaicron@cpan.orgE. 276 | 277 | This is free software, licensed under: 278 | 279 | The Artistic License 1.0 280 | 281 | The Artistic License 282 | 283 | Preamble 284 | 285 | The intent of this document is to state the conditions under which a Package 286 | may be copied, such that the Copyright Holder maintains some semblance of 287 | artistic control over the development of the package, while giving the users of 288 | the package the right to use and distribute the Package in a more-or-less 289 | customary fashion, plus the right to make reasonable modifications. 290 | 291 | Definitions: 292 | 293 | - "Package" refers to the collection of files distributed by the Copyright 294 | Holder, and derivatives of that collection of files created through 295 | textual modification. 296 | - "Standard Version" refers to such a Package if it has not been modified, 297 | or has been modified in accordance with the wishes of the Copyright 298 | Holder. 299 | - "Copyright Holder" is whoever is named in the copyright or copyrights for 300 | the package. 301 | - "You" is you, if you're thinking about copying or distributing this Package. 302 | - "Reasonable copying fee" is whatever you can justify on the basis of media 303 | cost, duplication charges, time of people involved, and so on. (You will 304 | not be required to justify it to the Copyright Holder, but only to the 305 | computing community at large as a market that must bear the fee.) 306 | - "Freely Available" means that no fee is charged for the item itself, though 307 | there may be fees involved in handling the item. It also means that 308 | recipients of the item may redistribute it under the same conditions they 309 | received it. 310 | 311 | 1. You may make and give away verbatim copies of the source form of the 312 | Standard Version of this Package without restriction, provided that you 313 | duplicate all of the original copyright notices and associated disclaimers. 314 | 315 | 2. You may apply bug fixes, portability fixes and other modifications derived 316 | from the Public Domain or from the Copyright Holder. A Package modified in such 317 | a way shall still be considered the Standard Version. 318 | 319 | 3. You may otherwise modify your copy of this Package in any way, provided that 320 | you insert a prominent notice in each changed file stating how and when you 321 | changed that file, and provided that you do at least ONE of the following: 322 | 323 | a) place your modifications in the Public Domain or otherwise make them 324 | Freely Available, such as by posting said modifications to Usenet or an 325 | equivalent medium, or placing the modifications on a major archive site 326 | such as ftp.uu.net, or by allowing the Copyright Holder to include your 327 | modifications in the Standard Version of the Package. 328 | 329 | b) use the modified Package only within your corporation or organization. 330 | 331 | c) rename any non-standard executables so the names do not conflict with 332 | standard executables, which must also be provided, and provide a separate 333 | manual page for each non-standard executable that clearly documents how it 334 | differs from the Standard Version. 335 | 336 | d) make other distribution arrangements with the Copyright Holder. 337 | 338 | 4. You may distribute the programs of this Package in object code or executable 339 | form, provided that you do at least ONE of the following: 340 | 341 | a) distribute a Standard Version of the executables and library files, 342 | together with instructions (in the manual page or equivalent) on where to 343 | get the Standard Version. 344 | 345 | b) accompany the distribution with the machine-readable source of the Package 346 | with your modifications. 347 | 348 | c) accompany any non-standard executables with their corresponding Standard 349 | Version executables, giving the non-standard executables non-standard 350 | names, and clearly documenting the differences in manual pages (or 351 | equivalent), together with instructions on where to get the Standard 352 | Version. 353 | 354 | d) make other distribution arrangements with the Copyright Holder. 355 | 356 | 5. You may charge a reasonable copying fee for any distribution of this 357 | Package. You may charge any fee you choose for support of this Package. You 358 | may not charge a fee for this Package itself. However, you may distribute this 359 | Package in aggregate with other (possibly commercial) programs as part of a 360 | larger (possibly commercial) software distribution provided that you do not 361 | advertise this Package as a product of your own. 362 | 363 | 6. The scripts and library files supplied as input to or produced as output 364 | from the programs of this Package do not automatically fall under the copyright 365 | of this Package, but belong to whomever generated them, and may be sold 366 | commercially, and may be aggregated with this Package. 367 | 368 | 7. C or perl subroutines supplied by you and linked into this Package shall not 369 | be considered part of this Package. 370 | 371 | 8. The name of the Copyright Holder may not be used to endorse or promote 372 | products derived from this software without specific prior written permission. 373 | 374 | 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED 375 | WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 376 | MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 377 | 378 | The End 379 | 380 | -------------------------------------------------------------------------------- /META.json: -------------------------------------------------------------------------------- 1 | { 2 | "abstract" : "JSON Web Token (JWT) implementation", 3 | "author" : [ 4 | "xaicron " 5 | ], 6 | "dynamic_config" : 0, 7 | "generated_by" : "Minilla/v2.3.0, CPAN::Meta::Converter version 2.143240", 8 | "license" : [ 9 | "perl_5" 10 | ], 11 | "meta-spec" : { 12 | "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", 13 | "version" : "2" 14 | }, 15 | "name" : "JSON-WebToken", 16 | "no_index" : { 17 | "directory" : [ 18 | "t", 19 | "xt", 20 | "inc", 21 | "share", 22 | "eg", 23 | "examples", 24 | "author", 25 | "builder" 26 | ] 27 | }, 28 | "prereqs" : { 29 | "configure" : { 30 | "requires" : { 31 | "Module::Build" : "0.38" 32 | } 33 | }, 34 | "develop" : { 35 | "requires" : { 36 | "Test::CPAN::Meta" : "0", 37 | "Test::MinimumVersion::Fast" : "0.04", 38 | "Test::PAUSE::Permissions" : "0.04", 39 | "Test::Pod" : "1.41", 40 | "Test::Spellunker" : "v0.2.7" 41 | } 42 | }, 43 | "runtime" : { 44 | "requires" : { 45 | "Carp" : "0", 46 | "Digest::SHA" : "0", 47 | "Exporter" : "0", 48 | "JSON" : "0", 49 | "MIME::Base64" : "0", 50 | "Module::Runtime" : "0", 51 | "parent" : "0", 52 | "perl" : "5.008001" 53 | } 54 | }, 55 | "test" : { 56 | "requires" : { 57 | "Test::Mock::Guard" : "0.07", 58 | "Test::More" : "0.98", 59 | "Test::Requires" : "0.06" 60 | } 61 | } 62 | }, 63 | "release_status" : "unstable", 64 | "resources" : { 65 | "bugtracker" : { 66 | "web" : "https://github.com/xaicron/p5-JSON-WebToken/issues" 67 | }, 68 | "homepage" : "https://github.com/xaicron/p5-JSON-WebToken", 69 | "repository" : { 70 | "type" : "git", 71 | "url" : "git://github.com/xaicron/p5-JSON-WebToken.git", 72 | "web" : "https://github.com/xaicron/p5-JSON-WebToken" 73 | } 74 | }, 75 | "version" : "0.10", 76 | "x_authority" : "cpan:XAICRON", 77 | "x_contributors" : [ 78 | "Masaki Sawamura ", 79 | "Ryo ", 80 | "yokoe.naosuke ", 81 | "maedama ", 82 | "xaicron " 83 | ] 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/xaicron/p5-JSON-WebToken.svg?branch=master)](https://travis-ci.org/xaicron/p5-JSON-WebToken) 2 | # NAME 3 | 4 | JSON::WebToken - JSON Web Token (JWT) implementation 5 | 6 | # SYNOPSIS 7 | 8 | use Test::More; 9 | use JSON; 10 | use JSON::WebToken; 11 | 12 | my $claims = { 13 | iss => 'joe', 14 | exp => 1300819380, 15 | 'http://example.com/is_root' => JSON::true, 16 | }; 17 | my $secret = 'secret'; 18 | 19 | my $jwt = encode_jwt $claims, $secret; 20 | my $got = decode_jwt $jwt, $secret; 21 | is_deeply $got, $claims; 22 | 23 | done_testing; 24 | 25 | # DESCRIPTION 26 | 27 | JSON::WebToken is JSON Web Token (JWT) implementation for Perl 28 | 29 | **THIS MODULE IS ALPHA LEVEL INTERFACE.** 30 | 31 | # METHODS 32 | 33 | ## encode($claims \[, $secret, $algorithm, $extra\_headers \]) : String 34 | 35 | This method is encoding JWT from hash reference. 36 | 37 | my $jwt = JSON::WebToken->encode({ 38 | iss => 'joe', 39 | exp => 1300819380, 40 | 'http://example.com/is_root' => JSON::true, 41 | }, 'secret'); 42 | # $jwt = join '.', 43 | # 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', 44 | # 'eyJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UifQ' 45 | # '4ldFxjibgJGz_uaIRCIq89b5ipR-sbI2Uq7B2WNEDs0' 46 | 47 | Default encryption algorithm is `HS256`. You can change algorithm as following: 48 | 49 | my $pricate_key_string = '...'; 50 | my $public_key_string = '...'; 51 | 52 | my $jwt = JSON::WebToken->encode({ 53 | iss => 'joe', 54 | exp => 1300819380, 55 | 'http://example.com/is_root' => JSON::true, 56 | }, $pricate_key_string, 'RS256'); 57 | 58 | my $claims = JSON::WebToken->decode($jwt, $public_key_string); 59 | 60 | When you use RS256, RS384 or RS512 algorithm then, We need [Crypt::OpenSSL::RSA](https://metacpan.org/pod/Crypt::OpenSSL::RSA). 61 | 62 | If you want to create a `Plaintext JWT`, should be specify `none` for the algorithm. 63 | 64 | my $jwt = JSON::WebToken->encode({ 65 | iss => 'joe', 66 | exp => 1300819380, 67 | 'http://example.com/is_root' => JSON::true, 68 | }, '', 'none'); 69 | # $jwt = join '.', 70 | # 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0', 71 | # 'eyJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UifQ', 72 | # '' 73 | 74 | ## decode($jwt \[, $secret, $verify\_signature, $accepted\_algorithms \]) : HASH 75 | 76 | This method is decoding hash reference from JWT string. 77 | 78 | my $claims = JSON::WebToken->decode($jwt, $secret, 1, ["RS256"]); 79 | 80 | Any signing algorithm (except "none") is acceptable by default, 81 | so you should check it with $accepted\_algorithms parameter. 82 | 83 | ## add\_signing\_algorithm($algorithm, $class) 84 | 85 | This method is adding signing algorithm. 86 | 87 | # resolve JSON::WebToken::Crypt::MYALG 88 | JSON::WebToken->add_signing_algorithm('MYALGXXX' => 'MYALG'); 89 | 90 | # resolve Some::Class::Algorithm 91 | JSON::WebToken->add_signing_algorithm('SOMEALGXXX' => '+Some::Class::Algorithm'); 92 | 93 | SEE ALSO [JSON::WebToken::Crypt::HMAC](https://metacpan.org/pod/JSON::WebToken::Crypt::HMAC) or [JSON::WebToken::Crypt::RAS](https://metacpan.org/pod/JSON::WebToken::Crypt::RAS). 94 | 95 | # FUNCTIONS 96 | 97 | ## encode\_jwt($claims \[, $secret, $algorithm, $extra\_headers \]) : String 98 | 99 | Same as `encode()` method. 100 | 101 | ## decode\_jwt($jwt \[, $secret, $verify\_signature, $accepted\_algorithms \]) : Hash 102 | 103 | Same as `decode()` method. 104 | 105 | # ERROR CODES 106 | 107 | JSON::WebToken::Exception will be thrown with following code. 108 | 109 | ## ERROR\_JWT\_INVALID\_PARAMETER 110 | 111 | When some method arguments are not valid. 112 | 113 | ## ERROR\_JWT\_MISSING\_SECRET 114 | 115 | When secret is required. (`alg != "none"`) 116 | 117 | ## ERROR\_JWT\_INVALID\_SEGMENT\_COUNT 118 | 119 | When JWT segment count is not between 2 and 4. 120 | 121 | ## ERROR\_JWT\_INVALID\_SEGMENT\_ENCODING 122 | 123 | When each JWT segment is not encoded by base64url. 124 | 125 | ## ERROR\_JWT\_UNWANTED\_SIGNATURE 126 | 127 | When `alg == "none"` but signature segment found. 128 | 129 | ## ERROR\_JWT\_INVALID\_SIGNATURE 130 | 131 | When JWT signature is invalid. 132 | 133 | ## ERROR\_JWT\_NOT\_SUPPORTED\_SIGNING\_ALGORITHM 134 | 135 | When given signing algorithm is not supported. 136 | 137 | ## ERROR\_JWT\_UNACCEPTABLE\_ALGORITHM 138 | 139 | When given signing algorithm is not included in acceptable\_algorithms. 140 | 141 | # AUTHOR 142 | 143 | xaicron 144 | 145 | zentooo 146 | 147 | # COPYRIGHT 148 | 149 | Copyright 2012 - xaicron 150 | 151 | # LICENSE 152 | 153 | This library is free software; you can redistribute it and/or modify 154 | it under the same terms as Perl itself. 155 | 156 | # SEE ALSO 157 | 158 | [http://tools.ietf.org/html/draft-ietf-oauth-json-web-token](http://tools.ietf.org/html/draft-ietf-oauth-json-web-token) 159 | -------------------------------------------------------------------------------- /cpanfile: -------------------------------------------------------------------------------- 1 | requires 'Carp'; 2 | requires 'parent'; 3 | requires 'Module::Runtime'; 4 | requires 'Digest::SHA'; 5 | requires 'Exporter'; 6 | requires 'JSON'; 7 | requires 'MIME::Base64'; 8 | requires 'perl', '5.008001'; 9 | 10 | on test => sub { 11 | requires 'Test::Mock::Guard', '0.07'; 12 | requires 'Test::More', '0.98'; 13 | requires 'Test::Requires', '0.06'; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/JSON/WebToken.pm: -------------------------------------------------------------------------------- 1 | package JSON::WebToken; 2 | 3 | use strict; 4 | use warnings; 5 | use 5.008_001; 6 | 7 | our $VERSION = '0.10'; 8 | 9 | use parent 'Exporter'; 10 | 11 | use Carp qw(croak carp); 12 | use JSON qw(encode_json decode_json); 13 | use MIME::Base64 qw(encode_base64 decode_base64); 14 | use Module::Runtime qw(use_module); 15 | 16 | use JSON::WebToken::Constants; 17 | use JSON::WebToken::Exception; 18 | 19 | our @EXPORT = qw(encode_jwt decode_jwt); 20 | 21 | our $ALGORITHM_MAP = { 22 | # for JWS 23 | HS256 => 'HMAC', 24 | HS384 => 'HMAC', 25 | HS512 => 'HMAC', 26 | RS256 => 'RSA', 27 | RS384 => 'RSA', 28 | RS512 => 'RSA', 29 | # ES256 => 'EC', 30 | # ES384 => 'EC', 31 | # ES512 => 'EC', 32 | none => 'NONE', 33 | 34 | # for JWE 35 | RSA1_5 => 'RSA', 36 | # 'RSA-OAEP' => 'OAEP', 37 | # A128KW => '', 38 | # A256KW => '', 39 | dir => 'NONE', 40 | # 'ECDH-ES' => '', 41 | # 'ECDH-ES+A128KW' => '', 42 | # 'ECDH-ES+A256KW' => '', 43 | 44 | # for JWK 45 | # EC => 'EC', 46 | RSA => 'RSA', 47 | }; 48 | 49 | #our $ENCRIPTION_ALGORITHM_MAP = { 50 | # 'A128CBC+HS256' => 'AES_CBC', 51 | # 'A256CBC+HS512' => 'AES_CBC', 52 | # A128GCM => '', 53 | # A256GCM => '', 54 | #}; 55 | 56 | our $DEFAULT_ALLOWED_ALGORITHMS = [ grep { $_ ne "none" } (keys %$ALGORITHM_MAP) ]; 57 | 58 | sub encode { 59 | my ($class, $claims, $secret, $algorithm, $extra_headers) = @_; 60 | unless (ref $claims eq 'HASH') { 61 | JSON::WebToken::Exception->throw( 62 | code => ERROR_JWT_INVALID_PARAMETER, 63 | message => 'Usage: JSON::WebToken->encode(\%claims [, $secret, $algorithm, \%$extra_headers ])', 64 | ); 65 | } 66 | 67 | $algorithm ||= 'HS256'; 68 | $extra_headers ||= {}; 69 | 70 | my $header = { 71 | # typ parameter is OPTIONAL ("JWT" or "urn:ietf:params:oauth:token-type:jwt") 72 | # typ => 'JWT', 73 | alg => $algorithm, 74 | %$extra_headers, 75 | }; 76 | 77 | $algorithm = $header->{alg}; 78 | if ($algorithm ne 'none' && !defined $secret) { 79 | JSON::WebToken::Exception->throw( 80 | code => ERROR_JWT_MISSING_SECRET, 81 | message => 'secret must be specified', 82 | ); 83 | } 84 | 85 | my $header_segment = encode_base64url(encode_json $header); 86 | my $claims_segment = encode_base64url(encode_json $claims); 87 | my $signature_input = join '.', $header_segment, $claims_segment; 88 | 89 | my $signature = $class->_sign($algorithm, $signature_input, $secret); 90 | 91 | return join '.', $signature_input, encode_base64url($signature); 92 | } 93 | 94 | sub encode_jwt { 95 | local $Carp::CarpLevel = $Carp::CarpLevel + 1; 96 | __PACKAGE__->encode(@_); 97 | } 98 | 99 | sub decode { 100 | my ($class, $jwt, $secret, $verify_signature, $accepted_algorithms) = @_; 101 | 102 | if (ref $accepted_algorithms eq 'ARRAY') { 103 | # do nothing 104 | } 105 | elsif (defined $accepted_algorithms) { 106 | if ($accepted_algorithms =~/^[01]$/) { 107 | carp "accept_algorithm none is deprecated"; 108 | $accepted_algorithms = !!$accepted_algorithms ? 109 | [@$DEFAULT_ALLOWED_ALGORITHMS, "none"] : $DEFAULT_ALLOWED_ALGORITHMS; 110 | } 111 | else { 112 | $accepted_algorithms = [ $accepted_algorithms ]; 113 | } 114 | } 115 | else { 116 | $accepted_algorithms = $DEFAULT_ALLOWED_ALGORITHMS; 117 | } 118 | 119 | unless (defined $jwt) { 120 | JSON::WebToken::Exception->throw( 121 | code => ERROR_JWT_INVALID_PARAMETER, 122 | message => 'Usage: JSON::WebToken->decode($jwt [, $secret, $verify_signature, $accepted_algorithms ])', 123 | ); 124 | } 125 | 126 | $verify_signature = 1 unless defined $verify_signature; 127 | if ($verify_signature && !defined $secret) { 128 | JSON::WebToken::Exception->throw( 129 | code => ERROR_JWT_MISSING_SECRET, 130 | message => 'secret must be specified', 131 | ); 132 | } 133 | 134 | my $segments = [ split '\.', $jwt ]; 135 | unless (@$segments >= 2 && @$segments <= 4) { 136 | JSON::WebToken::Exception->throw( 137 | code => ERROR_JWT_INVALID_SEGMENT_COUNT, 138 | message => "Not enough or too many segments by $jwt", 139 | ); 140 | } 141 | 142 | my ($header_segment, $claims_segment, $crypto_segment) = @$segments; 143 | my $signature_input = join '.', $header_segment, $claims_segment; 144 | 145 | my ($header, $claims, $signature); 146 | eval { 147 | $header = decode_json decode_base64url($header_segment); 148 | $claims = decode_json decode_base64url($claims_segment); 149 | $signature = decode_base64url($crypto_segment) if $header->{alg} ne 'none' && $verify_signature; 150 | }; 151 | if (my $e = $@) { 152 | JSON::WebToken::Exception->throw( 153 | code => ERROR_JWT_INVALID_SEGMENT_ENCODING, 154 | message => 'Invalid segment encoding', 155 | ); 156 | } 157 | 158 | return $claims unless $verify_signature; 159 | 160 | my $algorithm = $header->{alg}; 161 | # https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-37#section-3.6 162 | unless ( grep { $_ eq $algorithm } (@$accepted_algorithms) ) { 163 | JSON::WebToken::Exception->throw( 164 | code => ERROR_JWT_UNACCEPTABLE_ALGORITHM, 165 | message => "Algorithm \"$algorithm\" is not acceptable. Followings are accepted:" . join(",", @$accepted_algorithms) , 166 | ); 167 | } 168 | 169 | if (ref $secret eq 'CODE') { 170 | $secret = $secret->($header, $claims); 171 | } 172 | 173 | if ($algorithm eq 'none' and $crypto_segment) { 174 | JSON::WebToken::Exception->throw( 175 | code => ERROR_JWT_UNWANTED_SIGNATURE, 176 | message => 'Signature must be the empty string when alg is none', 177 | ); 178 | } 179 | 180 | unless ($class->_verify($algorithm, $signature_input, $secret, $signature)) { 181 | JSON::WebToken::Exception->throw( 182 | code => ERROR_JWT_INVALID_SIGNATURE, 183 | message => "Invalid signature by $signature", 184 | ); 185 | } 186 | 187 | return $claims; 188 | } 189 | 190 | sub decode_jwt { 191 | local $Carp::CarpLevel = $Carp::CarpLevel + 1; 192 | __PACKAGE__->decode(@_); 193 | } 194 | 195 | sub add_signing_algorithm { 196 | my ($class, $algorithm, $signing_class) = @_; 197 | unless ($algorithm && $signing_class) { 198 | JSON::WebToken::Exception->throw( 199 | code => ERROR_JWT_INVALID_PARAMETER, 200 | message => 'Usage: JSON::WebToken->add_signing_algorithm($algorithm, $signing_class)', 201 | ); 202 | } 203 | push(@$DEFAULT_ALLOWED_ALGORITHMS, $algorithm); 204 | $ALGORITHM_MAP->{$algorithm} = $signing_class; 205 | } 206 | 207 | sub _sign { 208 | my ($class, $algorithm, $message, $secret) = @_; 209 | return '' if $algorithm eq 'none'; 210 | 211 | local $Carp::CarpLevel = $Carp::CarpLevel + 1; 212 | $class->_ensure_class_loaded($algorithm)->sign($algorithm, $message, $secret); 213 | } 214 | 215 | sub _verify { 216 | my ($class, $algorithm, $message, $secret, $signature) = @_; 217 | return 1 if $algorithm eq 'none'; 218 | 219 | local $Carp::CarpLevel = $Carp::CarpLevel + 1; 220 | $class->_ensure_class_loaded($algorithm)->verify($algorithm, $message, $secret, $signature); 221 | } 222 | 223 | my (%class_loaded, %alg_to_class); 224 | sub _ensure_class_loaded { 225 | my ($class, $algorithm) = @_; 226 | return $alg_to_class{$algorithm} if $alg_to_class{$algorithm}; 227 | 228 | my $klass = $ALGORITHM_MAP->{$algorithm}; 229 | unless ($klass) { 230 | JSON::WebToken::Exception->throw( 231 | code => ERROR_JWT_NOT_SUPPORTED_SIGNING_ALGORITHM, 232 | message => "`$algorithm` is Not supported siging algorithm", 233 | ); 234 | } 235 | 236 | my $signing_class = $klass =~ s/^\+// ? $klass : "JSON::WebToken::Crypt::$klass"; 237 | return $signing_class if $class_loaded{$signing_class}; 238 | 239 | use_module $signing_class unless $class->_is_inner_package($signing_class); 240 | 241 | $class_loaded{$signing_class} = 1; 242 | $alg_to_class{$algorithm} = $signing_class; 243 | 244 | return $signing_class; 245 | } 246 | 247 | sub _is_inner_package { 248 | my ($class, $klass) = @_; 249 | no strict 'refs'; 250 | %{ "$klass\::" } ? 1 : 0; 251 | } 252 | 253 | #################################################### 254 | # Taken from newer MIME::Base64 255 | # In order to support older version of MIME::Base64 256 | #################################################### 257 | sub encode_base64url { 258 | my $e = encode_base64(shift, ""); 259 | $e =~ s/=+\z//; 260 | $e =~ tr[+/][-_]; 261 | return $e; 262 | } 263 | 264 | sub decode_base64url { 265 | my $s = shift; 266 | $s =~ tr[-_][+/]; 267 | $s .= '=' while length($s) % 4; 268 | return decode_base64($s); 269 | } 270 | 271 | 1; 272 | __END__ 273 | 274 | =encoding utf-8 275 | 276 | =for stopwords 277 | 278 | =head1 NAME 279 | 280 | JSON::WebToken - JSON Web Token (JWT) implementation 281 | 282 | =head1 SYNOPSIS 283 | 284 | use Test::More; 285 | use JSON; 286 | use JSON::WebToken; 287 | 288 | my $claims = { 289 | iss => 'joe', 290 | exp => 1300819380, 291 | 'http://example.com/is_root' => JSON::true, 292 | }; 293 | my $secret = 'secret'; 294 | 295 | my $jwt = encode_jwt $claims, $secret; 296 | my $got = decode_jwt $jwt, $secret; 297 | is_deeply $got, $claims; 298 | 299 | done_testing; 300 | 301 | =head1 DESCRIPTION 302 | 303 | JSON::WebToken is JSON Web Token (JWT) implementation for Perl 304 | 305 | B<< THIS MODULE IS ALPHA LEVEL INTERFACE. >> 306 | 307 | =head1 METHODS 308 | 309 | =head2 encode($claims [, $secret, $algorithm, $extra_headers ]) : String 310 | 311 | This method is encoding JWT from hash reference. 312 | 313 | my $jwt = JSON::WebToken->encode({ 314 | iss => 'joe', 315 | exp => 1300819380, 316 | 'http://example.com/is_root' => JSON::true, 317 | }, 'secret'); 318 | # $jwt = join '.', 319 | # 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', 320 | # 'eyJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UifQ' 321 | # '4ldFxjibgJGz_uaIRCIq89b5ipR-sbI2Uq7B2WNEDs0' 322 | 323 | Default encryption algorithm is C<< HS256 >>. You can change algorithm as following: 324 | 325 | my $pricate_key_string = '...'; 326 | my $public_key_string = '...'; 327 | 328 | my $jwt = JSON::WebToken->encode({ 329 | iss => 'joe', 330 | exp => 1300819380, 331 | 'http://example.com/is_root' => JSON::true, 332 | }, $pricate_key_string, 'RS256'); 333 | 334 | my $claims = JSON::WebToken->decode($jwt, $public_key_string); 335 | 336 | When you use RS256, RS384 or RS512 algorithm then, We need L<< Crypt::OpenSSL::RSA >>. 337 | 338 | If you want to create a C<< Plaintext JWT >>, should be specify C<< none >> for the algorithm. 339 | 340 | my $jwt = JSON::WebToken->encode({ 341 | iss => 'joe', 342 | exp => 1300819380, 343 | 'http://example.com/is_root' => JSON::true, 344 | }, '', 'none'); 345 | # $jwt = join '.', 346 | # 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0', 347 | # 'eyJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UifQ', 348 | # '' 349 | 350 | =head2 decode($jwt [, $secret, $verify_signature, $accepted_algorithms ]) : HASH 351 | 352 | This method is decoding hash reference from JWT string. 353 | 354 | my $claims = JSON::WebToken->decode($jwt, $secret, 1, ["RS256"]); 355 | 356 | Any signing algorithm (except "none") is acceptable by default, 357 | so you should check it with $accepted_algorithms parameter. 358 | 359 | =head2 add_signing_algorithm($algorithm, $class) 360 | 361 | This method is adding signing algorithm. 362 | 363 | # resolve JSON::WebToken::Crypt::MYALG 364 | JSON::WebToken->add_signing_algorithm('MYALGXXX' => 'MYALG'); 365 | 366 | # resolve Some::Class::Algorithm 367 | JSON::WebToken->add_signing_algorithm('SOMEALGXXX' => '+Some::Class::Algorithm'); 368 | 369 | SEE ALSO L<< JSON::WebToken::Crypt::HMAC >> or L<< JSON::WebToken::Crypt::RAS >>. 370 | 371 | =head1 FUNCTIONS 372 | 373 | =head2 encode_jwt($claims [, $secret, $algorithm, $extra_headers ]) : String 374 | 375 | Same as C<< encode() >> method. 376 | 377 | =head2 decode_jwt($jwt [, $secret, $verify_signature, $accepted_algorithms ]) : Hash 378 | 379 | Same as C<< decode() >> method. 380 | 381 | =head1 ERROR CODES 382 | 383 | JSON::WebToken::Exception will be thrown with following code. 384 | 385 | =head2 ERROR_JWT_INVALID_PARAMETER 386 | 387 | When some method arguments are not valid. 388 | 389 | =head2 ERROR_JWT_MISSING_SECRET 390 | 391 | When secret is required. (C<< alg != "none" >>) 392 | 393 | =head2 ERROR_JWT_INVALID_SEGMENT_COUNT 394 | 395 | When JWT segment count is not between 2 and 4. 396 | 397 | =head2 ERROR_JWT_INVALID_SEGMENT_ENCODING 398 | 399 | When each JWT segment is not encoded by base64url. 400 | 401 | =head2 ERROR_JWT_UNWANTED_SIGNATURE 402 | 403 | When C<< alg == "none" >> but signature segment found. 404 | 405 | =head2 ERROR_JWT_INVALID_SIGNATURE 406 | 407 | When JWT signature is invalid. 408 | 409 | =head2 ERROR_JWT_NOT_SUPPORTED_SIGNING_ALGORITHM 410 | 411 | When given signing algorithm is not supported. 412 | 413 | =head2 ERROR_JWT_UNACCEPTABLE_ALGORITHM 414 | 415 | When given signing algorithm is not included in acceptable_algorithms. 416 | 417 | =head1 AUTHOR 418 | 419 | xaicron Exaicron@cpan.orgE 420 | 421 | zentooo 422 | 423 | =head1 COPYRIGHT 424 | 425 | Copyright 2012 - xaicron 426 | 427 | =head1 LICENSE 428 | 429 | This library is free software; you can redistribute it and/or modify 430 | it under the same terms as Perl itself. 431 | 432 | =head1 SEE ALSO 433 | 434 | L<< http://tools.ietf.org/html/draft-ietf-oauth-json-web-token >> 435 | 436 | =cut 437 | -------------------------------------------------------------------------------- /lib/JSON/WebToken/Constants.pm: -------------------------------------------------------------------------------- 1 | package JSON::WebToken::Constants; 2 | 3 | use strict; 4 | use warnings; 5 | use parent qw/Exporter/; 6 | 7 | my @error_code = qw/ 8 | ERROR_JWT_INVALID_PARAMETER 9 | ERROR_JWT_MISSING_SECRET 10 | ERROR_JWT_INVALID_SEGMENT_COUNT 11 | ERROR_JWT_INVALID_SEGMENT_ENCODING 12 | ERROR_JWT_UNWANTED_SIGNATURE 13 | ERROR_JWT_INVALID_SIGNATURE 14 | ERROR_JWT_NOT_SUPPORTED_SIGNING_ALGORITHM 15 | ERROR_JWT_UNACCEPTABLE_ALGORITHM 16 | /; 17 | 18 | our @EXPORT = @error_code; 19 | our @EXPORT_OK = (); 20 | our %EXPORT_TAGS = ( 21 | all => [@EXPORT, @EXPORT_OK], 22 | error_code => \@error_code, 23 | ); 24 | 25 | use constant { 26 | ERROR_JWT_INVALID_PARAMETER => "invalid_parameter", 27 | ERROR_JWT_MISSING_SECRET => "missing_secret", 28 | ERROR_JWT_INVALID_SEGMENT_COUNT => "invalid_segment_count", 29 | ERROR_JWT_INVALID_SEGMENT_ENCODING => "invalid_segment_encoding", 30 | ERROR_JWT_UNWANTED_SIGNATURE => "unwanted_signature", 31 | ERROR_JWT_INVALID_SIGNATURE => "invalid_signature", 32 | ERROR_JWT_NOT_SUPPORTED_SIGNING_ALGORITHM => "not_supported_signing_algorithm", 33 | ERROR_JWT_UNACCEPTABLE_ALGORITHM => "unacceptable_algorithm", 34 | }; 35 | 36 | 1; 37 | __END__ 38 | -------------------------------------------------------------------------------- /lib/JSON/WebToken/Crypt.pm: -------------------------------------------------------------------------------- 1 | package JSON::WebToken::Crypt; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | sub sign { 7 | my ($class, $algorithm, $message, $key) = @_; 8 | die 'sign method must be implements!'; 9 | } 10 | 11 | sub verify { 12 | my ($class, $algorithm, $message, $key, $signature) = @_; 13 | die 'verify method must be implements!' 14 | } 15 | 16 | 1; 17 | __END__ 18 | -------------------------------------------------------------------------------- /lib/JSON/WebToken/Crypt/HMAC.pm: -------------------------------------------------------------------------------- 1 | package JSON::WebToken::Crypt::HMAC; 2 | 3 | use strict; 4 | use warnings; 5 | use parent 'JSON::WebToken::Crypt'; 6 | 7 | use Digest::SHA (); 8 | 9 | our $ALGORITHM2SIGNING_METHOD_MAP = { 10 | HS256 => \&Digest::SHA::hmac_sha256, 11 | HS384 => \&Digest::SHA::hmac_sha384, 12 | HS512 => \&Digest::SHA::hmac_sha512, 13 | }; 14 | 15 | sub sign { 16 | my ($class, $algorithm, $message, $key) = @_; 17 | 18 | my $sign = ''; 19 | my $method = $ALGORITHM2SIGNING_METHOD_MAP->{$algorithm}; 20 | return $method->($message, $key); 21 | } 22 | 23 | sub verify { 24 | my ($class, $algorithm, $message, $key, $signature) = @_; 25 | my $sign = $class->sign($algorithm, $message, $key); 26 | return $sign eq $signature ? 1 : 0; 27 | } 28 | 29 | 1; 30 | __END__ 31 | -------------------------------------------------------------------------------- /lib/JSON/WebToken/Crypt/RSA.pm: -------------------------------------------------------------------------------- 1 | package JSON::WebToken::Crypt::RSA; 2 | 3 | use strict; 4 | use warnings; 5 | use parent 'JSON::WebToken::Crypt'; 6 | 7 | use Crypt::OpenSSL::RSA (); 8 | 9 | our $ALGORITHM2SIGNING_METHOD_MAP = { 10 | RS256 => 'use_sha256_hash', 11 | RS384 => 'use_sha384_hash', 12 | RS512 => 'use_sha512_hash', 13 | RSA1_5 => 'use_pkcs1_padding', 14 | }; 15 | 16 | sub sign { 17 | my ($class, $algorithm, $message, $key) = @_; 18 | 19 | my $private_key = Crypt::OpenSSL::RSA->new_private_key($key); 20 | my $method = $ALGORITHM2SIGNING_METHOD_MAP->{$algorithm}; 21 | $private_key->$method; 22 | return $private_key->sign($message); 23 | } 24 | 25 | sub verify { 26 | my ($class, $algorithm, $message, $key, $signature) = @_; 27 | 28 | my $public_key = Crypt::OpenSSL::RSA->new_public_key($key); 29 | my $method = $ALGORITHM2SIGNING_METHOD_MAP->{$algorithm}; 30 | $public_key->$method; 31 | return $public_key->verify($message, $signature) ? 1 : 0; 32 | } 33 | 34 | 1; 35 | __END__ 36 | -------------------------------------------------------------------------------- /lib/JSON/WebToken/Exception.pm: -------------------------------------------------------------------------------- 1 | package JSON::WebToken::Exception; 2 | 3 | use strict; 4 | use warnings; 5 | use overload ( 6 | q|""| => \&to_string, 7 | ); 8 | 9 | use Carp qw/croak/; 10 | 11 | sub throw { 12 | my ($class, %args) = @_; 13 | my $self = bless \%args, $class; 14 | croak $self; 15 | } 16 | 17 | sub code { $_[0]->{code} } 18 | sub message { $_[0]->{message} } 19 | 20 | sub to_string { 21 | $_[0]->message; 22 | } 23 | 24 | 1; 25 | __END__ 26 | 27 | 28 | -------------------------------------------------------------------------------- /minil.toml: -------------------------------------------------------------------------------- 1 | name = "JSON-WebToken" 2 | authority = "cpan:XAICRON" 3 | badges = ["travis"] 4 | -------------------------------------------------------------------------------- /t/00_compile.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More tests => 1; 4 | 5 | BEGIN { use_ok 'JSON::WebToken' } 6 | -------------------------------------------------------------------------------- /t/01_basic.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use t::Util; 4 | use Test::More; 5 | 6 | test_encode_decode( 7 | desc => 'simple', 8 | input => { 9 | claims => { foo => 'bar' }, 10 | secret => 'secret', 11 | }, 12 | ); 13 | 14 | test_encode_decode( 15 | desc => 'with algorithm: HS256', 16 | input => { 17 | claims => { foo => 'bar' }, 18 | secret => 'secret', 19 | algorithm => 'HS256', 20 | }, 21 | ); 22 | 23 | test_encode_decode( 24 | desc => 'with algorithm: HS384', 25 | input => { 26 | claims => { foo => 'bar' }, 27 | secret => 'secret', 28 | algorithm => 'HS384', 29 | }, 30 | ); 31 | 32 | test_encode_decode( 33 | desc => 'with algorithm: HS512', 34 | input => { 35 | claims => { foo => 'bar' }, 36 | secret => 'secret', 37 | algorithm => 'HS512', 38 | }, 39 | ); 40 | 41 | test_encode_decode( 42 | desc => 'with header_fields', 43 | input => { 44 | claims => { foo => 'bar' }, 45 | secret => 'secret', 46 | algorithm => 'XXXXXX', 47 | header_fields => { 48 | alg => 'HS256', 49 | }, 50 | }, 51 | ); 52 | 53 | done_testing; 54 | -------------------------------------------------------------------------------- /t/02_rsa.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use t::Util; 4 | 5 | use Test::More; 6 | use Test::Requires 'Crypt::OpenSSL::RSA'; 7 | 8 | my $rsa = Crypt::OpenSSL::RSA->generate_key(1024); 9 | my $private_key = $rsa->get_private_key_string; 10 | my $public_key = $rsa->get_public_key_string; 11 | 12 | test_encode_decode( 13 | desc => 'RS256', 14 | input => { 15 | claims => { foo => 'bar' }, 16 | secret => $private_key, 17 | public_key => $public_key, 18 | algorithm => 'RS256', 19 | }, 20 | ); 21 | 22 | test_encode_decode( 23 | desc => 'RS384', 24 | input => { 25 | claims => { foo => 'bar' }, 26 | secret => $private_key, 27 | public_key => $public_key, 28 | algorithm => 'RS384', 29 | }, 30 | ); 31 | 32 | test_encode_decode( 33 | desc => 'RS512', 34 | input => { 35 | claims => { foo => 'bar' }, 36 | secret => $private_key, 37 | public_key => $public_key, 38 | algorithm => 'RS512', 39 | }, 40 | ); 41 | 42 | done_testing; 43 | -------------------------------------------------------------------------------- /t/03_add_signing_algorithm.t: -------------------------------------------------------------------------------- 1 | package JSON::WebToken::Crypt::__FOO__; 2 | use Test::More; 3 | 4 | sub sign { 5 | my ($class, $algorithm, $message, $key) = @_; 6 | return pack 'H*' => join \0, $algorithm, $message, $key; 7 | } 8 | 9 | sub verify { 10 | my ($class, $algorithm, $message, $key, $signature) = @_; 11 | $signature eq $class->sign($algorithm, $message, $key); 12 | } 13 | 14 | package __TEST__::FOO::BAR; 15 | 16 | sub sign { 17 | my ($class, $algorithm, $message, $key) = @_; 18 | return pack 'H*' => join \0, $algorithm, $message, $key; 19 | } 20 | 21 | sub verify { 22 | my ($class, $algorithm, $message, $key, $signature) = @_; 23 | $signature eq $class->sign($algorithm, $message, $key); 24 | } 25 | 26 | package main; 27 | 28 | use strict; 29 | use warnings; 30 | use t::Util; 31 | use Test::More; 32 | use JSON::WebToken; 33 | 34 | JSON::WebToken->add_signing_algorithm(FOO => '__FOO__'); 35 | 36 | test_encode_decode( 37 | desc => 'using JSON::WebToken::Crypt::__FOO__', 38 | input => { 39 | claims => { foo => 'bar' }, 40 | secret => 'secret', 41 | algorithm => 'FOO', 42 | }, 43 | ); 44 | 45 | JSON::WebToken->add_signing_algorithm(BAR => '+__TEST__::FOO::BAR'); 46 | 47 | test_encode_decode( 48 | desc => 'using __TEST__::FOO::BAR', 49 | input => { 50 | claims => { foo => 'bar' }, 51 | secret => 'secret', 52 | algorithm => 'BAR', 53 | }, 54 | ); 55 | 56 | done_testing; 57 | -------------------------------------------------------------------------------- /t/04_secret_cb.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use JSON::WebToken; 5 | 6 | my $secret_map = { 7 | joe => "joe's secret", 8 | smith => "smith's secret", 9 | }; 10 | 11 | sub test_secret_cb { 12 | my %specs = @_; 13 | my ($claims, $desc) = @specs{qw/claims desc/}; 14 | 15 | my $secret = $secret_map->{$claims->{iss}}; 16 | my $secret_cb = sub { 17 | my ($header, $claims) = @_; 18 | $secret_map->{$claims->{iss}}; 19 | }; 20 | 21 | subtest $desc => sub { 22 | my $jwt = encode_jwt $claims, $secret; 23 | my $data = decode_jwt $jwt, $secret_cb; 24 | is_deeply $data, $claims; 25 | }; 26 | } 27 | 28 | test_secret_cb( 29 | claims => { 30 | iss => 'joe', 31 | exp => time + 30, 32 | foo => 'bar', 33 | }, 34 | desc => 'joe', 35 | ); 36 | 37 | test_secret_cb( 38 | claims => { 39 | iss => 'smith', 40 | exp => time + 30, 41 | foo => 'bar', 42 | }, 43 | desc => 'smith', 44 | ); 45 | 46 | done_testing; 47 | -------------------------------------------------------------------------------- /t/99_exception.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use JSON::WebToken; 5 | use JSON::WebToken::Constants; 6 | 7 | subtest 'without claims and secret' => sub { 8 | eval { encode_jwt }; 9 | like $@, qr/Usage: JSON::WebToken->encode/; 10 | is $@->code, ERROR_JWT_INVALID_PARAMETER; 11 | }; 12 | 13 | subtest 'without secret' => sub { 14 | eval { encode_jwt { foo => 'bar' } }; 15 | like $@, qr/secret must be specified/; 16 | is $@->code, ERROR_JWT_MISSING_SECRET; 17 | }; 18 | 19 | subtest 'claims is not HASH' => sub { 20 | eval { encode_jwt [], 'secret' }; 21 | like $@, qr/Usage: JSON::WebToken->encode/; 22 | is $@->code, ERROR_JWT_INVALID_PARAMETER; 23 | }; 24 | 25 | subtest 'not supported algorithm' => sub { 26 | eval { 27 | encode_jwt { foo => 'bar' }, 'secret', 'XXXX'; 28 | }; 29 | like $@, qr/`XXXX` is Not supported siging algorithm/; 30 | is $@->code, ERROR_JWT_NOT_SUPPORTED_SIGNING_ALGORITHM; 31 | }; 32 | 33 | subtest 'without jwt' => sub { 34 | eval { decode_jwt }; 35 | like $@, qr/Usage: JSON::WebToken->decode/; 36 | is $@->code, ERROR_JWT_INVALID_PARAMETER; 37 | }; 38 | 39 | subtest 'too many segments' => sub { 40 | eval { decode_jwt 'x.y.z.foo.bar', 'secret' }; 41 | like $@, qr/Not enough or too many segments/; 42 | is $@->code, ERROR_JWT_INVALID_SEGMENT_COUNT; 43 | }; 44 | 45 | subtest 'not enough segments' => sub { 46 | eval { decode_jwt 'x', 'secret' }; 47 | like $@, qr/Not enough or too many segments/; 48 | is $@->code, ERROR_JWT_INVALID_SEGMENT_COUNT; 49 | }; 50 | 51 | subtest 'invalid segments' => sub { 52 | eval { decode_jwt 'x.y.z', 'secret' }; 53 | like $@, qr/Invalid segment encoding/; 54 | is $@->code, ERROR_JWT_INVALID_SEGMENT_ENCODING; 55 | }; 56 | 57 | subtest 'invalid signature' => sub { 58 | my $jwt = encode_jwt { foo => 'bar' }, 'secret'; 59 | eval { decode_jwt "$jwt-xxxx", 'foo' }; 60 | like $@, qr/Invalid signature/; 61 | is $@->code, ERROR_JWT_INVALID_SIGNATURE; 62 | }; 63 | 64 | subtest 'unacceptable algorithm' => sub { 65 | my $jwt = encode_jwt { foo => 'bar' }, '', 'none'; 66 | eval { decode_jwt "$jwt"."xxx", 'foo' }; 67 | like $@, qr/Algorithm "none" is not acceptable/; 68 | is $@->code, ERROR_JWT_UNACCEPTABLE_ALGORITHM; 69 | }; 70 | 71 | subtest 'deprecated: accept_algorithm_none' => sub { 72 | my $jwt = encode_jwt { foo => 'bar' }, '', 'none'; 73 | ok decode_jwt $jwt, "", 1, 1; 74 | eval { decode_jwt "$jwt", "", 1, 0 }; 75 | like $@, qr/Algorithm "none" is not acceptable/; 76 | is $@->code, ERROR_JWT_UNACCEPTABLE_ALGORITHM; 77 | }; 78 | 79 | subtest 'unacceptable algorithm' => sub { 80 | my $jwt = encode_jwt { foo => 'bar' }, 'secret', 'HS256'; 81 | ok decode_jwt "$jwt", 'secret', 1, ["HS256"]; 82 | ok decode_jwt "$jwt", 'secret', 1, "HS256"; 83 | eval { decode_jwt "$jwt", 'secret', 1, ["RS256"] }; 84 | like $@, qr/Algorithm "HS256" is not acceptable. Followings are accepted:RS256/; 85 | is $@->code, ERROR_JWT_UNACCEPTABLE_ALGORITHM; 86 | 87 | 88 | }; 89 | 90 | subtest 'signature must be empty' => sub { 91 | my $jwt = encode_jwt { foo => 'bar' }, '', 'none'; 92 | eval { decode_jwt "$jwt"."xxx", 'foo', 1, "none" }; 93 | like $@, qr/Signature must be the empty string when alg is none/; 94 | is $@->code, ERROR_JWT_UNWANTED_SIGNATURE; 95 | }; 96 | 97 | subtest 'is_verify true, but without secret' => sub { 98 | my $jwt = encode_jwt { foo => 'bar' }, 'secret'; 99 | eval { decode_jwt $jwt }; 100 | like $@, qr/secret must be specified/; 101 | is $@->code, ERROR_JWT_MISSING_SECRET; 102 | }; 103 | 104 | subtest 'is_verify false' => sub { 105 | my $jwt = encode_jwt { foo => 'bar' }, 'secret'; 106 | my $got = decode_jwt "$jwt-xxxx", undef, 0; 107 | is_deeply $got, { foo => 'bar' }; 108 | }; 109 | 110 | done_testing; 111 | -------------------------------------------------------------------------------- /t/Util.pm: -------------------------------------------------------------------------------- 1 | package t::Util; 2 | 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | use Exporter 'import'; 7 | 8 | use JSON::WebToken; 9 | 10 | our @EXPORT = qw(test_encode_decode); 11 | 12 | sub test_encode_decode { 13 | my %specs = @_; 14 | my ($desc, $input, $expects_exception) = 15 | @specs{qw/desc input expects_exception/}; 16 | 17 | my ($claims, $secret, $public_key, $algorithm, $header_fields) = 18 | @$input{qw/claims secret public_key algorithm header_fields/}; 19 | $public_key ||= $secret; 20 | 21 | my $test = sub { 22 | my $jwt = encode_jwt $claims, $secret, $algorithm, $header_fields; 23 | note "jwt: $jwt"; 24 | return decode_jwt $jwt, $public_key, $algorithm; 25 | }; 26 | subtest $desc => sub { 27 | unless ($expects_exception) { 28 | my $got = $test->(); 29 | is_deeply $got, $claims; 30 | } 31 | else { 32 | eval { $test->() }; 33 | like $@, qr/$expects_exception/; 34 | } 35 | }; 36 | } 37 | 38 | 1; 39 | __END__ 40 | -------------------------------------------------------------------------------- /t/spec/draft-ietf-jose-json-web-signature-08-A1.hmac_sha256.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use Test::Mock::Guard qw(mock_guard); 5 | use JSON; 6 | use JSON::WebToken; 7 | 8 | my $header = pack 'C*' => @{ [ 9 | 123, 34, 116, 121, 112, 34, 58, 34, 74, 87, 10 | 84, 34, 44, 13, 10, 32, 34, 97, 108, 103, 11 | 34, 58, 34, 72, 83, 50, 53, 54, 34, 125 12 | ] }; 13 | 14 | my $claims = pack 'C*' => @{ [ 15 | 123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 16 | 10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 17 | 57, 51, 56, 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 18 | 47, 47, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 47, 19 | 105, 115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125 20 | ] }; 21 | 22 | my $secret = pack 'C*' => @{ [ 23 | 3, 35, 53, 75, 43, 15, 165, 188, 131, 126, 6, 101, 119, 123, 24 | 166, 143, 90, 179, 40, 230, 240, 84, 201, 40, 169, 15, 132, 178, 25 | 210, 80, 46, 191, 211, 251, 90, 146, 210, 6, 71, 239, 150, 138, 26 | 180, 195, 119, 98, 61, 34, 61, 46, 33, 114, 5, 46, 79, 8, 27 | 192, 205, 154, 245, 103, 208, 128, 163 28 | ] }; 29 | 30 | my $guard = mock_guard('JSON::WebToken' => { 31 | encode_json => sub { 32 | my $array = [$header, $claims]; 33 | sub { shift @$array }; 34 | }->(), 35 | }); 36 | 37 | my $jwt = JSON::WebToken->encode({}, $secret); 38 | is $jwt, join q{}, qw{ 39 | eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9 40 | . 41 | eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt 42 | cGxlLmNvbS9pc19yb290Ijp0cnVlfQ 43 | . 44 | dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk 45 | }; 46 | 47 | my $data = JSON::WebToken->decode($jwt, $secret); 48 | is_deeply $data, decode_json($claims); 49 | 50 | done_testing; 51 | -------------------------------------------------------------------------------- /t/spec/draft-ietf-jose-json-web-signature-08-A2.rsa_sha256.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use Test::Requires qw( 5 | Crypt::OpenSSL::RSA 6 | Crypt::OpenSSL::Bignum 7 | ); 8 | use Test::Mock::Guard qw(mock_guard); 9 | use JSON; 10 | use JSON::WebToken; 11 | 12 | my $header = pack 'C*' => @{ [123, 34, 97, 108, 103, 34, 58, 34, 82, 83, 50, 53, 54, 34, 125] }; 13 | 14 | my $claims = pack 'C*' => @{ [ 15 | 123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 16 | 10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 17 | 57, 51, 56, 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 18 | 47, 47, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 47, 19 | 105, 115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125 20 | ] }; 21 | 22 | my $singing_input = pack 'C*' => @{ [ 23 | 101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 83, 85, 122, 24 | 73, 49, 78, 105, 74, 57, 46, 101, 121, 74, 112, 99, 51, 77, 25 | 105, 79, 105, 74, 113, 98, 50, 85, 105, 76, 65, 48, 75, 73, 26 | 67, 74, 108, 101, 72, 65, 105, 79, 106, 69, 122, 77, 68, 65, 27 | 52, 77, 84, 107, 122, 79, 68, 65, 115, 68, 81, 111, 103, 73, 28 | 109, 104, 48, 100, 72, 65, 54, 76, 121, 57, 108, 101, 71, 70, 29 | 116, 99, 71, 120, 108, 76, 109, 78, 118, 98, 83, 57, 112, 99, 30 | 49, 57, 121, 98, 50, 57, 48, 73, 106, 112, 48, 99, 110, 86, 31 | 108, 102, 81 32 | ] }; 33 | 34 | my $rsa = do { 35 | my $n = pack 'C*' => @{ [ 36 | 161, 248, 22, 10, 226, 227, 201, 180, 101, 206, 141, 45, 101, 98, 37 | 99, 54, 43, 146, 125, 190, 41, 225, 240, 36, 119, 252, 22, 37, 38 | 204, 144, 161, 54, 227, 139, 217, 52, 151, 197, 182, 234, 99, 221, 39 | 119, 17, 230, 124, 116, 41, 249, 86, 176, 251, 138, 143, 8, 154, 40 | 220, 75, 105, 137, 60, 193, 51, 63, 83, 237, 208, 25, 184, 119, 41 | 132, 37, 47, 236, 145, 79, 228, 133, 119, 105, 89, 75, 234, 66, 42 | 128, 211, 44, 15, 85, 191, 98, 148, 79, 19, 3, 150, 188, 110, 43 | 155, 223, 110, 189, 210, 189, 163, 103, 142, 236, 160, 198, 104, 247, 44 | 1, 179, 141, 191, 251, 56, 200, 52, 44, 226, 254, 109, 39, 250, 45 | 222, 74, 90, 72, 116, 151, 157, 212, 185, 207, 154, 222, 196, 199, 46 | 91, 5, 133, 44, 44, 15, 94, 248, 165, 193, 117, 3, 146, 249, 47 | 68, 232, 237, 100, 193, 16, 198, 182, 71, 96, 154, 164, 120, 58, 48 | 235, 156, 108, 154, 215, 85, 49, 48, 80, 99, 139, 131, 102, 92, 49 | 111, 111, 122, 130, 163, 150, 112, 42, 31, 100, 27, 130, 211, 235, 50 | 242, 57, 34, 25, 73, 31, 182, 134, 135, 44, 87, 22, 245, 10, 51 | 248, 53, 141, 154, 139, 157, 23, 195, 64, 114, 143, 127, 135, 216, 52 | 154, 24, 216, 252, 171, 103, 173, 132, 89, 12, 46, 207, 117, 147, 53 | 57, 54, 60, 7, 3, 77, 111, 96, 111, 158, 33, 224, 84, 86, 54 | 202, 229, 233, 161 55 | ] }; 56 | my $e = pack 'C*' => @{ [1, 0, 1] }; 57 | my $d = pack 'C*' => @{ [ 58 | 18, 174, 113, 164, 105, 205, 10, 43, 195, 126, 82, 108, 69, 0, 59 | 87, 31, 29, 97, 117, 29, 100, 233, 73, 112, 123, 98, 89, 15, 60 | 157, 11, 165, 124, 150, 60, 64, 30, 63, 207, 47, 44, 211, 189, 61 | 236, 136, 229, 3, 191, 198, 67, 155, 11, 40, 200, 47, 125, 55, 62 | 151, 103, 31, 82, 19, 238, 216, 193, 90, 37, 216, 213, 206, 160, 63 | 2, 94, 227, 171, 46, 139, 127, 121, 33, 111, 198, 59, 234, 86, 64 | 39, 83, 180, 6, 68, 198, 161, 81, 39, 217, 178, 149, 69, 64, 65 | 160, 187, 225, 163, 5, 86, 152, 45, 78, 159, 222, 95, 100, 37, 66 | 241, 77, 75, 113, 52, 65, 181, 93, 199, 59, 155, 74, 237, 204, 67 | 146, 172, 227, 146, 126, 55, 245, 125, 12, 253, 94, 117, 129, 250, 68 | 81, 44, 143, 73, 97, 169, 235, 11, 128, 248, 168, 7, 70, 114, 69 | 138, 85, 255, 70, 71, 31, 52, 37, 6, 59, 157, 83, 100, 47, 70 | 94, 222, 30, 132, 214, 19, 8, 26, 250, 92, 34, 208, 81, 40, 71 | 91, 214, 59, 148, 59, 86, 93, 137, 138, 5, 104, 84, 19, 229, 72 | 60, 60, 108, 101, 37, 255, 31, 227, 78, 61, 220, 112, 240, 213, 73 | 100, 80, 253, 164, 139, 161, 46, 16, 78, 157, 235, 159, 184, 24, 74 | 129, 225, 196, 189, 242, 93, 146, 71, 244, 80, 200, 101, 146, 121, 75 | 104, 231, 115, 52, 244, 65, 79, 117, 167, 80, 225, 57, 84, 110, 76 | 58, 138, 115, 157 77 | ] }; 78 | 79 | my $rsa = Crypt::OpenSSL::RSA->new_key_from_parameters(map { 80 | Crypt::OpenSSL::Bignum->new_from_bin($_) 81 | } $n, $e, $d); 82 | $rsa->use_sha256_hash; 83 | 84 | $rsa; 85 | }; 86 | 87 | my $S = pack 'C*' => @{ [ 88 | 112, 46, 33, 137, 67, 232, 143, 209, 30, 181, 216, 45, 191, 120, 89 | 69, 243, 65, 6, 174, 27, 129, 255, 247, 115, 17, 22, 173, 209, 90 | 113, 125, 131, 101, 109, 66, 10, 253, 60, 150, 238, 221, 115, 162, 91 | 102, 62, 81, 102, 104, 123, 0, 11, 135, 34, 110, 1, 135, 237, 92 | 16, 115, 249, 69, 229, 130, 173, 252, 239, 22, 216, 90, 121, 142, 93 | 232, 198, 109, 219, 61, 184, 151, 91, 23, 208, 148, 2, 190, 237, 94 | 213, 217, 217, 112, 7, 16, 141, 178, 129, 96, 213, 248, 4, 12, 95 | 167, 68, 87, 98, 184, 31, 190, 127, 249, 217, 46, 10, 231, 111, 96 | 36, 242, 91, 51, 187, 230, 244, 74, 230, 30, 177, 4, 10, 203, 97 | 32, 4, 77, 62, 249, 18, 142, 212, 1, 48, 121, 91, 212, 189, 98 | 59, 65, 238, 202, 208, 102, 171, 101, 25, 129, 253, 228, 141, 247, 99 | 127, 55, 45, 195, 139, 159, 175, 221, 59, 239, 177, 139, 93, 163, 100 | 204, 60, 46, 176, 47, 158, 58, 65, 214, 18, 202, 173, 21, 145, 101 | 18, 115, 160, 95, 35, 185, 232, 56, 250, 175, 132, 157, 105, 132, 102 | 41, 239, 90, 30, 136, 121, 130, 54, 195, 212, 14, 96, 69, 34, 103 | 165, 68, 200, 242, 122, 122, 45, 184, 6, 99, 209, 108, 247, 202, 104 | 234, 86, 222, 64, 92, 178, 33, 90, 69, 178, 194, 85, 102, 181, 105 | 90, 193, 167, 72, 160, 112, 223, 200, 163, 42, 70, 149, 67, 208, 106 | 25, 238, 251, 71 107 | ] }; 108 | 109 | is $rsa->sign($singing_input), $S; 110 | ok $rsa->verify($singing_input, $S); 111 | 112 | my $guard = mock_guard( 113 | 'JSON::WebToken' => { 114 | encode_json => sub { 115 | my $array = [$header, $claims]; 116 | sub { shift @$array }; 117 | }->(), 118 | }, 119 | 'Crypt::OpenSSL::RSA' => { 120 | new_private_key => $rsa, 121 | }, 122 | ); 123 | 124 | my $public_key = $rsa->get_public_key_string; 125 | my $jwt = JSON::WebToken->encode({}, 'dummy', 'RS256'); 126 | is $jwt, join q{}, qw{ 127 | eyJhbGciOiJSUzI1NiJ9 128 | . 129 | eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt 130 | cGxlLmNvbS9pc19yb290Ijp0cnVlfQ 131 | . 132 | cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7 133 | AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4 134 | BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K 135 | 0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv 136 | hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB 137 | p0igcN_IoypGlUPQGe77Rw 138 | }; 139 | 140 | my $data = JSON::WebToken->decode($jwt, $public_key); 141 | is_deeply $data, decode_json($claims); 142 | 143 | done_testing; 144 | -------------------------------------------------------------------------------- /t/spec/draft-ietf-oauth-json-web-token-06-3.1.example.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use t::Util; 4 | use Test::More; 5 | use Test::Mock::Guard qw(mock_guard); 6 | 7 | use JSON; 8 | 9 | my $expects = join q{}, qw{ 10 | eyJhbGciOiJub25lIn0 11 | . 12 | eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt 13 | cGxlLmNvbS9pc19yb290Ijp0cnVlfQ 14 | . 15 | }; 16 | 17 | my $header = pack 'C*' => @{ [ 18 | 123, 34, 97, 108, 103, 34, 58, 34, 110, 111, 110, 101, 34, 125 19 | ] }; 20 | 21 | my $claims = pack 'C*' => @{ [ 22 | 123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 23 | 10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 24 | 57, 51, 56, 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 25 | 47, 47, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 47, 26 | 105, 115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125 27 | ] }; 28 | 29 | my $secret = ''; 30 | 31 | my $guard = mock_guard('JSON::WebToken' => { 32 | encode_json => sub { 33 | my $array = [$header, $claims]; 34 | sub { shift @$array }; 35 | }->(), 36 | }); 37 | 38 | my $jwt = JSON::WebToken->encode({}, $secret, 'none'); 39 | is $jwt, $expects; 40 | 41 | my $data = JSON::WebToken->decode($jwt, $secret, 1, 1); 42 | is_deeply $data, decode_json $claims; 43 | 44 | done_testing; 45 | -------------------------------------------------------------------------------- /tmp/MEMO: -------------------------------------------------------------------------------- 1 | 1. JWE Header 2 | 2. JWE Encrypted Key 3 | 3. JWE Ciphertext 4 | 4. JWE Integrity Value 5 | 6 | 3. Cryptographic Algorithms for JWS 7 | 3.1. "alg" (Algorithm) Header Parameter Values for JWS 8 | 9 | +--------------+--------------------------------+-------------------+ 10 | | alg | Digital Signature or MAC | Implementation | 11 | | Parameter | Algorithm | Requirements | 12 | | Value | | | 13 | +--------------+--------------------------------+-------------------+ 14 | | HS256 | HMAC using SHA-256 hash | REQUIRED | Digest::SHA (hmac_sha256) 15 | | | algorithm | | 16 | | HS384 | HMAC using SHA-384 hash | OPTIONAL | Digest::SHA (hmac_sha384) 17 | | | algorithm | | 18 | | HS512 | HMAC using SHA-512 hash | OPTIONAL | Digest::SHA (hmac_sha512) 19 | | | algorithm | | 20 | | RS256 | RSASSA using SHA-256 hash | RECOMMENDED | Crypt::OpenSSL::RSA (use_sha256_hash) 21 | | | algorithm | | 22 | | RS384 | RSASSA using SHA-384 hash | OPTIONAL | Crypt::OpenSSL::RSA (use_sha384_hash) 23 | | | algorithm | | 24 | | RS512 | RSASSA using SHA-512 hash | OPTIONAL | Crypt::OpenSSL::RSA (use_sha512_hash) 25 | | | algorithm | | 26 | | ES256 | ECDSA using P-256 curve and | RECOMMENDED+ | Crypt::OpenSSL::EC 27 | | | SHA-256 hash algorithm | | 28 | | ES384 | ECDSA using P-384 curve and | OPTIONAL | Crypt::OpenSSL::EC 29 | | | SHA-384 hash algorithm | | 30 | | ES512 | ECDSA using P-521 curve and | OPTIONAL | Crypt::OpenSSL::EC 31 | | | SHA-512 hash algorithm | | 32 | | none | No digital signature or MAC | REQUIRED | 33 | | | value included | | 34 | +--------------+--------------------------------+-------------------+ 35 | 36 | 4. Cryptographic Algorithms for JWE 37 | 4.1. "alg" (Algorithm) Header Parameter Values for JWE 38 | 39 | +-----------+--------------------------------------+----------------+ 40 | | alg | Key Encryption or Agreement | Implementation | 41 | | Parameter | Algorithm | Requirements | 42 | | Value | | | 43 | +-----------+--------------------------------------+----------------+ 44 | | RSA1_5 | RSAES-PKCS1-V1_5 [RFC3447] | REQUIRED | Crypt::OpenSSL::RSA (use_pkcs1_padding) 45 | | RSA-OAEP | RSAES using Optimal Asymmetric | OPTIONAL | Crypt::RSA::ES::OAEP ? 46 | | | Encryption Padding (OAEP) [RFC3447], | | 47 | | | with the default parameters | | 48 | | | specified by RFC 3447 in Section | | 49 | | | A.2.1 | | 50 | | ECDH-ES | Elliptic Curve Diffie-Hellman | RECOMMENDED+ | ? 51 | | | Ephemeral Static [RFC6090], and | | 52 | | | using the Concat KDF, as defined in | | 53 | | | Section 5.8.1 of [NIST.800-56A], | | 54 | | | where the Digest Method is SHA-256 | | 55 | | | and all OtherInfo parameters are the | | 56 | | | empty bit string | | 57 | | A128KW | Advanced Encryption Standard (AES) | RECOMMENDED | ? 58 | | | Key Wrap Algorithm [RFC3394] using | | 59 | | | 128 bit keys | | 60 | | A256KW | AES Key Wrap Algorithm using 256 bit | RECOMMENDED | ? 61 | | | keys | | 62 | +-----------+--------------------------------------+----------------+ 63 | 64 | 4.2. "enc" (Encryption Method) Header Parameter Values for JWE 65 | 66 | +-----------+--------------------------------------+----------------+ 67 | | enc | Block Encryption Algorithm | Implementation | 68 | | Parameter | | Requirements | 69 | | Value | | | 70 | +-----------+--------------------------------------+----------------+ 71 | | A128CBC | Advanced Encryption Standard (AES) | REQUIRED | Crypt::OpenSSL::AES with Crypt::CBC 72 | | | in Cipher Block Chaining (CBC) mode | | 73 | | | with PKCS #5 padding [AES] | | 74 | | | [NIST.800-38A] using 128 bit keys | | 75 | | A256CBC | AES in CBC mode with PKCS #5 padding | REQUIRED | Crypt::OpenSSL::AES with Crypt::CBC 76 | | | using 256 bit keys | | 77 | | A128GCM | AES in Galois/Counter Mode (GCM) | RECOMMENDED | Crypt::GCM 78 | | | [AES] [NIST.800-38D] using 128 bit | | 79 | | | keys | | 80 | | A256GCM | AES GCM using 256 bit keys | RECOMMENDED | Crypt::GCM 81 | +-----------+--------------------------------------+----------------+ 82 | 83 | 5. Cryptographic Algorithms for JWK 84 | 5.1. "alg" (Algorithm Family) Parameter Values for JWK 85 | 86 | +-----------------+-------------------------+-----------------------+ 87 | | alg Parameter | Algorithm Family | Implementation | 88 | | Value | | Requirements | 89 | +-----------------+-------------------------+-----------------------+ 90 | | EC | Elliptic Curve [DSS] | RECOMMENDED+ | 91 | | | key family | | 92 | | RSA | RSA [RFC3447] key | REQUIRED | 93 | | | family | | 94 | +-----------------+-------------------------+-----------------------+ 95 | 96 | 5.2. JWK Parameters for Elliptic Curve Keys 97 | 5.2.1. "crv" (Curve) Parameter 98 | 99 | o "P-256" 100 | 101 | o "P-384" 102 | 103 | o "P-521" 104 | 105 | 5.2.2. "x" (X Coordinate) Parameter 106 | 107 | 5.2.3. "y" (Y Coordinate) Parameter 108 | 109 | 5.3. JWK Parameters for RSA Keys 110 | 111 | 5.3.1. "mod" (Modulus) Parameter 112 | 113 | 5.3.2. "exp" (Exponent) Parameter 114 | 115 | -------------------------------------------------------------------------------- /tmp/spec.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use lib 'lib'; 4 | use Test::More; 5 | 6 | use JSON::WebToken; 7 | use Crypt::CBC; 8 | use Crypt::OpenSSL::AES; 9 | use Crypt::OpenSSL::RSA; 10 | use Crypt::OpenSSL::Bignum; 11 | use Digest::SHA qw(hmac_sha256); 12 | 13 | my $input = join q{} => ( 14 | 'eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDIiwiaW50IjoiSFMyNTYiLCJpdiI6IkF4W', 15 | 'ThEQ3REYUdsc2JHbGpiM1JvWlEifQ', 16 | '.', 17 | 'VjBkk22MjrFUMUl8ItbS8CjKjku4HQz4RiHD0eVG4dir-7XbDkPr1q6YtnN1X-av1EKmEnsrb', 18 | 'hSxTvqtY4oEbWKLoEQ7zVm_0BW-rnwxdwrj4QJrhXGnqIL6bC4waZVJqYhVQIahVWSQsCRcS1', 19 | 'oYXA-2GhT6rk91y118DUkhGDsvdK2_hQsNGE6BQVN1i-XwUoz5sM6_0PRQ1FsYnJATMjVZfa4', 20 | 'otHiooZ_KcOlSWIDxhMDqfPOu60--1ej0eZByO7Ar_IZvzPAWqJ9agGFQIVGRZviXhN0WeErq', 21 | '9fVTcgeSUPsmurRSTYhTrNFLojqPqqk8pI61kn8GmZxA80-RUQ', 22 | '.', 23 | '7kLQQst655TUxmDzysjRLXnD-nfLK5EQK7ODAUkwxc0aRb9NOgu0EMJgOR6Vz8eNbaf8six_O', 24 | 'P6BRyUTYrCkH73-inD6Rc-7vc9eC5fcfSM', 25 | '.', 26 | 'COyXNSm-CdfAL22WIKcoyCgQwb85aLW3ttDkzNj_1Wg', 27 | ); 28 | 29 | my $plaintext = pack 'C*' => @{ [ 30 | 78, 111, 119, 32, 105, 115, 32, 116, 104, 101, 32, 116, 105, 109, 31 | 101, 32, 102, 111, 114, 32, 97, 108, 108, 32, 103, 111, 111, 100, 32 | 32, 109, 101, 110, 32, 116, 111, 32, 99, 111, 109, 101, 32, 116, 33 | 111, 32, 116, 104, 101, 32, 97, 105, 100, 32, 111, 102, 32, 116, 34 | 104, 101, 105, 114, 32, 99, 111, 117, 110, 116, 114, 121, 46 35 | ] }; 36 | 37 | my $iv = pack 'C*' => @{ [ 38 | 3, 22, 60, 12, 43, 67, 104, 105, 108, 108, 105, 99, 111, 116, 104, 101 39 | ] }; 40 | 41 | my $cmk = pack 'C*' => @{ [ 42 | 4, 211, 31, 197, 84, 157, 252, 254, 11, 100, 157, 250, 43 | 63, 170, 106, 206, 107, 124, 212, 45, 111, 107, 9, 219, 44 | 200, 177, 0, 240, 143, 156, 44, 207 45 | ] }; 46 | 47 | my $rsa = do { 48 | my $n = pack 'C*' => @{ [ 49 | 177, 119, 33, 13, 164, 30, 108, 121, 207, 136, 107, 50 | 242, 12, 224, 19, 226, 198, 134, 17, 71, 173, 75, 42, 51 | 61, 48, 162, 206, 161, 97, 108, 185, 234, 226, 219, 52 | 118, 206, 118, 5, 169, 224, 60, 181, 90, 85, 51, 123, 53 | 6, 224, 4, 122, 29, 230, 151, 12, 244, 127, 121, 25, 54 | 4, 85, 220, 144, 215, 110, 130, 17, 68, 228, 129, 55 | 138, 7, 130, 231, 40, 212, 214, 17, 179, 28, 124, 56 | 151, 178, 207, 20, 14, 154, 222, 113, 176, 24, 198, 57 | 73, 211, 113, 9, 33, 178, 80, 13, 25, 21, 25, 153, 58 | 212, 206, 67, 154, 147, 70, 194, 192, 183, 160, 83, 59 | 98, 236, 175, 85, 23, 97, 75, 199, 177, 73, 145, 50, 60 | 253, 206, 32, 179, 254, 236, 190, 82, 73, 67, 129, 61 | 253, 252, 220, 108, 136, 138, 11, 192, 1, 36, 239, 62 | 228, 55, 81, 113, 17, 25, 140, 63, 239, 146, 3, 172, 63 | 96, 60, 227, 233, 64, 255, 224, 173, 225, 228, 229, 64 | 92, 112, 72, 99, 97, 26, 87, 187, 123, 46, 50, 90, 65 | 202, 117, 73, 10, 153, 47, 224, 178, 163, 77, 48, 46, 66 | 154, 33, 148, 34, 228, 33, 172, 216, 89, 46, 225, 67 | 127, 68, 146, 234, 30, 147, 54, 146, 5, 133, 45, 78, 68 | 254, 85, 55, 75, 213, 86, 194, 218, 215, 163, 189, 69 | 194, 54, 6, 83, 36, 18, 153, 53, 7, 48, 89, 35, 66, 70 | 144, 7, 65, 154, 13, 97, 75, 55, 230, 132, 3, 13, 71 | 239, 71 72 | ] }; 73 | my $e = pack 'C*' => @{ [1, 0, 1] }; 74 | my $d = pack 'C*' => @{ [ 75 | 84, 80, 150, 58, 165, 235, 242, 123, 217, 55, 38, 76 | 154, 36, 181, 221, 156, 211, 215, 100, 164, 90, 88, 77 | 40, 228, 83, 148, 54, 122, 4, 16, 165, 48, 76, 194, 78 | 26, 107, 51, 53, 179, 165, 31, 18, 198, 173, 78, 61, 79 | 56, 97, 252, 158, 140, 80, 63, 25, 223, 156, 36, 203, 80 | 214, 252, 120, 67, 180, 167, 3, 82, 243, 25, 97, 214, 81 | 83, 133, 69, 16, 104, 54, 160, 200, 41, 83, 164, 187, 82 | 70, 153, 111, 234, 242, 158, 175, 28, 198, 48, 211, 83 | 45, 148, 58, 23, 62, 227, 74, 52, 117, 42, 90, 41, 84 | 249, 130, 154, 80, 119, 61, 26, 193, 40, 125, 10, 85 | 152, 174, 227, 225, 205, 32, 62, 66, 6, 163, 100, 99, 86 | 219, 19, 253, 25, 105, 80, 201, 29, 252, 157, 237, 87 | 69, 1, 80, 171, 167, 20, 196, 156, 109, 249, 88, 0, 88 | 3, 152, 38, 165, 72, 87, 6, 152, 71, 156, 214, 16, 89 | 71, 30, 82, 51, 103, 76, 218, 63, 9, 84, 163, 249, 90 | 91, 215, 44, 238, 85, 101, 240, 148, 1, 82, 224, 91, 91 | 135, 105, 127, 84, 171, 181, 152, 210, 183, 126, 24, 92 | 46, 196, 90, 173, 38, 245, 219, 186, 222, 27, 240, 93 | 212, 194, 15, 66, 135, 226, 178, 190, 52, 245, 74, 94 | 65, 224, 81, 100, 85, 25, 204, 165, 203, 187, 175, 95 | 84, 100, 82, 15, 11, 23, 202, 151, 107, 54, 41, 207, 96 | 3, 136, 229, 134, 131, 93, 139, 50, 182, 204, 93, 97 | 130, 89 98 | ] }; 99 | 100 | my $rsa = Crypt::OpenSSL::RSA->new_key_from_parameters(map { 101 | Crypt::OpenSSL::Bignum->new_from_bin($_); 102 | } $n, $e, $d); 103 | $rsa->use_pkcs1_padding; 104 | $rsa; 105 | }; 106 | 107 | my $result = pack 'C*' => @{ [ 108 | 32, 242, 63, 207, 94, 246, 133, 37, 135, 48, 88, 4, 15, 193, 109 | 6, 244, 51, 58, 132, 133, 212, 255, 163, 90, 59, 80, 200, 152, 110 | 41, 244, 188, 215, 174, 160, 26, 188, 227, 180, 165, 234, 172, 63, 111 | 24, 116, 152, 28, 149, 16, 94, 213, 201, 171, 180, 191, 11, 21, 112 | 149, 172, 143, 54, 194, 58, 206, 201, 164, 28, 107, 155, 75, 101, 113 | 22, 92, 227, 144, 95, 40, 119, 170, 7, 36, 225, 40, 141, 186, 114 | 213, 7, 175, 16, 174, 122, 75, 32, 48, 193, 119, 202, 41, 152, 115 | 210, 190, 68, 57, 119, 4, 197, 74, 7, 242, 239, 170, 204, 73, 116 | 75, 213, 202, 113, 216, 18, 23, 66, 106, 208, 69, 244, 117, 147, 117 | 2, 37, 207, 199, 184, 96, 102, 44, 70, 212, 87, 143, 253, 0, 118 | 166, 59, 41, 115, 217, 80, 165, 87, 38, 5, 9, 184, 202, 68, 119 | 67, 176, 4, 87, 254, 166, 227, 88, 124, 238, 249, 75, 114, 205, 120 | 148, 149, 45, 78, 193, 134, 64, 189, 168, 76, 170, 76, 176, 72, 121 | 148, 77, 215, 159, 146, 55, 189, 213, 85, 253, 135, 200, 59, 247, 122 | 79, 37, 22, 200, 32, 110, 53, 123, 54, 39, 9, 178, 231, 238, 123 | 95, 25, 211, 143, 87, 220, 88, 138, 209, 13, 227, 72, 58, 102, 124 | 164, 136, 241, 14, 14, 45, 32, 77, 44, 244, 162, 239, 150, 248, 125 | 181, 138, 251, 116, 245, 205, 137, 78, 34, 34, 10, 6, 59, 4, 126 | 197, 2, 153, 251 127 | ] }; 128 | 129 | is $rsa->decrypt($rsa->encrypt($cmk)), $cmk; 130 | is $rsa->decrypt($result), $cmk; 131 | 132 | my $cek = pack 'C*' => @{ [ 133 | 249, 255, 87, 218, 224, 223, 221, 53, 204, 121, 166, 130, 195, 184, 134 | 50, 69 135 | ] }; 136 | 137 | my $cik = pack 'C*' => @{ [ 138 | 218, 209, 130, 50, 169, 45, 70, 214, 29, 187, 123, 20, 139 | 3, 158, 111, 122, 182, 94, 57, 133, 245, 76, 97, 44, 140 | 193, 80, 81, 246, 115, 177, 225, 159 141 | ] }; 142 | 143 | my $ciphertext = pack 'C*' => @{ [ 144 | 253, 159, 221, 142, 82, 40, 11, 131, 3, 72, 34, 162, 173, 229, 145 | 146, 217, 183, 173, 139, 132, 58, 137, 33, 182, 82, 49, 110, 141, 146 | 11, 221, 207, 239, 207, 65, 213, 28, 20, 217, 14, 186, 87, 160, 147 | 15, 160, 96, 142, 7, 69, 46, 55, 129, 224, 113, 206, 59, 181, 148 | 7, 188, 255, 15, 16, 59, 180, 107, 75, 0, 217, 175, 254, 8, 149 | 141, 48, 217, 132, 16, 217, 4, 30, 223, 147 150 | ] }; 151 | 152 | my $cbc = Crypt::CBC->new({ 153 | literal_key => 1, 154 | header => 'none', 155 | key => $cek, 156 | keysize => 128 / 8, 157 | iv => $iv, 158 | cipher => 'Crypt::OpenSSL::AES', 159 | }); 160 | my $expects_plaintext = $cbc->decrypt($ciphertext); 161 | is $plaintext, $expects_plaintext; 162 | 163 | my $signing_body = pack 'C*' => @{ [ 164 | 101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 83, 85, 48, 165 | 69, 120, 88, 122, 85, 105, 76, 67, 74, 108, 98, 109, 77, 105, 166 | 79, 105, 74, 66, 77, 84, 73, 52, 81, 48, 74, 68, 73, 105, 167 | 119, 105, 97, 87, 53, 48, 73, 106, 111, 105, 83, 70, 77, 121, 168 | 78, 84, 89, 105, 76, 67, 74, 112, 100, 105, 73, 54, 73, 107, 169 | 70, 52, 87, 84, 104, 69, 81, 51, 82, 69, 89, 85, 100, 115, 170 | 99, 50, 74, 72, 98, 71, 112, 105, 77, 49, 74, 118, 87, 108, 171 | 69, 105, 102, 81, 46, 73, 80, 73, 95, 122, 49, 55, 50, 104, 172 | 83, 87, 72, 77, 70, 103, 69, 68, 56, 69, 71, 57, 68, 77, 173 | 54, 104, 73, 88, 85, 95, 54, 78, 97, 79, 49, 68, 73, 109, 174 | 67, 110, 48, 118, 78, 101, 117, 111, 66, 113, 56, 52, 55, 83, 175 | 108, 54, 113, 119, 95, 71, 72, 83, 89, 72, 74, 85, 81, 88, 176 | 116, 88, 74, 113, 55, 83, 95, 67, 120, 87, 86, 114, 73, 56, 177 | 50, 119, 106, 114, 79, 121, 97, 81, 99, 97, 53, 116, 76, 90, 178 | 82, 90, 99, 52, 53, 66, 102, 75, 72, 101, 113, 66, 121, 84, 179 | 104, 75, 73, 50, 54, 49, 81, 101, 118, 69, 75, 53, 54, 83, 180 | 121, 65, 119, 119, 88, 102, 75, 75, 90, 106, 83, 118, 107, 81, 181 | 53, 100, 119, 84, 70, 83, 103, 102, 121, 55, 54, 114, 77, 83, 182 | 85, 118, 86, 121, 110, 72, 89, 69, 104, 100, 67, 97, 116, 66, 183 | 70, 57, 72, 87, 84, 65, 105, 88, 80, 120, 55, 104, 103, 90, 184 | 105, 120, 71, 49, 70, 101, 80, 95, 81, 67, 109, 79, 121, 108, 185 | 122, 50, 86, 67, 108, 86, 121, 89, 70, 67, 98, 106, 75, 82, 186 | 69, 79, 119, 66, 70, 102, 45, 112, 117, 78, 89, 102, 79, 55, 187 | 53, 83, 51, 76, 78, 108, 74, 85, 116, 84, 115, 71, 71, 81, 188 | 76, 50, 111, 84, 75, 112, 77, 115, 69, 105, 85, 84, 100, 101, 189 | 102, 107, 106, 101, 57, 49, 86, 88, 57, 104, 56, 103, 55, 57, 190 | 48, 56, 108, 70, 115, 103, 103, 98, 106, 86, 55, 78, 105, 99, 191 | 74, 115, 117, 102, 117, 88, 120, 110, 84, 106, 49, 102, 99, 87, 192 | 73, 114, 82, 68, 101, 78, 73, 79, 109, 97, 107, 105, 80, 69, 193 | 79, 68, 105, 48, 103, 84, 83, 122, 48, 111, 117, 45, 87, 45, 194 | 76, 87, 75, 45, 51, 84, 49, 122, 89, 108, 79, 73, 105, 73, 195 | 75, 66, 106, 115, 69, 120, 81, 75, 90, 45, 119, 46, 95, 90, 196 | 95, 100, 106, 108, 73, 111, 67, 52, 77, 68, 83, 67, 75, 105, 197 | 114, 101, 87, 83, 50, 98, 101, 116, 105, 52, 81, 54, 105, 83, 198 | 71, 50, 85, 106, 70, 117, 106, 81, 118, 100, 122, 45, 95, 80, 199 | 81, 100, 85, 99, 70, 78, 107, 79, 117, 108, 101, 103, 68, 54, 200 | 66, 103, 106, 103, 100, 70, 76, 106, 101, 66, 52, 72, 72, 79, 201 | 79, 55, 85, 72, 118, 80, 56, 80, 69, 68, 117, 48, 97, 48, 202 | 115, 65, 50, 97, 95, 45, 67, 73, 48, 119, 50, 89, 81, 81, 203 | 50, 81, 81, 101, 51, 53, 77 204 | ] }; 205 | 206 | my $integrity = pack 'C*' => @{ [ 207 | 115, 141, 100, 225, 62, 30, 2, 0, 130, 183, 173, 230, 208 | 241, 147, 102, 136, 232, 167, 49, 200, 133, 23, 42, 78, 209 | 22, 155, 226, 119, 184, 186, 15, 73 210 | ] }; 211 | 212 | my $expects_integrity = hmac_sha256 $signing_body, $cik; 213 | is $integrity, $expects_integrity; 214 | 215 | 216 | done_testing; 217 | 218 | # IPI_z172hSWHMFgED8EG9DM6hIXU_6NaO1DImCn0vNeuoBq847Sl6qw_GHSYHJUQ 219 | # XtXJq7S_CxWVrI82wjrOyaQca5tLZRZc45BfKHeqByThKI261QevEK56SyAwwXfK 220 | # KZjSvkQ5dwTFSgfy76rMSUvVynHYEhdCatBF9HWTAiXPx7hgZixG1FeP_QCmOylz 221 | # 2VClVyYFCbjKREOwBFf-puNYfO75S3LNlJUtTsGGQL2oTKpMsEiUTdefkje91VX9 222 | # h8g7908lFsggbjV7NicJsufuXxnTj1fcWIrRDeNIOmakiPEODi0gTSz0ou-W-LWK 223 | # -3T1zYlOIiIKBjsExQKZ-w 224 | # 225 | --------------------------------------------------------------------------------