11using System . CommandLine ;
22using System . Security . Cryptography . X509Certificates ;
3+ using System . Text ;
4+ using System . Text . Json ;
5+ using System . Text . Json . Serialization ;
36
47using Microsoft . Extensions . Logging ;
58using Microsoft . Extensions . Logging . Abstractions ;
@@ -11,8 +14,9 @@ namespace SAPTeam.EasySign.CommandLine
1114 /// <summary>
1215 /// Provides command definitions and handlers for the EasySign command line interface.
1316 /// </summary>
14- /// <typeparam name="T">The type of the bundle.</typeparam>
15- public abstract partial class CommandProvider < T >
17+ /// <typeparam name="TBundle">The type of the bundle.</typeparam>
18+ public abstract partial class CommandProvider < TBundle > : IDisposable
19+ where TBundle : Bundle
1620 {
1721 /// <summary>
1822 /// Gets or sets the logger to use for logging.
@@ -24,6 +28,11 @@ public abstract partial class CommandProvider<T>
2428 /// </summary>
2529 public string AppDirectory { get ; set ; }
2630
31+ /// <summary>
32+ /// Gets the application configurations.
33+ /// </summary>
34+ public Configuration Configuration { get ; }
35+
2736 /// <summary>
2837 /// Initializes a new instance of the <see cref="CommandProvider{T}"/> class with the specified application directory and logger.
2938 /// </summary>
@@ -37,6 +46,17 @@ public abstract partial class CommandProvider<T>
3746 protected CommandProvider ( string appDirectory , ILogger ? logger )
3847 {
3948 AppDirectory = appDirectory ?? throw new ArgumentNullException ( nameof ( appDirectory ) ) ;
49+
50+ if ( File . Exists ( Path . Combine ( AppDirectory , "config.json" ) ) )
51+ {
52+ using var fs = File . OpenRead ( Path . Combine ( AppDirectory , "config.json" ) ) ;
53+ Configuration = JsonSerializer . Deserialize ( fs , typeof ( Configuration ) , SourceGenerationConfigurationContext . Default ) as Configuration ?? new ( ) ;
54+ }
55+ else
56+ {
57+ Configuration = new ( ) ;
58+ }
59+
4060 Logger = logger ?? NullLogger . Instance ;
4161 }
4262
@@ -66,11 +86,11 @@ public Command Add
6686 continueOpt . AddAlias ( "-c" ) ;
6787
6888 Command command = new Command ( "add" , "Create new bundle or update an existing one" )
69- {
70- BundlePath ,
71- replaceOpt ,
72- continueOpt ,
73- } ;
89+ {
90+ BundlePath ,
91+ replaceOpt ,
92+ continueOpt ,
93+ } ;
7494
7595 command . SetHandler ( ( bundlePath , replace , continueOnError ) =>
7696 {
@@ -121,10 +141,46 @@ public Command Sign
121141 return ;
122142 }
123143
124- var subject = CertificateUtilities . GetSubjectNameFromUser ( ) ;
125- var issuedCert = CertificateUtilities . IssueCertificate ( subject , rootCA ) ;
144+ string ? selectedCert = null ;
145+ if ( Configuration . IssuedCertificates . Count > 0 )
146+ {
147+ selectedCert = AnsiConsole . Prompt < string > (
148+ new SelectionPrompt < string > ( )
149+ . PageSize ( 10 )
150+ . Title ( "Select Self-Signing Certificate" )
151+ . MoreChoicesText ( "[grey](Move up and down to see more certificates)[/]" )
152+ . AddChoices ( Configuration . IssuedCertificates . Keys )
153+ . AddChoices ( "Issue New Certificate" ) ) ;
154+ }
155+
156+ if ( string . IsNullOrEmpty ( selectedCert ) || selectedCert == "Issue New Certificate" )
157+ {
158+ var subject = CertificateUtilities . GetSubjectNameFromUser ( ) ;
159+ var issuedCert = CertificateUtilities . IssueCertificate ( subject , rootCA ) ;
160+
161+ var certFileName = $ "{ issuedCert . GetNameInfo ( X509NameType . SimpleName , false ) . Replace ( " " , "_" ) } -{ issuedCert . GetSerialNumberString ( ) [ ^ 6 ] } .pfx";
162+ var certFilePath = Path . Combine ( AppDirectory , "certs" , certFileName ) ;
126163
127- certs = new X509Certificate2Collection ( issuedCert ) ;
164+ Directory . CreateDirectory ( Path . Combine ( AppDirectory , "certs" ) ) ;
165+
166+ using ( var fs = File . Create ( certFilePath ) )
167+ {
168+ fs . Write ( issuedCert . Export ( X509ContentType . Pfx ) ) ;
169+ }
170+
171+ Configuration . IssuedCertificates . Add ( subject , certFileName ) ;
172+ certs = new X509Certificate2Collection ( issuedCert ) ;
173+ }
174+ else
175+ {
176+ certs = [ ] ;
177+ var certFilePath = Path . Combine ( AppDirectory , "certs" , Configuration . IssuedCertificates [ selectedCert ] ) ;
178+ #if NET9_0_OR_GREATER
179+ certs . AddRange ( X509CertificateLoader . LoadPkcs12CollectionFromFile ( certFilePath , null , X509KeyStorageFlags . EphemeralKeySet | X509KeyStorageFlags . Exportable ) ) ;
180+ #else
181+ certs . Import ( certFilePath , null , X509KeyStorageFlags . EphemeralKeySet | X509KeyStorageFlags . Exportable ) ;
182+ #endif
183+ }
128184 }
129185 else
130186 {
@@ -286,5 +342,29 @@ public Command SelfSign
286342
287343 return null ;
288344 }
345+
346+ /// <summary>
347+ /// Writes the configuration to disk.
348+ /// </summary>
349+ public void WriteConfig ( )
350+ {
351+ string json = JsonSerializer . Serialize ( Configuration , typeof ( Configuration ) , SourceGenerationConfigurationContext . Default ) ;
352+ var buffer = Encoding . UTF8 . GetBytes ( json ) ;
353+
354+ using ( FileStream fs = File . OpenWrite ( Path . Combine ( AppDirectory , "config.json" ) ) )
355+ {
356+ fs . Write ( buffer ) ;
357+ }
358+ }
359+
360+ /// <inheritdoc/>
361+ public void Dispose ( )
362+ {
363+ WriteConfig ( ) ;
364+
365+ Bundle = null ;
366+
367+ GC . SuppressFinalize ( this ) ;
368+ }
289369 }
290370}
0 commit comments