33
33
34
34
using System ;
35
35
using System . IO ;
36
- using System . Security ;
36
+ using System . Runtime . InteropServices ;
37
37
using System . Security . Cryptography ;
38
38
using System . Security . Cryptography . X509Certificates ;
39
+ using System . Text ;
39
40
40
41
namespace DFe . Utils . Assinatura
41
42
{
42
43
public static class CertificadoDigital
43
44
{
45
+ private static X509Certificate2 _certificado ;
46
+
47
+ #region Métodos privados
44
48
45
49
/// <summary>
46
- /// Exibe a lista de certificados instalados no PC e devolve o certificado selecionado
50
+ /// Cria e devolve um objeto <see cref="X509Store"/>
47
51
/// </summary>
52
+ /// <param name="openFlags"></param>
48
53
/// <returns></returns>
49
- public static X509Certificate2 ObterDoRepositorio ( )
54
+ private static X509Store ObterX509Store ( OpenFlags openFlags )
50
55
{
51
56
var store = new X509Store ( StoreName . My , StoreLocation . CurrentUser ) ;
52
- store . Open ( OpenFlags . OpenExistingOnly | OpenFlags . ReadOnly ) ;
57
+ store . Open ( openFlags ) ;
58
+ return store ;
59
+ }
53
60
54
- var collection = store . Certificates ;
55
- var fcollection = collection . Find ( X509FindType . FindByTimeValid , DateTime . Now , true ) ;
56
- var scollection = X509Certificate2UI . SelectFromCollection ( fcollection , "Certificados válidos:" , "Selecione o certificado que deseja usar" ,
57
- X509SelectionFlag . SingleSelection ) ;
61
+ #region Métodos para obter um certificado X509Certificate2
58
62
59
- if ( scollection . Count == 0 )
63
+ /// <summary>
64
+ /// Obtém um certificado a partir do arquivo e da senha passados nos parâmetros
65
+ /// </summary>
66
+ /// <param name="arquivo">Arquivo do certificado digital</param>
67
+ /// <param name="senha">Senha do certificado digital</param>
68
+ /// <returns></returns>
69
+ private static X509Certificate2 ObterDeArquivo ( string arquivo , string senha )
70
+ {
71
+ if ( ! File . Exists ( arquivo ) )
60
72
{
61
- throw new Exception ( "Nenhum certificado foi selecionado!" ) ;
73
+ throw new Exception ( String . Format ( "Certificado digital {0} não encontrado!" , arquivo ) ) ;
62
74
}
63
75
64
- store . Close ( ) ;
65
- return scollection [ 0 ] ;
76
+ var certificado = new X509Certificate2 ( arquivo , senha , X509KeyStorageFlags . MachineKeySet ) ;
77
+ return certificado ;
66
78
}
67
79
68
80
/// <summary>
69
- /// Obtém um certificado instalado no PC a partir do número de série passado no parâmetro
81
+ /// Obtém um objeto <see cref="X509Certificate2"/> pelo serial passado no parÂmetro
70
82
/// </summary>
71
- /// <param name="numeroSerial">Serial do certificado</param>
72
- /// <param name="senha">Informe a senha se desejar que o usuário não precise digitá-la toda vez que for iniciada uma nova instância da aplicação. Não informe a senha para certificado A1!</param>
73
83
/// <returns></returns>
74
- public static X509Certificate2 ObterDoRepositorio ( ConfiguracaoCertificado configuracaoCertificado )
84
+ private static X509Certificate2 ObterDoRepositorio ( string serial , OpenFlags opcoesDeAbertura )
75
85
{
76
- if ( string . IsNullOrEmpty ( configuracaoCertificado . Serial ) )
77
- throw new Exception ( "O nº de série do certificado não foi informado para a função ObterDoRepositorio!" ) ;
86
+ X509Certificate2 certificado = null ;
87
+ var store = ObterX509Store ( opcoesDeAbertura ) ;
88
+ try
89
+ {
90
+ foreach ( var item in store . Certificates )
91
+ {
92
+ if ( item . SerialNumber != null && item . SerialNumber . ToUpper ( ) . Equals ( serial . ToUpper ( ) , StringComparison . InvariantCultureIgnoreCase ) )
93
+ certificado = item ;
94
+ }
95
+
96
+ if ( certificado == null )
97
+ throw new Exception ( string . Format ( "Certificado digital nº {0} não encontrado!" , serial . ToUpper ( ) ) ) ;
98
+ }
99
+ finally
100
+ {
101
+ store . Close ( ) ;
102
+ }
78
103
79
- return configuracaoCertificado . CriaCertificado ( ) ;
104
+ return certificado ;
80
105
}
81
106
82
107
/// <summary>
83
- /// Obtém um certificado a partir do arquivo e da senha passados nos parâmetros
108
+ /// Obtém um objeto <see cref="X509Certificate2"/> pelo serial passado no parâmetro e com opção de definir o PIN
84
109
/// </summary>
85
- /// <param name="arquivo">Arquivo do certificado digital </param>
86
- /// <param name="senha">Senha do certificado digital </param>
110
+ /// <param name="serial"> </param>
111
+ /// <param name="senha"></param>
87
112
/// <returns></returns>
88
- public static X509Certificate2 ObterDeArquivo ( string arquivo , string senha )
113
+ private static X509Certificate2 ObterDoRepositorioPassandoPin ( string serial , string senha = null )
89
114
{
90
- if ( ! File . Exists ( arquivo ) )
115
+ var certificado = ObterDoRepositorio ( serial , OpenFlags . ReadOnly ) ;
116
+ if ( string . IsNullOrEmpty ( senha ) ) return certificado ;
117
+ certificado . DefinirPinParaChavePrivada ( senha ) ;
118
+ return certificado ;
119
+ }
120
+
121
+ #endregion
122
+
123
+ /// <summary>
124
+ /// Define o PIN para chave privada de um objeto <see cref="X509Certificate2"/> passado no parâmetro
125
+ /// </summary>
126
+ private static void DefinirPinParaChavePrivada ( this X509Certificate2 certificado , string pin )
127
+ {
128
+ if ( certificado == null ) throw new ArgumentNullException ( "certificado" ) ;
129
+ var key = ( RSACryptoServiceProvider ) certificado . PrivateKey ;
130
+
131
+ var providerHandle = IntPtr . Zero ;
132
+ var pinBuffer = Encoding . ASCII . GetBytes ( pin ) ;
133
+
134
+ MetodosNativos . Executar ( ( ) => MetodosNativos . CryptAcquireContext ( ref providerHandle ,
135
+ key . CspKeyContainerInfo . KeyContainerName ,
136
+ key . CspKeyContainerInfo . ProviderName ,
137
+ key . CspKeyContainerInfo . ProviderType ,
138
+ MetodosNativos . CryptContextFlags . Silent ) ) ;
139
+ MetodosNativos . Executar ( ( ) => MetodosNativos . CryptSetProvParam ( providerHandle ,
140
+ MetodosNativos . CryptParameter . KeyExchangePin ,
141
+ pinBuffer , 0 ) ) ;
142
+ MetodosNativos . Executar ( ( ) => MetodosNativos . CertSetCertificateContextProperty (
143
+ certificado . Handle ,
144
+ MetodosNativos . CertificateProperty . CryptoProviderHandle ,
145
+ 0 , providerHandle ) ) ;
146
+ }
147
+
148
+ /// <summary>
149
+ /// Se a propriedade <see cref="ConfiguracaoCertificado.Arquivo"/> for informada, Obtém o certificado do arquivo, senão obtém o certificado do repositório
150
+ /// </summary>
151
+ /// <returns></returns>
152
+ private static X509Certificate2 ObterDadosCertificado ( ConfiguracaoCertificado configuracaoCertificado )
153
+ {
154
+ switch ( configuracaoCertificado . TipoCertificado )
91
155
{
92
- throw new Exception ( string . Format ( "Certificado digital {0} não encontrado!" , arquivo ) ) ;
156
+ case TipoCertificado . A1Repositorio :
157
+ return ObterDoRepositorio ( configuracaoCertificado . Serial , OpenFlags . MaxAllowed ) ;
158
+ case TipoCertificado . A1Arquivo :
159
+ return ObterDeArquivo ( configuracaoCertificado . Arquivo , configuracaoCertificado . Senha ) ;
160
+ case TipoCertificado . A3 :
161
+ return ObterDoRepositorioPassandoPin ( configuracaoCertificado . Serial , configuracaoCertificado . Senha ) ;
162
+ default :
163
+ throw new ArgumentOutOfRangeException ( ) ;
93
164
}
94
-
95
- var certificado = new X509Certificate2 ( arquivo , senha , X509KeyStorageFlags . MachineKeySet ) ;
96
- return certificado ;
97
165
}
98
166
99
- private static X509Certificate2 _certificado ;
167
+ #endregion
100
168
169
+ /// <summary>
170
+ /// Exibe a lista de certificados instalados no PC e devolve o certificado selecionado
171
+ /// </summary>
172
+ /// <returns></returns>
173
+ public static X509Certificate2 ListareObterDoRepositorio ( )
174
+ {
175
+ var store = ObterX509Store ( OpenFlags . OpenExistingOnly | OpenFlags . ReadOnly ) ;
176
+ var collection = store . Certificates ;
177
+ var fcollection = collection . Find ( X509FindType . FindByTimeValid , DateTime . Now , true ) ;
178
+ var scollection = X509Certificate2UI . SelectFromCollection ( fcollection , "Certificados válidos:" , "Selecione o certificado que deseja usar" ,
179
+ X509SelectionFlag . SingleSelection ) ;
180
+
181
+ if ( scollection . Count == 0 )
182
+ {
183
+ throw new Exception ( "Nenhum certificado foi selecionado!" ) ;
184
+ }
185
+
186
+ store . Close ( ) ;
187
+ return scollection [ 0 ] ;
188
+ }
189
+
101
190
/// <summary>
102
191
/// Obtém um objeto contendo o certificado digital
103
192
/// <para>Se for informado <see cref="ConfiguracaoCertificado.Arquivo"/>,
104
193
/// o certificado digital será obtido pelo método <see cref="ObterDeArquivo(string,string)"/>,
105
- /// senão será obtido pelo método <see cref="ObterDoRepositorio() "/> </para>
194
+ /// senão será obtido pelo método <see cref="ListareObterDoRepositorio "/> </para>
106
195
/// <para>Para liberar os recursos do certificado, após seu uso, invoque o método <see cref="X509Certificate2.Reset()"/></para>
107
196
/// </summary>
108
197
public static X509Certificate2 ObterCertificado ( ConfiguracaoCertificado configuracaoCertificado )
@@ -114,13 +203,58 @@ public static X509Certificate2 ObterCertificado(ConfiguracaoCertificado configur
114
203
_certificado = ObterDadosCertificado ( configuracaoCertificado ) ;
115
204
return _certificado ;
116
205
}
206
+ }
117
207
118
- private static X509Certificate2 ObterDadosCertificado ( ConfiguracaoCertificado configuracaoCertificado )
208
+ internal static class MetodosNativos
209
+ {
210
+ internal enum CryptContextFlags
119
211
{
120
- return string . IsNullOrEmpty ( configuracaoCertificado . Arquivo )
121
- ? ObterDoRepositorio ( configuracaoCertificado )
122
- : ObterDeArquivo ( configuracaoCertificado . Arquivo ,
123
- configuracaoCertificado . Senha ) ;
212
+ None = 0 ,
213
+ Silent = 0x40
214
+ }
215
+
216
+ internal enum CertificateProperty
217
+ {
218
+ None = 0 ,
219
+ CryptoProviderHandle = 0x1
220
+ }
221
+
222
+ internal enum CryptParameter
223
+ {
224
+ None = 0 ,
225
+ KeyExchangePin = 0x20
226
+ }
227
+
228
+ [ DllImport ( "advapi32.dll" , CharSet = CharSet . Auto , SetLastError = true ) ]
229
+ public static extern bool CryptAcquireContext (
230
+ ref IntPtr hProv ,
231
+ string containerName ,
232
+ string providerName ,
233
+ int providerType ,
234
+ CryptContextFlags flags
235
+ ) ;
236
+
237
+ [ DllImport ( "advapi32.dll" , SetLastError = true , CharSet = CharSet . Auto ) ]
238
+ public static extern bool CryptSetProvParam (
239
+ IntPtr hProv ,
240
+ CryptParameter dwParam ,
241
+ [ In ] byte [ ] pbData ,
242
+ uint dwFlags ) ;
243
+
244
+ [ DllImport ( "CRYPT32.DLL" , SetLastError = true ) ]
245
+ internal static extern bool CertSetCertificateContextProperty (
246
+ IntPtr pCertContext ,
247
+ CertificateProperty propertyId ,
248
+ uint dwFlags ,
249
+ IntPtr pvData
250
+ ) ;
251
+
252
+ public static void Executar ( Func < bool > action )
253
+ {
254
+ if ( ! action ( ) )
255
+ {
256
+ throw new System . ComponentModel . Win32Exception ( Marshal . GetLastWin32Error ( ) ) ;
257
+ }
124
258
}
125
259
}
126
260
}
0 commit comments