CryptoPro / corefx

This repo contains the .NET Core foundational libraries, called CoreFX. It includes classes for collections, file systems, console, XML, async and many others. We welcome contributions.
https://github.com/dotnet/core
MIT License
27 stars 7 forks source link

GetMethod GetPrivateKeyInfo missing #12

Closed Demonell closed 5 years ago

Demonell commented 5 years ago

I'm unable to sign xml smev message since X509Certificate2 has no GetPrivateKeyInfo method.

        [Fact]
        public void GetPrivateKeyInfoMethodExists()
        {
            var getPrivateKeyInfoMethod = typeof(X509Certificate2).GetMethod("GetPrivateKeyInfo", BindingFlags.Static | BindingFlags.NonPublic);
            Assert.NotNull(getPrivateKeyInfoMethod);
        }

This test pass in Framework version using CryptoPro .net sdk and does not pass in current corefx fork version.

Fasjeit commented 5 years ago

Hi.

This method does not exist anymore in dotnet core. X509Certificate2.cs

Why do you even need this non-public method? If you are trying to get CspParameters of the specified certificate private key you can just simply use CspKeyContainerInfo property.

var certKey = signerCert.PrivateKey as Gost3410CryptoServiceProvider;
var cspParameters = new CspParameters()
{
    KeyContainerName = certKey.CspKeyContainerInfo.KeyContainerName,
    ProviderType = certKey.CspKeyContainerInfo.ProviderType,
    ProviderName = certKey.CspKeyContainerInfo.ProviderName,
    Flags = certKey.CspKeyContainerInfo.MachineKeyStore
        ? (CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore)
        : (CspProviderFlags.UseExistingKey),
    KeyNumber = (int)certKey.CspKeyContainerInfo.KeyNumber
};

Also if you are planning to use SignedXml class you need to use gost_x509_xml branch if you are building from the sources, or nuget packages. For now SignedXml is windows only, Linux support is on its way.

Demonell commented 5 years ago

I have builded gost_x509_xml branch. CspKeyContainerInfo and PrivateKey getting works perfect. Tried to use SignedXml class and got exception. Same test works fine in net framework.

Exception

System.Security.Cryptography.CryptographicException : SignatureDescription could not be created for the signature algorithm supplied.
   at System.Security.Cryptography.Xml.SignedXml.ComputeSignature()

Test

[Theory]
[InlineData(StoreName.My, StoreLocation.CurrentUser, "03F7FA48BE7B112598C9ED6CE4CA968B74541A98", true)]
public void XmlSigner(StoreName storeName, StoreLocation storeLocation, string thumbprint, bool onlyValidCert)
{
    var store = new X509Store(storeName, storeLocation);
    store.Open(OpenFlags.ReadOnly);
    var certificate = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, onlyValidCert)[0];
    store.Close();

    var xmlDocument = new XmlDocument();
    xmlDocument.LoadXml(@"<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<S:Envelope xmlns:S='http://schemas.xmlsoap.org/soap/envelope/' xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' xmlns:wsu='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'>
<S:Header>
<wsse:Security S:actor='http://smev.gosuslugi.ru/actors/smev'>
    <ds:Signature xmlns:ds='http://www.w3.org/2000/09/xmldsig#'>
        <ds:KeyInfo>
            <wsse:SecurityTokenReference>
                <wsse:Reference URI='#SenderCertificate'/>
            </wsse:SecurityTokenReference>
        </ds:KeyInfo>
    </ds:Signature>
    <wsse:BinarySecurityToken EncodingType='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary' ValueType='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3' wsu:Id='SenderCertificate'>
    </wsse:BinarySecurityToken>
</wsse:Security>
</S:Header>
<S:Body wsu:Id='body'>
<ws:queryINNFL xmlns:ws='http://ws.unisoft/'>
    <smev:Message xmlns:smev='http://smev.gosuslugi.ru/rev110801'>
        <smev:Sender>
            <smev:Code>MINECONOMSK_SYS_1</smev:Code>
            <smev:Name>Mincomsvyaz SK</smev:Name>
        </smev:Sender>
        <smev:Recipient>
            <smev:Code>13312</smev:Code>
            <smev:Name>FNS</smev:Name>
        </smev:Recipient>
        <smev:Originator>
            <smev:Code>MINECONOMSK_SYS_1</smev:Code>
            <smev:Name>Mincomsvyaz SK</smev:Name>
        </smev:Originator>
        <smev:TypeCode>2</smev:TypeCode>
        <smev:Date>2012-03-13T11:10:54.54Z</smev:Date>
    </smev:Message>
    <smev:MessageData xmlns:smev='http://smev.gosuslugi.ru/rev110801'>
        <smev:AppData wsu:Id='fns-AppData'>
            <Document xmlns='http://ws.unisoft/FNSINN/queryINNFL' Id='AB324006-978B-44D4-933D-C5E6DFA8A576'>data</Document>
        </smev:AppData>
    </smev:MessageData>
</ws:queryINNFL>
</S:Body>
</S:Envelope>");

    var signedXml = new SignedXml(xmlDocument)
    {
        SigningKey = certificate.GetGost3410_2012_256PrivateKey()
    };

    var reference = new Reference
    {
        Uri = "",
        DigestMethod = "urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34112012-256"
    };
    reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
    reference.AddTransform(new XmlDsigExcC14NTransform());
    signedXml.AddReference(reference);

    signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
    signedXml.SignedInfo.SignatureMethod = "urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102012-gostr34112012-256";

    signedXml.ComputeSignature();

    var xml = signedXml.GetXml();

    Assert.NotNull(signedXml.SignedInfo);
    Assert.NotNull(signedXml.SignatureValue);
    Assert.NotNull(certificate.RawData);
}
Fasjeit commented 5 years ago

Make sure you reference your local xml and pkcs assemblies. This can be done using these lines in csproj file:

    <ItemGroup>
      <Reference Include="System.Security.Cryptography.Pkcs">
        <HintPath>..\corefx\artifacts\bin\runtime\netcoreapp-Windows_NT-Debug-x64\System.Security.Cryptography.Pkcs.dll</HintPath>
      </Reference>
      <Reference Include="System.Security.Cryptography.Xml">
        <HintPath>..\corefx\artifacts\bin\runtime\netcoreapp-Windows_NT-Debug-x64\System.Security.Cryptography.Xml.dll</HintPath>
      </Reference>
    </ItemGroup>

This should be done as these assemblies are not included in corefx package, and distributed by ms as stand-alone packages.

Demonell commented 5 years ago

Thank you for quick replies, all works fine now!

Am I getting right if I need make it compatible for linux then I have to change it to

  <ItemGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">
    <Reference Include="System.Security.Cryptography.Pkcs">
      <HintPath>..\..\..\corefx\corefx\artifacts\bin\runtime\some-linux-folder\System.Security.Cryptography.Pkcs.dll</HintPath>
    </Reference>
    <Reference Include="System.Security.Cryptography.Xml">
      <HintPath>..\..\..\corefx\corefx\artifacts\bin\runtime\some-linux-folder\System.Security.Cryptography.Xml.dll</HintPath>
    </Reference>
    <Reference Include="System.Security.Permissions">
      <HintPath>..\..\..\corefx\corefx\artifacts\bin\runtime\some-linux-folder\System.Security.Permissions.dll</HintPath>
    </Reference>
  </ItemGroup>

  <ItemGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">
    <Reference Include="System.Security.Cryptography.Pkcs">
      <HintPath>..\..\..\corefx\corefx\artifacts\bin\runtime\netcoreapp-Windows_NT-Debug-x64\System.Security.Cryptography.Pkcs.dll</HintPath>
    </Reference>
    <Reference Include="System.Security.Cryptography.Xml">
      <HintPath>..\..\..\corefx\corefx\artifacts\bin\runtime\netcoreapp-Windows_NT-Debug-x64\System.Security.Cryptography.Xml.dll</HintPath>
    </Reference>
    <Reference Include="System.Security.Permissions">
      <HintPath>..\..\..\corefx\corefx\artifacts\bin\runtime\netcoreapp-Windows_NT-Debug-x64\System.Security.Permissions.dll</HintPath>
    </Reference>
  </ItemGroup>

And in this case what folder should I use instead of netcoreapp-Windows_NT-Debug-x64 for linux?

Fasjeit commented 5 years ago

For linux you have to replace netstandard assemblies:

git clone https://github.com/CryptoProLLC/NetStandard.Library
mkdir -p ~/.nuget/packages/netstandard.library
cp -r ./NetStandard.Library/nugetReady/netstandard.library ~/.nuget/packages/

Linux runtime artifacts folder is

corefx/artifacts/bin/runtime/netcoreapp-Linux-Debug-x64

Also (as I said before) for now SignedXml and some Pkcs features are windows only, Linux support is on its way. Do not expect anything to work properly in SignedXml on linux.

Demonell commented 5 years ago

Got it, thank you!