22using System . IO ;
33using System . Security . Cryptography ;
44using System . Text ;
5+ using System . Text . Json ;
56using System . Threading . Tasks ;
67using Microsoft . Extensions . Logging ;
78using Microsoft . Extensions . Logging . Abstractions ;
@@ -21,7 +22,7 @@ public GenerateJwksCommand()
2122 Logger = NullLogger < GenerateJwksCommand > . Instance ;
2223 }
2324
24- public async Task ExecuteAsync ( CommandLineArgs commandLineArgs )
25+ public Task ExecuteAsync ( CommandLineArgs commandLineArgs )
2526 {
2627 var outputDir = commandLineArgs . Options . GetOrNull ( "output" , "o" )
2728 ?? Directory . GetCurrentDirectory ( ) ;
@@ -33,13 +34,13 @@ public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
3334 if ( ! int . TryParse ( keySizeStr , out var keySize ) || ( keySize != 2048 && keySize != 4096 ) )
3435 {
3536 Logger . LogError ( "Invalid key size '{0}'. Supported values: 2048, 4096." , keySizeStr ) ;
36- return ;
37+ return Task . CompletedTask ;
3738 }
3839
3940 if ( ! IsValidAlgorithm ( alg ) )
4041 {
4142 Logger . LogError ( "Invalid algorithm '{0}'. Supported values: RS256, RS384, RS512, PS256, PS384, PS512." , alg ) ;
42- return ;
43+ return Task . CompletedTask ;
4344 }
4445
4546 if ( ! Directory . Exists ( outputDir ) )
@@ -49,16 +50,17 @@ public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
4950
5051 Logger . LogInformation ( "Generating RSA {0}-bit key pair (algorithm: {1})..." , keySize , alg ) ;
5152
52- using var rsa = RSA . Create ( keySize ) ;
53+ using var rsa = RSA . Create ( ) ;
54+ rsa . KeySize = keySize ;
5355
5456 var jwksJson = BuildJwksJson ( rsa , alg , kid ) ;
5557 var privateKeyPem = ExportPrivateKeyPem ( rsa ) ;
5658
5759 var jwksFilePath = Path . Combine ( outputDir , $ "{ filePrefix } .json") ;
5860 var privateKeyFilePath = Path . Combine ( outputDir , $ "{ filePrefix } -private.pem") ;
5961
60- await File . WriteAllTextAsync ( jwksFilePath , jwksJson , Encoding . UTF8 ) ;
61- await File . WriteAllTextAsync ( privateKeyFilePath , privateKeyPem , Encoding . UTF8 ) ;
62+ File . WriteAllText ( jwksFilePath , jwksJson , Encoding . UTF8 ) ;
63+ File . WriteAllText ( privateKeyFilePath , privateKeyPem , Encoding . UTF8 ) ;
6264
6365 Logger . LogInformation ( "" ) ;
6466 Logger . LogInformation ( "Generated files:" ) ;
@@ -72,7 +74,7 @@ public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
7274 Logger . LogInformation ( "IMPORTANT: Keep the private key file safe. Never share it or commit it to source control." ) ;
7375 Logger . LogInformation ( " The JWKS file contains only the public key and is safe to share." ) ;
7476
75- await Task . CompletedTask ;
77+ return Task . CompletedTask ;
7678 }
7779
7880 private static string BuildJwksJson ( RSA rsa , string alg , string kid )
@@ -82,27 +84,34 @@ private static string BuildJwksJson(RSA rsa, string alg, string kid)
8284 var n = Base64UrlEncode ( parameters . Modulus ) ;
8385 var e = Base64UrlEncode ( parameters . Exponent ) ;
8486
85- var sb = new StringBuilder ( ) ;
86- sb . AppendLine ( "{" ) ;
87- sb . AppendLine ( " \" keys\" : [" ) ;
88- sb . AppendLine ( " {" ) ;
89- sb . AppendLine ( " \" kty\" : \" RSA\" ," ) ;
90- sb . AppendLine ( " \" use\" : \" sig\" ," ) ;
91- sb . AppendLine ( $ " \" kid\" : \" { kid } \" ,") ;
92- sb . AppendLine ( $ " \" alg\" : \" { alg } \" ,") ;
93- sb . AppendLine ( $ " \" n\" : \" { n } \" ,") ;
94- sb . AppendLine ( $ " \" e\" : \" { e } \" ") ;
95- sb . AppendLine ( " }" ) ;
96- sb . AppendLine ( " ]" ) ;
97- sb . Append ( "}" ) ;
98-
99- return sb . ToString ( ) ;
87+ using var stream = new System . IO . MemoryStream ( ) ;
88+ using var writer = new Utf8JsonWriter ( stream , new JsonWriterOptions { Indented = true } ) ;
89+
90+ writer . WriteStartObject ( ) ;
91+ writer . WriteStartArray ( "keys" ) ;
92+ writer . WriteStartObject ( ) ;
93+ writer . WriteString ( "kty" , "RSA" ) ;
94+ writer . WriteString ( "use" , "sig" ) ;
95+ writer . WriteString ( "kid" , kid ) ;
96+ writer . WriteString ( "alg" , alg ) ;
97+ writer . WriteString ( "n" , n ) ;
98+ writer . WriteString ( "e" , e ) ;
99+ writer . WriteEndObject ( ) ;
100+ writer . WriteEndArray ( ) ;
101+ writer . WriteEndObject ( ) ;
102+ writer . Flush ( ) ;
103+
104+ return Encoding . UTF8 . GetString ( stream . ToArray ( ) ) ;
100105 }
101106
102107 private static string ExportPrivateKeyPem ( RSA rsa )
103108 {
104109#if NET5_0_OR_GREATER
105110 return rsa . ExportPkcs8PrivateKeyPem ( ) ;
111+ #elif NETSTANDARD2_0
112+ // RSA.ExportPkcs8PrivateKey() was introduced in .NET Standard 2.1.
113+ // The ABP CLI always runs on .NET 5+, so this path is never reached at runtime.
114+ throw new PlatformNotSupportedException ( "Private key export requires .NET Standard 2.1 or later." ) ;
106115#else
107116 var privateKeyBytes = rsa . ExportPkcs8PrivateKey ( ) ;
108117 var base64 = Convert . ToBase64String ( privateKeyBytes , Base64FormattingOptions . InsertLineBreaks ) ;
@@ -120,7 +129,8 @@ private static string Base64UrlEncode(byte[] input)
120129
121130 private static bool IsValidAlgorithm ( string alg )
122131 {
123- return alg is "RS256" or "RS384" or "RS512" or "PS256" or "PS384" or "PS512" ;
132+ return alg == "RS256" || alg == "RS384" || alg == "RS512" ||
133+ alg == "PS256" || alg == "PS384" || alg == "PS512" ;
124134 }
125135
126136 public string GetUsageInfo ( )
0 commit comments