Skip to content

Commit 6482e93

Browse files
committed
- Adicionada propriedade TipoCertificado na classe ConfiguracaoCertificado; - Centralizado todo o código referente a certificado digital na classe CertificadoDigital (essa classe é quem decidirá que tipo de abordagem vai utilizar, com base no TipoDeCertificado informado; - Movidas as classes numExt e Reflexao do projeto NFe.Utils para DFe.Utils; - Primeira versão do DANFE da NFCe nativo com base no código compartilhado por Fabio no P/R https://github.com/adeniltonbs/Zeus.Net.NFe.NFCe/pull/367.
1 parent 6e12a70 commit 6482e93

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1478
-463
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,5 @@ Temporary Items
9696
/MDFe.Damdfe.Base/bin
9797
/MDFe.Damdfe.Fast/obj
9898
/MDFe.Damdfe.Fast/bin
99+
/NFe.Danfe.Nativo/bin/Debug
100+
/NFe.Danfe.Nativo/obj/Debug

DFe.Utils/Assinatura/CertificadoDigital.cs

+169-35
Original file line numberDiff line numberDiff line change
@@ -33,76 +33,165 @@
3333

3434
using System;
3535
using System.IO;
36-
using System.Security;
36+
using System.Runtime.InteropServices;
3737
using System.Security.Cryptography;
3838
using System.Security.Cryptography.X509Certificates;
39+
using System.Text;
3940

4041
namespace DFe.Utils.Assinatura
4142
{
4243
public static class CertificadoDigital
4344
{
45+
private static X509Certificate2 _certificado;
46+
47+
#region Métodos privados
4448

4549
/// <summary>
46-
/// Exibe a lista de certificados instalados no PC e devolve o certificado selecionado
50+
/// Cria e devolve um objeto <see cref="X509Store"/>
4751
/// </summary>
52+
/// <param name="openFlags"></param>
4853
/// <returns></returns>
49-
public static X509Certificate2 ObterDoRepositorio()
54+
private static X509Store ObterX509Store(OpenFlags openFlags)
5055
{
5156
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
52-
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
57+
store.Open(openFlags);
58+
return store;
59+
}
5360

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
5862

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))
6072
{
61-
throw new Exception("Nenhum certificado foi selecionado!");
73+
throw new Exception(String.Format("Certificado digital {0} não encontrado!", arquivo));
6274
}
6375

64-
store.Close();
65-
return scollection[0];
76+
var certificado = new X509Certificate2(arquivo, senha, X509KeyStorageFlags.MachineKeySet);
77+
return certificado;
6678
}
6779

6880
/// <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
7082
/// </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>
7383
/// <returns></returns>
74-
public static X509Certificate2 ObterDoRepositorio(ConfiguracaoCertificado configuracaoCertificado)
84+
private static X509Certificate2 ObterDoRepositorio(string serial, OpenFlags opcoesDeAbertura)
7585
{
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+
}
78103

79-
return configuracaoCertificado.CriaCertificado();
104+
return certificado;
80105
}
81106

82107
/// <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
84109
/// </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>
87112
/// <returns></returns>
88-
public static X509Certificate2 ObterDeArquivo(string arquivo, string senha)
113+
private static X509Certificate2 ObterDoRepositorioPassandoPin(string serial, string senha = null)
89114
{
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)
91155
{
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();
93164
}
94-
95-
var certificado = new X509Certificate2(arquivo, senha, X509KeyStorageFlags.MachineKeySet);
96-
return certificado;
97165
}
98166

99-
private static X509Certificate2 _certificado;
167+
#endregion
100168

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+
101190
/// <summary>
102191
/// Obtém um objeto contendo o certificado digital
103192
/// <para>Se for informado <see cref="ConfiguracaoCertificado.Arquivo"/>,
104193
/// 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>
106195
/// <para>Para liberar os recursos do certificado, após seu uso, invoque o método <see cref="X509Certificate2.Reset()"/></para>
107196
/// </summary>
108197
public static X509Certificate2 ObterCertificado(ConfiguracaoCertificado configuracaoCertificado)
@@ -114,13 +203,58 @@ public static X509Certificate2 ObterCertificado(ConfiguracaoCertificado configur
114203
_certificado = ObterDadosCertificado(configuracaoCertificado);
115204
return _certificado;
116205
}
206+
}
117207

118-
private static X509Certificate2 ObterDadosCertificado(ConfiguracaoCertificado configuracaoCertificado)
208+
internal static class MetodosNativos
209+
{
210+
internal enum CryptContextFlags
119211
{
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+
}
124258
}
125259
}
126260
}

DFe.Utils/Assinatura/CertificadoProvider/CertificadoAutoPinProvider.cs

-52
This file was deleted.

DFe.Utils/Assinatura/CertificadoProvider/CertificadoProvider.cs

-70
This file was deleted.

0 commit comments

Comments
 (0)