Skip to content

Commit

Permalink
Merge pull request #5 from adeniltonbs/master
Browse files Browse the repository at this point in the history
Update
  • Loading branch information
adrbarros authored Feb 2, 2017
2 parents 0229429 + 8d4a76e commit cc39d41
Show file tree
Hide file tree
Showing 109 changed files with 1,924 additions and 650 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,5 @@ Temporary Items
/MDFe.Damdfe.Base/bin
/MDFe.Damdfe.Fast/obj
/MDFe.Damdfe.Fast/bin
/NFe.Danfe.Nativo/bin/Debug
/NFe.Danfe.Nativo/obj/Debug
4 changes: 2 additions & 2 deletions DFe.Classes/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.0.0.6")]
[assembly: AssemblyFileVersion("0.0.0.6")]
[assembly: AssemblyVersion("0.0.0.7")]
[assembly: AssemblyFileVersion("0.0.0.7")]
212 changes: 152 additions & 60 deletions DFe.Utils/Assinatura/CertificadoDigital.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,117 +33,165 @@

using System;
using System.IO;
using System.Security;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace DFe.Utils.Assinatura
{
public static class CertificadoDigital
{
private static X509Certificate2 _certificado;

#region Métodos privados

/// <summary>
/// Exibe a lista de certificados instalados no PC e devolve o certificado selecionado
/// Cria e devolve um objeto <see cref="X509Store"/>
/// </summary>
/// <param name="openFlags"></param>
/// <returns></returns>
public static X509Certificate2 ObterDoRepositorio()
private static X509Store ObterX509Store(OpenFlags openFlags)
{
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
store.Open(openFlags);
return store;
}

var collection = store.Certificates;
var fcollection = collection.Find(X509FindType.FindByTimeValid, DateTime.Now, true);
var scollection = X509Certificate2UI.SelectFromCollection(fcollection, "Certificados válidos:", "Selecione o certificado que deseja usar",
X509SelectionFlag.SingleSelection);
#region Métodos para obter um certificado X509Certificate2

if (scollection.Count == 0)
/// <summary>
/// Obtém um certificado a partir do arquivo e da senha passados nos parâmetros
/// </summary>
/// <param name="arquivo">Arquivo do certificado digital</param>
/// <param name="senha">Senha do certificado digital</param>
/// <returns></returns>
private static X509Certificate2 ObterDeArquivo(string arquivo, string senha)
{
if (!File.Exists(arquivo))
{
throw new Exception("Nenhum certificado foi selecionado!");
throw new Exception(String.Format("Certificado digital {0} não encontrado!", arquivo));
}

store.Close();
return scollection[0];
var certificado = new X509Certificate2(arquivo, senha, X509KeyStorageFlags.MachineKeySet);
return certificado;
}

/// <summary>
/// Obtém um certificado instalado no PC a partir do número de série passado no parâmetro
/// Obtém um objeto <see cref="X509Certificate2"/> pelo serial passado no parÂmetro
/// </summary>
/// <param name="numeroSerial">Serial do certificado</param>
/// <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>
/// <returns></returns>
public static X509Certificate2 ObterDoRepositorio(string numeroSerial, string senha = null)
private static X509Certificate2 ObterDoRepositorio(string serial, OpenFlags opcoesDeAbertura)
{
if (string.IsNullOrEmpty(numeroSerial))
throw new Exception("O nº de série do certificado não foi informado para a função ObterDoRepositorio!");

X509Certificate2 certificado = null;

var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
var store = ObterX509Store(opcoesDeAbertura);
try
{
store.Open(OpenFlags.MaxAllowed);

foreach (var item in store.Certificates)
{
if (item.SerialNumber != null && item.SerialNumber.ToUpper().Equals(numeroSerial.ToUpper(), StringComparison.InvariantCultureIgnoreCase))
if (item.SerialNumber != null && item.SerialNumber.ToUpper().Equals(serial.ToUpper(), StringComparison.InvariantCultureIgnoreCase))
certificado = item;
}

if (certificado == null)
throw new Exception(string.Format("Certificado digital nº {0} não encontrado!", numeroSerial.ToUpper()));
throw new Exception(string.Format("Certificado digital nº {0} não encontrado!", serial.ToUpper()));
}
finally
{
store.Close();
}


return certificado;
}

/// <summary>
/// Obtém um objeto <see cref="X509Certificate2"/> pelo serial passado no parâmetro e com opção de definir o PIN
/// </summary>
/// <param name="serial"></param>
/// <param name="senha"></param>
/// <returns></returns>
private static X509Certificate2 ObterDoRepositorioPassandoPin(string serial, string senha = null)
{
var certificado = ObterDoRepositorio(serial, OpenFlags.ReadOnly);
if (string.IsNullOrEmpty(senha)) return certificado;
certificado.DefinirPinParaChavePrivada(senha);
return certificado;
}

//Se a senha for passada no parâmetro
var senhaSegura = new SecureString();
var passPhrase = senha.ToCharArray();
foreach (var t in passPhrase)
{
senhaSegura.AppendChar(t);
}
#endregion

var chavePrivada = certificado.PrivateKey as RSACryptoServiceProvider;
if (chavePrivada == null) return certificado;
/// <summary>
/// Define o PIN para chave privada de um objeto <see cref="X509Certificate2"/> passado no parâmetro
/// </summary>
private static void DefinirPinParaChavePrivada(this X509Certificate2 certificado, string pin)
{
if (certificado == null) throw new ArgumentNullException("certificado");
var key = (RSACryptoServiceProvider)certificado.PrivateKey;

var cspParameters = new CspParameters(chavePrivada.CspKeyContainerInfo.ProviderType,
chavePrivada.CspKeyContainerInfo.ProviderName,
chavePrivada.CspKeyContainerInfo.KeyContainerName,
null,
senhaSegura);
var rsaCsp = new RSACryptoServiceProvider(cspParameters);
certificado.PrivateKey = rsaCsp;
return certificado;
var providerHandle = IntPtr.Zero;
var pinBuffer = Encoding.ASCII.GetBytes(pin);

MetodosNativos.Executar(() => MetodosNativos.CryptAcquireContext(ref providerHandle,
key.CspKeyContainerInfo.KeyContainerName,
key.CspKeyContainerInfo.ProviderName,
key.CspKeyContainerInfo.ProviderType,
MetodosNativos.CryptContextFlags.Silent));
MetodosNativos.Executar(() => MetodosNativos.CryptSetProvParam(providerHandle,
MetodosNativos.CryptParameter.KeyExchangePin,
pinBuffer, 0));
MetodosNativos.Executar(() => MetodosNativos.CertSetCertificateContextProperty(
certificado.Handle,
MetodosNativos.CertificateProperty.CryptoProviderHandle,
0, providerHandle));
}

/// <summary>
/// Obtém um certificado a partir do arquivo e da senha passados nos parâmetros
/// Se a propriedade <see cref="ConfiguracaoCertificado.Arquivo"/> for informada, Obtém o certificado do arquivo, senão obtém o certificado do repositório
/// </summary>
/// <param name="arquivo">Arquivo do certificado digital</param>
/// <param name="senha">Senha do certificado digital</param>
/// <returns></returns>
public static X509Certificate2 ObterDeArquivo(string arquivo, string senha)
private static X509Certificate2 ObterDadosCertificado(ConfiguracaoCertificado configuracaoCertificado)
{
if (!File.Exists(arquivo))
switch (configuracaoCertificado.TipoCertificado)
{
throw new Exception(string.Format("Certificado digital {0} não encontrado!", arquivo));
case TipoCertificado.A1Repositorio:
return ObterDoRepositorio(configuracaoCertificado.Serial, OpenFlags.MaxAllowed);
case TipoCertificado.A1Arquivo:
return ObterDeArquivo(configuracaoCertificado.Arquivo, configuracaoCertificado.Senha);
case TipoCertificado.A3:
return ObterDoRepositorioPassandoPin(configuracaoCertificado.Serial, configuracaoCertificado.Senha);
default:
throw new ArgumentOutOfRangeException();
}

var certificado = new X509Certificate2(arquivo, senha, X509KeyStorageFlags.MachineKeySet);
return certificado;
}

private static X509Certificate2 _certificado;
#endregion

/// <summary>
/// Exibe a lista de certificados instalados no PC e devolve o certificado selecionado
/// </summary>
/// <returns></returns>
public static X509Certificate2 ListareObterDoRepositorio()
{
var store = ObterX509Store(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var collection = store.Certificates;
var fcollection = collection.Find(X509FindType.FindByTimeValid, DateTime.Now, true);
var scollection = X509Certificate2UI.SelectFromCollection(fcollection, "Certificados válidos:", "Selecione o certificado que deseja usar",
X509SelectionFlag.SingleSelection);

if (scollection.Count == 0)
{
throw new Exception("Nenhum certificado foi selecionado!");
}

store.Close();
return scollection[0];
}

/// <summary>
/// Obtém um objeto contendo o certificado digital
/// <para>Se for informado <see cref="ConfiguracaoCertificado.Arquivo"/>,
/// o certificado digital será obtido pelo método <see cref="ObterDeArquivo(string,string)"/>,
/// senão será obtido pelo método <see cref="ObterDoRepositorio()"/> </para>
/// senão será obtido pelo método <see cref="ListareObterDoRepositorio"/> </para>
/// <para>Para liberar os recursos do certificado, após seu uso, invoque o método <see cref="X509Certificate2.Reset()"/></para>
/// </summary>
public static X509Certificate2 ObterCertificado(ConfiguracaoCertificado configuracaoCertificado)
Expand All @@ -155,14 +203,58 @@ public static X509Certificate2 ObterCertificado(ConfiguracaoCertificado configur
_certificado = ObterDadosCertificado(configuracaoCertificado);
return _certificado;
}
}

private static X509Certificate2 ObterDadosCertificado(ConfiguracaoCertificado configuracaoCertificado)
internal static class MetodosNativos
{
internal enum CryptContextFlags
{
return string.IsNullOrEmpty(configuracaoCertificado.Arquivo)
? ObterDoRepositorio(configuracaoCertificado.Serial,
configuracaoCertificado.Senha)
: ObterDeArquivo(configuracaoCertificado.Arquivo,
configuracaoCertificado.Senha);
None = 0,
Silent = 0x40
}

internal enum CertificateProperty
{
None = 0,
CryptoProviderHandle = 0x1
}

internal enum CryptParameter
{
None = 0,
KeyExchangePin = 0x20
}

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptAcquireContext(
ref IntPtr hProv,
string containerName,
string providerName,
int providerType,
CryptContextFlags flags
);

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool CryptSetProvParam(
IntPtr hProv,
CryptParameter dwParam,
[In] byte[] pbData,
uint dwFlags);

[DllImport("CRYPT32.DLL", SetLastError = true)]
internal static extern bool CertSetCertificateContextProperty(
IntPtr pCertContext,
CertificateProperty propertyId,
uint dwFlags,
IntPtr pvData
);

public static void Executar(Func<bool> action)
{
if (!action())
{
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
Loading

0 comments on commit cc39d41

Please sign in to comment.