├── .gitignore ├── JwtRsaHmacSample.sln ├── LICENSE ├── README.md ├── content ├── private.pem └── public.pem └── src └── JwtRsaHmacSample.Api ├── Controllers └── AccountController.cs ├── Framework └── Extensions.cs ├── JwtRsaHmacSample.Api.csproj ├── Models ├── JWT.cs ├── JwtSettings.cs └── SignIn.cs ├── Program.cs ├── Services ├── IJwtHandler.cs └── JwtHandler.cs ├── Startup.cs ├── appsettings.json ├── rsa-private-key.xml └── rsa-public-key.xml /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /JwtRsaHmacSample.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D37A2883-D2CB-47AF-A4A0-E1E62FD169F0}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JwtRsaHmacSample.Api", "src\JwtRsaHmacSample.Api\JwtRsaHmacSample.Api.csproj", "{9338171C-52D3-46C3-BA33-C8FBB9FF2C78}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {9338171C-52D3-46C3-BA33-C8FBB9FF2C78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {9338171C-52D3-46C3-BA33-C8FBB9FF2C78}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {9338171C-52D3-46C3-BA33-C8FBB9FF2C78}.Debug|x64.ActiveCfg = Debug|x64 26 | {9338171C-52D3-46C3-BA33-C8FBB9FF2C78}.Debug|x64.Build.0 = Debug|x64 27 | {9338171C-52D3-46C3-BA33-C8FBB9FF2C78}.Debug|x86.ActiveCfg = Debug|x86 28 | {9338171C-52D3-46C3-BA33-C8FBB9FF2C78}.Debug|x86.Build.0 = Debug|x86 29 | {9338171C-52D3-46C3-BA33-C8FBB9FF2C78}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {9338171C-52D3-46C3-BA33-C8FBB9FF2C78}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {9338171C-52D3-46C3-BA33-C8FBB9FF2C78}.Release|x64.ActiveCfg = Release|x64 32 | {9338171C-52D3-46C3-BA33-C8FBB9FF2C78}.Release|x64.Build.0 = Release|x64 33 | {9338171C-52D3-46C3-BA33-C8FBB9FF2C78}.Release|x86.ActiveCfg = Release|x86 34 | {9338171C-52D3-46C3-BA33-C8FBB9FF2C78}.Release|x86.Build.0 = Release|x86 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {9338171C-52D3-46C3-BA33-C8FBB9FF2C78} = {D37A2883-D2CB-47AF-A4A0-E1E62FD169F0} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Piotr Gankiewicz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ASP.NET Core Web API sample using JWT RSA & HMAC for authentication 2 | 3 | ## Read more [here](http://piotrgankiewicz.com/2017/07/24/jwt-rsa-hmac-asp-net-core/) -------------------------------------------------------------------------------- /content/private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAzfU9wLR5CY3tSz258TcXmufSqHVzcB+q81FQZewAz7cB0ZHG 3 | O2+jrePkmb5sGhmFgWNGIh2Com1nzyVcTri1kKSXIWiF5R/29SieOacRCaOlNECF 4 | kavF44AYaUfthjtRDWNjKnCXDzvK0QLp16CyUZzzROy6QVIMXsoJpQbWUREz6HU7 5 | jM3lZFxxI7Vvo68vPn0hx0H3JEUXBeUX9cVhBAX57lsNIhSFqypX49LuDM8SWrqS 6 | /sd3lJ8rg7YIPrbarym6ekrN7QG9UVhkJOoh1MqhI4qk30Nx1oqO64xQP6SnaEtk 7 | PAZRK6u4nX2r7TPqYb3LyXDWHFTC+6sd3dxaLwIDAQABAoIBAQCdY4XXV5MPTBhE 8 | YV1RCkrNo86F0Ytv6aNX4ZHQ8XMFSNLo9b9I+F1aq0asfqpZn5s4b0bPF0IXIggs 9 | cl6CAgEuEbk0XI3FtJGic3HGmPcaKKY8sfnggiXtXpxJCCBpbbbYxlSnv/aQO58X 10 | 7mQI1dKvL4Nv7n+/HxY48ahBJmJs+5tW4nh9OqCxnyDasBmj/ZBk52sVtQ+wkUVy 11 | itQZsyWx467U87i8v4wr+aPeDhP+WgAJlSDAPYi+q2/aQdMEctLOHMAhqzCgvSn6 12 | kvM4DoIsC+atzu1fdR/saLBcXqmw3nMD1E6RVm7dHDoCbGkoE3ljXldaKMfCqlCy 13 | 7GPc07VBAoGBAOyVXizJGRF9Bj4g4mU8rq8WVEhzaiNGQi4cF5j5DGOHUq9ONrwY 14 | jmM1+tEu8xGnzIGxGwSbvcUf+MKakTMn7RpmY1h/QgaihQ84Q5e/99fri4e21akv 15 | 4kqBEV/4TFQWSGor1rfd4kiBtNfi6ooM5CqISIwJLD0q3z4AnV0g/fCxAoGBAN7c 16 | bhIhJBkKyBVqa/fjOoFZwODywzNAfKNGCQsIVw4Ap6IiGMavDADOKM9yR9S5ljza 17 | yoM0tGjrPRZesTi2iYMaiZq3qAtzGuGFrYt4vXGTDdRwksfHncKYtV0Xq4sYUUUw 18 | O3Or514+tPPk4FcgZdL5CfkTRQwz6OG1lM7ZjrDfAoGAK59PAgsCaEsZP5NoqyoJ 19 | O5duav188Iwf38imQTqKoj9ta42MYhpVBs4JNVDm2LaL6s3xIWRmFVbT024Un84Y 20 | 1elTIBo23mpRBoFlVTG8TT/NNnTr6Io/u2UZAw0RZd/F8m2q5bQv6Rahdb0Nae7+ 21 | kykV11xJn+2rxA7w9R8EM8ECgYEAjxBkXKEHwkeokA7kRpqJGTZb2kwdQQ55tHqm 22 | HX36HJQRCMTosMr4Yp/1lM4hDI8iwegWLsorslqouW6KSATuG8pyYW7aopb+v52H 23 | /cvBmWI0c5bcswES5jQP4TXrunwe19KRp7zH5zlMAnGADo5Or3ONkmZrYd0E97gQ 24 | UgVZU3MCgYBc1skIFPHHz82GUK9tK2HpiYocZDx/zPDVdn7lcGyVsLx+WSiEMDkC 25 | EkSg3dZA5QHXTLH4GKMdAmDvdle/FNXT7V1g62cObEEWi2TmJOc8TZdj07bTZmv0 26 | qX8HfQ6ylUhSYXAI++kz3kRfYoRsUdeDF+Rpddzjk/Eh1wTSV8GVVQ== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /content/public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzfU9wLR5CY3tSz258TcX 3 | mufSqHVzcB+q81FQZewAz7cB0ZHGO2+jrePkmb5sGhmFgWNGIh2Com1nzyVcTri1 4 | kKSXIWiF5R/29SieOacRCaOlNECFkavF44AYaUfthjtRDWNjKnCXDzvK0QLp16Cy 5 | UZzzROy6QVIMXsoJpQbWUREz6HU7jM3lZFxxI7Vvo68vPn0hx0H3JEUXBeUX9cVh 6 | BAX57lsNIhSFqypX49LuDM8SWrqS/sd3lJ8rg7YIPrbarym6ekrN7QG9UVhkJOoh 7 | 1MqhI4qk30Nx1oqO64xQP6SnaEtkPAZRK6u4nX2r7TPqYb3LyXDWHFTC+6sd3dxa 8 | LwIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /src/JwtRsaHmacSample.Api/Controllers/AccountController.cs: -------------------------------------------------------------------------------- 1 | using JwtRsaHmacSample.Api.Models; 2 | using JwtRsaHmacSample.Api.Services; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.AspNetCore.Mvc; 5 | 6 | namespace JwtRsaHmacSample.Api.Controllers 7 | { 8 | public class AccountController : Controller 9 | { 10 | private readonly IJwtHandler _jwtHandler; 11 | 12 | public AccountController(IJwtHandler jwtHandler) 13 | { 14 | _jwtHandler = jwtHandler; 15 | } 16 | 17 | [HttpGet("me")] 18 | [Authorize] 19 | public IActionResult Get() 20 | { 21 | return Content($"Hello {User.Identity.Name}"); 22 | } 23 | 24 | [HttpPost("sign-in")] 25 | public IActionResult SignIn([FromBody]SignIn request) 26 | { 27 | if(string.IsNullOrWhiteSpace(request.Username) || request.Password != "secret") 28 | { 29 | return Unauthorized(); 30 | } 31 | return Json(_jwtHandler.Create(request.Username)); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/JwtRsaHmacSample.Api/Framework/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | using System.Xml; 4 | 5 | namespace JwtRsaHmacSample.Api.Framework 6 | { 7 | public static class Extensions 8 | { 9 | public static void FromXmlString(this RSA rsa, string xmlString) 10 | { 11 | var parameters = new RSAParameters(); 12 | XmlDocument xmlDoc = new XmlDocument(); 13 | xmlDoc.LoadXml(xmlString); 14 | 15 | if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue")) 16 | { 17 | foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes) 18 | { 19 | switch (node.Name) 20 | { 21 | case "Modulus": parameters.Modulus = Convert.FromBase64String(node.InnerText); break; 22 | case "Exponent": parameters.Exponent = Convert.FromBase64String(node.InnerText); break; 23 | case "P": parameters.P = Convert.FromBase64String(node.InnerText); break; 24 | case "Q": parameters.Q = Convert.FromBase64String(node.InnerText); break; 25 | case "DP": parameters.DP = Convert.FromBase64String(node.InnerText); break; 26 | case "DQ": parameters.DQ = Convert.FromBase64String(node.InnerText); break; 27 | case "InverseQ": parameters.InverseQ = Convert.FromBase64String(node.InnerText); break; 28 | case "D": parameters.D = Convert.FromBase64String(node.InnerText); break; 29 | } 30 | } 31 | } else 32 | { 33 | throw new Exception("Invalid XML RSA key."); 34 | } 35 | 36 | rsa.ImportParameters(parameters); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/JwtRsaHmacSample.Api/JwtRsaHmacSample.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp1.1 5 | 6 | 7 | 8 | 9 | 10 | PreserveNewest 11 | 12 | 13 | PreserveNewest 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/JwtRsaHmacSample.Api/Models/JWT.cs: -------------------------------------------------------------------------------- 1 | namespace JwtRsaHmacSample.Api.Models 2 | { 3 | public class JWT 4 | { 5 | public string Token { get; set; } 6 | public long Expires { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/JwtRsaHmacSample.Api/Models/JwtSettings.cs: -------------------------------------------------------------------------------- 1 | namespace JwtRsaHmacSample.Api.Models 2 | { 3 | public class JwtSettings 4 | { 5 | public string HmacSecretKey { get; set; } 6 | public int ExpiryDays { get; set; } 7 | public string Issuer { get; set; } 8 | public bool UseRsa { get; set; } 9 | public string RsaPrivateKeyXML { get; set; } 10 | public string RsaPublicKeyXML { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/JwtRsaHmacSample.Api/Models/SignIn.cs: -------------------------------------------------------------------------------- 1 | namespace JwtRsaHmacSample.Api.Models 2 | { 3 | public class SignIn 4 | { 5 | public string Username { get; set; } 6 | public string Password { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/JwtRsaHmacSample.Api/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | 9 | namespace JwtRsaHmacSample.Api 10 | { 11 | public class Program 12 | { 13 | public static void Main(string[] args) 14 | { 15 | var host = new WebHostBuilder() 16 | .UseKestrel() 17 | .UseContentRoot(Directory.GetCurrentDirectory()) 18 | .UseIISIntegration() 19 | .UseStartup() 20 | .Build(); 21 | 22 | host.Run(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/JwtRsaHmacSample.Api/Services/IJwtHandler.cs: -------------------------------------------------------------------------------- 1 | using JwtRsaHmacSample.Api.Models; 2 | using Microsoft.IdentityModel.Tokens; 3 | 4 | namespace JwtRsaHmacSample.Api.Services 5 | { 6 | public interface IJwtHandler 7 | { 8 | JWT Create(string userId); 9 | TokenValidationParameters Parameters { get;} 10 | } 11 | } -------------------------------------------------------------------------------- /src/JwtRsaHmacSample.Api/Services/JwtHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IdentityModel.Tokens.Jwt; 3 | using System.IO; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | using JwtRsaHmacSample.Api.Framework; 7 | using JwtRsaHmacSample.Api.Models; 8 | using Microsoft.Extensions.Options; 9 | using Microsoft.IdentityModel.Tokens; 10 | 11 | namespace JwtRsaHmacSample.Api.Services 12 | { 13 | public class JwtHandler : IJwtHandler 14 | { 15 | private readonly JwtSettings _settings; 16 | private readonly JwtSecurityTokenHandler _jwtSecurityTokenHandler = new JwtSecurityTokenHandler(); 17 | private SecurityKey _issuerSigningKey; 18 | private SigningCredentials _signingCredentials; 19 | private JwtHeader _jwtHeader; 20 | public TokenValidationParameters Parameters { get; private set; } 21 | 22 | public JwtHandler(IOptions settings) 23 | { 24 | _settings = settings.Value; 25 | if(_settings.UseRsa) 26 | { 27 | InitializeRsa(); 28 | } 29 | else 30 | { 31 | InitializeHmac(); 32 | } 33 | InitializeJwtParameters(); 34 | } 35 | 36 | private void InitializeRsa() 37 | { 38 | using(RSA publicRsa = RSA.Create()) 39 | { 40 | var publicKeyXml = File.ReadAllText(_settings.RsaPublicKeyXML); 41 | publicRsa.FromXmlString(publicKeyXml); 42 | _issuerSigningKey = new RsaSecurityKey(publicRsa); 43 | } 44 | if(string.IsNullOrWhiteSpace(_settings.RsaPrivateKeyXML)) 45 | { 46 | return; 47 | } 48 | using(RSA privateRsa = RSA.Create()) 49 | { 50 | var privateKeyXml = File.ReadAllText(_settings.RsaPrivateKeyXML); 51 | privateRsa.FromXmlString(privateKeyXml); 52 | var privateKey = new RsaSecurityKey(privateRsa); 53 | _signingCredentials = new SigningCredentials(privateKey, SecurityAlgorithms.RsaSha256); 54 | } 55 | } 56 | 57 | private void InitializeHmac() 58 | { 59 | _issuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_settings.HmacSecretKey)); 60 | _signingCredentials = new SigningCredentials(_issuerSigningKey, SecurityAlgorithms.HmacSha256); 61 | } 62 | 63 | private void InitializeJwtParameters() 64 | { 65 | _jwtHeader = new JwtHeader(_signingCredentials); 66 | Parameters = new TokenValidationParameters 67 | { 68 | ValidateAudience = false, 69 | ValidIssuer = _settings.Issuer, 70 | IssuerSigningKey = _issuerSigningKey 71 | }; 72 | } 73 | 74 | public JWT Create(string userId) 75 | { 76 | var nowUtc = DateTime.UtcNow; 77 | var expires = nowUtc.AddDays(_settings.ExpiryDays); 78 | var centuryBegin = new DateTime(1970, 1, 1); 79 | var exp = (long)(new TimeSpan(expires.Ticks - centuryBegin.Ticks).TotalSeconds); 80 | var now = (long)(new TimeSpan(nowUtc.Ticks - centuryBegin.Ticks).TotalSeconds); 81 | var issuer = _settings.Issuer ?? string.Empty; 82 | var payload = new JwtPayload 83 | { 84 | {"sub", userId}, 85 | {"unique_name", userId}, 86 | {"iss", issuer}, 87 | {"iat", now}, 88 | {"nbf", now}, 89 | {"exp", exp}, 90 | {"jti", Guid.NewGuid().ToString("N")} 91 | }; 92 | var jwt = new JwtSecurityToken(_jwtHeader, payload); 93 | var token = _jwtSecurityTokenHandler.WriteToken(jwt); 94 | 95 | return new JWT 96 | { 97 | Token = token, 98 | Expires = exp 99 | }; 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /src/JwtRsaHmacSample.Api/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using JwtRsaHmacSample.Api.Models; 6 | using JwtRsaHmacSample.Api.Services; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.Configuration; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Extensions.Logging; 12 | 13 | namespace JwtRsaHmacSample.Api 14 | { 15 | public class Startup 16 | { 17 | public Startup(IHostingEnvironment env) 18 | { 19 | var builder = new ConfigurationBuilder() 20 | .SetBasePath(env.ContentRootPath) 21 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 22 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) 23 | .AddEnvironmentVariables(); 24 | Configuration = builder.Build(); 25 | } 26 | 27 | public IConfigurationRoot Configuration { get; } 28 | 29 | // This method gets called by the runtime. Use this method to add services to the container. 30 | public void ConfigureServices(IServiceCollection services) 31 | { 32 | // Add framework services. 33 | services.Configure(Configuration.GetSection("jwt")); 34 | services.AddSingleton(); 35 | services.AddMvc(); 36 | } 37 | 38 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 39 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 40 | { 41 | loggerFactory.AddConsole(Configuration.GetSection("Logging")); 42 | loggerFactory.AddDebug(); 43 | 44 | var jwtHandler = app.ApplicationServices.GetService(); 45 | app.UseJwtBearerAuthentication(new JwtBearerOptions 46 | { 47 | AutomaticAuthenticate = true, 48 | TokenValidationParameters = jwtHandler.Parameters 49 | }); 50 | app.UseMvc(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/JwtRsaHmacSample.Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug" 6 | } 7 | }, 8 | "jwt": { 9 | "issuer": "http://localhost:5000", 10 | "expiryDays": 3, 11 | "useRsa": true, 12 | "hmacSecretKey": "GRQKzLUn9w59LpXEbsESa8gtJnN3hyspq7EV4J6Fz3FjBk994r", 13 | "rsaPrivateKeyXml": "rsa-private-key.xml", 14 | "rsaPublicKeyXml": "rsa-public-key.xml" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/JwtRsaHmacSample.Api/rsa-private-key.xml: -------------------------------------------------------------------------------- 1 | 2 | zfU9wLR5CY3tSz258TcXmufSqHVzcB+q81FQZewAz7cB0ZHGO2+jrePkmb5sGhmFgWNGIh2Com1nzyVcTri1kKSXIWiF5R/29SieOacRCaOlNECFkavF44AYaUfthjtRDWNjKnCXDzvK0QLp16CyUZzzROy6QVIMXsoJpQbWUREz6HU7jM3lZFxxI7Vvo68vPn0hx0H3JEUXBeUX9cVhBAX57lsNIhSFqypX49LuDM8SWrqS/sd3lJ8rg7YIPrbarym6ekrN7QG9UVhkJOoh1MqhI4qk30Nx1oqO64xQP6SnaEtkPAZRK6u4nX2r7TPqYb3LyXDWHFTC+6sd3dxaLw== 3 | AQAB 4 |

7JVeLMkZEX0GPiDiZTyurxZUSHNqI0ZCLhwXmPkMY4dSr042vBiOYzX60S7zEafMgbEbBJu9xR/4wpqRMyftGmZjWH9CBqKFDzhDl7/31+uLh7bVqS/iSoERX/hMVBZIaivWt93iSIG01+LqigzkKohIjAksPSrfPgCdXSD98LE=

5 | 3txuEiEkGQrIFWpr9+M6gVnA4PLDM0B8o0YJCwhXDgCnoiIYxq8MAM4oz3JH1LmWPNrKgzS0aOs9Fl6xOLaJgxqJmreoC3Ma4YWti3i9cZMN1HCSx8edwpi1XRerixhRRTA7c6vnXj608+TgVyBl0vkJ+RNFDDPo4bWUztmOsN8= 6 | K59PAgsCaEsZP5NoqyoJO5duav188Iwf38imQTqKoj9ta42MYhpVBs4JNVDm2LaL6s3xIWRmFVbT024Un84Y1elTIBo23mpRBoFlVTG8TT/NNnTr6Io/u2UZAw0RZd/F8m2q5bQv6Rahdb0Nae7+kykV11xJn+2rxA7w9R8EM8E= 7 | jxBkXKEHwkeokA7kRpqJGTZb2kwdQQ55tHqmHX36HJQRCMTosMr4Yp/1lM4hDI8iwegWLsorslqouW6KSATuG8pyYW7aopb+v52H/cvBmWI0c5bcswES5jQP4TXrunwe19KRp7zH5zlMAnGADo5Or3ONkmZrYd0E97gQUgVZU3M= 8 | XNbJCBTxx8/NhlCvbSth6YmKHGQ8f8zw1XZ+5XBslbC8flkohDA5AhJEoN3WQOUB10yx+BijHQJg73ZXvxTV0+1dYOtnDmxBFotk5iTnPE2XY9O202Zr9Kl/B30OspVIUmFwCPvpM95EX2KEbFHXgxfkaXXc45PxIdcE0lfBlVU= 9 | nWOF11eTD0wYRGFdUQpKzaPOhdGLb+mjV+GR0PFzBUjS6PW/SPhdWqtGrH6qWZ+bOG9GzxdCFyIILHJeggIBLhG5NFyNxbSRonNxxpj3GiimPLH54IIl7V6cSQggaW222MZUp7/2kDufF+5kCNXSry+Db+5/vx8WOPGoQSZibPubVuJ4fTqgsZ8g2rAZo/2QZOdrFbUPsJFFcorUGbMlseOu1PO4vL+MK/mj3g4T/loACZUgwD2Ivqtv2kHTBHLSzhzAIaswoL0p+pLzOA6CLAvmrc7tX3Uf7GiwXF6psN5zA9ROkVZu3Rw6AmxpKBN5Y15XWijHwqpQsuxj3NO1QQ== 10 |
-------------------------------------------------------------------------------- /src/JwtRsaHmacSample.Api/rsa-public-key.xml: -------------------------------------------------------------------------------- 1 | 2 | zfU9wLR5CY3tSz258TcXmufSqHVzcB+q81FQZewAz7cB0ZHGO2+jrePkmb5sGhmFgWNGIh2Com1nzyVcTri1kKSXIWiF5R/29SieOacRCaOlNECFkavF44AYaUfthjtRDWNjKnCXDzvK0QLp16CyUZzzROy6QVIMXsoJpQbWUREz6HU7jM3lZFxxI7Vvo68vPn0hx0H3JEUXBeUX9cVhBAX57lsNIhSFqypX49LuDM8SWrqS/sd3lJ8rg7YIPrbarym6ekrN7QG9UVhkJOoh1MqhI4qk30Nx1oqO64xQP6SnaEtkPAZRK6u4nX2r7TPqYb3LyXDWHFTC+6sd3dxaLw== 3 | AQAB 4 | --------------------------------------------------------------------------------