├── fiscalapi.ico
├── fiscalapi.png
├── Common
├── CredentialType.cs
├── Utf8StringWriter.cs
├── FileType.cs
├── CredentialSettings.cs
├── Flags.cs
├── StringExtensions.cs
└── HelpersToVerify.cs
├── fiscalapi-credentials.sln
├── .github
└── workflows
│ └── CICD.yml
├── Core
├── IPrivateKey.cs
├── PrivateKey.cs
├── ICertificate.cs
├── ICredential.cs
├── Certificate.cs
└── Credential.cs
├── fiscalapi-credentials.csproj
├── .gitignore
├── README.md
└── LICENSE.txt
/fiscalapi.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FiscalAPI/fiscalapi-credentials-net/HEAD/fiscalapi.ico
--------------------------------------------------------------------------------
/fiscalapi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FiscalAPI/fiscalapi-credentials-net/HEAD/fiscalapi.png
--------------------------------------------------------------------------------
/Common/CredentialType.cs:
--------------------------------------------------------------------------------
1 | namespace Fiscalapi.Credentials.Common;
2 |
3 | public enum CredentialType
4 | {
5 | Csd,
6 | Fiel,
7 | }
--------------------------------------------------------------------------------
/Common/Utf8StringWriter.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 |
4 | namespace Fiscalapi.Credentials.Common
5 | {
6 | public class Utf8StringWriter : StringWriter
7 | {
8 | public override Encoding Encoding
9 | {
10 | get { return Encoding.UTF8; }
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Common/FileType.cs:
--------------------------------------------------------------------------------
1 | namespace Fiscalapi.Credentials.Common;
2 |
3 | ///
4 | /// This will be used when implementing data persistence with entity framework core.
5 | ///
6 | public enum FileType
7 | {
8 | CertificateCsd,
9 | PrivateKeyCsd,
10 | CertificateFiel,
11 | PrivateKeyFiel,
12 | Pfx,
13 | }
--------------------------------------------------------------------------------
/Common/CredentialSettings.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Fiscalapi.Credentials.Common;
3 |
4 | public static class CredentialSettings
5 | {
6 | //Algoritmos ahora son parte de la instancia (thread-safe)
7 |
8 | ///
9 | /// Path of the CadenaOriginal.xslt file to do XML data transformation using an XSLT stylesheet.
10 | ///
11 | public static string? OriginalStringPath { get; set; }
12 | }
--------------------------------------------------------------------------------
/Common/Flags.cs:
--------------------------------------------------------------------------------
1 | namespace Fiscalapi.Credentials.Common;
2 |
3 | ///
4 | /// headers and footers for PEM formats files
5 | ///
6 | public static class Flags
7 | {
8 | ///
9 | /// Header for private key
10 | ///
11 | public const string PemPrivateKey = "PRIVATE KEY";
12 |
13 | ///
14 | /// Header for certificate
15 | ///
16 | public const string PemCertificate = "CERTIFICATE";
17 | public const string PemPublicKey = "PUBLIC KEY";
18 | public const string PemRsaPrivateKey = "RSA PRIVATE KEY";
19 | public const string PemEncriptedPrivateKey = "ENCRYPTED PRIVATE KEY";
20 | }
--------------------------------------------------------------------------------
/fiscalapi-credentials.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.12.35514.174 d17.12
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "fiscalapi-credentials", "fiscalapi-credentials.csproj", "{F1B9B06E-F9FD-4C21-9B91-A96E7B0F52B3}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {F1B9B06E-F9FD-4C21-9B91-A96E7B0F52B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {F1B9B06E-F9FD-4C21-9B91-A96E7B0F52B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {F1B9B06E-F9FD-4C21-9B91-A96E7B0F52B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {F1B9B06E-F9FD-4C21-9B91-A96E7B0F52B3}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/.github/workflows/CICD.yml:
--------------------------------------------------------------------------------
1 | name: Publish NuGet Package
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | build-and-publish:
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | # Checkout the repository
12 | - name: Checkout repository
13 | uses: actions/checkout@v3
14 |
15 | # Setup .NET
16 | - name: Setup .NET
17 | uses: actions/setup-dotnet@v3
18 | with:
19 | dotnet-version: '9.0.x'
20 |
21 | # Restore dependencies
22 | - name: Restore dependencies
23 | run: dotnet restore
24 |
25 | # Step to extract the AssemblyVersion from the .csproj file
26 | - name: Extract Package Version
27 | id: get_version
28 | run: |
29 | version=$(grep -oPm1 "(?<=)[^<]+" $(find . -name "*.csproj"))
30 | echo "PACKAGE_VERSION=$version" >> $GITHUB_ENV
31 | echo "Package Version extracted: $version"
32 |
33 | # Build the project
34 | - name: Build the project
35 | run: dotnet build --configuration Release
36 |
37 | # Pack the NuGet package
38 | - name: Pack NuGet Package
39 | run: dotnet pack --configuration Release --output ./nupkg
40 |
41 | # Publish the package to NuGet
42 | - name: Publish to NuGet
43 | env:
44 | NUGET_KEY: ${{ secrets.NUGET_KEY }}
45 | run: |
46 | dotnet nuget push ./nupkg/*.nupkg \
47 | --api-key $NUGET_KEY \
48 | --source https://api.nuget.org/v3/index.json
49 |
--------------------------------------------------------------------------------
/Core/IPrivateKey.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Cryptography;
2 |
3 | namespace Fiscalapi.Credentials.Core;
4 |
5 | ///
6 | /// Represents a wrapper for FIEL and CSD private key.
7 | ///
8 | public interface IPrivateKey
9 | {
10 | ///
11 | /// File .key encoded in base64
12 | ///
13 | string Base64 { get; }
14 |
15 | ///
16 | /// Private key password
17 | ///
18 | string PasswordPhrase { get; }
19 |
20 | ///
21 | /// Private key RSA object
22 | ///
23 | RSA RsaPrivateKey { get; }
24 |
25 | ///
26 | /// File .key in bytes
27 | ///
28 | byte[] PrivateKeyBytes { get; }
29 |
30 | ///
31 | /// Private key password converted to bytes
32 | ///
33 | byte[] PasswordPhraseBytes { get; }
34 |
35 | ///
36 | /// Convert PKCS#8 DER private key to PKCS#8 PEM
37 | ///
38 | ///
39 | string GetPemRepresentation();
40 |
41 | ///
42 | /// Sign some data
43 | ///
44 | /// string to be signed
45 | /// Hash algorithm to use for signing
46 | /// Signature padding to use
47 | /// signed bytes
48 | byte[] SignData(string toSign, HashAlgorithmName algorithm, RSASignaturePadding padding);
49 |
50 | ///
51 | /// Verify the signature of some data
52 | ///
53 | /// original data in bytes
54 | /// signed data in bytes
55 | /// Hash algorithm to use for verification
56 | /// Signature padding to use
57 | /// True when the signature is valid, otherwise false
58 | bool VerifyData(byte[] dataToVerify, byte[] signedData, HashAlgorithmName algorithm, RSASignaturePadding padding);
59 | }
--------------------------------------------------------------------------------
/fiscalapi-credentials.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0;net9.0
5 | 4.0.340
6 | $(Version)
7 | $(Version)
8 | $(Version)
9 | Fiscalapi.Credentials
10 | Fiscalapi.Credentials
11 | Fiscalapi.Credentials
12 | Fiscalapi
13 | fiscalapi.png
14 | Biblioteca .NET para el manejo de Certificados de Sello Digital (CSD) y Firma Electrónica Avanzada (FIEL/e.firma) del SAT. Simplifica operaciones criptográficas como firma digital (sellado), verificación de firmas, cálculo de hashes, y extracción de información de certificados. Perfecta para desarrolladores que necesitan integrar autenticación digital en sus aplicaciones de facturación electrónica o trámites gubernamentales en México. Compatible con los formatos originales del SAT sin necesidad de conversiones adicionales.
15 | sat csd fiel e-firma efirma sellos-digitales cfdi facturacion-electronica mexico certificados-digitales firma-digital cryptography cer key pkcs8 pkcs12 pfx dotnet
16 | Fiscalapi credentials
17 | Fiscalapi
18 | fiscalapi.ico
19 | True
20 | FISCAL API S DE R.L DE C.V
21 | FISCAL API S DE R.L DE C.V © 2019
22 | https://www.fiscalapi.com
23 | README.md
24 | https://github.com/FiscalAPI/fiscalapi-credentials-net
25 | git
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Common/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 |
4 | namespace Fiscalapi.Credentials.Common
5 | {
6 | public static class StringExtensions
7 | {
8 | ///
9 | /// Encode string to base64
10 | ///
11 | /// string to encode
12 | /// base64 encoded string
13 | public static string EncodeToBase64(this string plainText)
14 | {
15 | var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
16 | return Convert.ToBase64String(plainTextBytes);
17 | }
18 |
19 | ///
20 | /// Decode string to plainText
21 | ///
22 | /// base64 encoded data to decode
23 | /// plainText
24 | public static string DecodeFromBase64(this string base64EncodedData)
25 | {
26 | var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
27 | return Encoding.UTF8.GetString(base64EncodedBytes);
28 | }
29 |
30 | ///
31 | /// Get array bytes from any string
32 | ///
33 | ///
34 | /// array bytes
35 | public static byte[] GetBytes(this string plainText)
36 | {
37 | return Encoding.UTF8.GetBytes(plainText);
38 | }
39 |
40 |
41 | ///
42 | /// Converts an array of bytes to base64 encode
43 | ///
44 | ///
45 | /// base64String
46 | public static string ToBase64String(this byte[] inArray)
47 | {
48 | return Convert.ToBase64String(inArray);
49 | }
50 |
51 | ///
52 | /// Get an array of bytes of base64 encoded text
53 | ///
54 | ///
55 | ///
56 | public static byte[] FromBase64String(this string base64EncodedText)
57 | {
58 | return Convert.FromBase64String(base64EncodedText);
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/Core/PrivateKey.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Cryptography;
3 | using System.Text;
4 | using Fiscalapi.Credentials.Common;
5 |
6 | namespace Fiscalapi.Credentials.Core;
7 |
8 | ///
9 | /// Represents a wrapper for FIEL and CSD private key.
10 | ///
11 | public class PrivateKey : IPrivateKey
12 | {
13 | public PrivateKey(string fileInbase64, string passwordPhrase)
14 | {
15 | PasswordPhrase = passwordPhrase;
16 | Base64 = fileInbase64;
17 | RsaPrivateKey = RSA.Create();
18 | RsaPrivateKey.ImportEncryptedPkcs8PrivateKey(PasswordPhraseBytes, PrivateKeyBytes, out _);
19 | }
20 |
21 | ///
22 | /// File .key encoded in base64
23 | ///
24 | public string Base64 { get; }
25 |
26 | ///
27 | /// Private key password
28 | ///
29 | public string PasswordPhrase { get; }
30 |
31 | ///
32 | /// Private key RSA object
33 | ///
34 | public RSA RsaPrivateKey { get; }
35 |
36 | ///
37 | /// File .key in bytes
38 | ///
39 | public byte[] PrivateKeyBytes
40 | {
41 | get => Convert.FromBase64String(Base64);
42 | }
43 |
44 | ///
45 | /// Private key password converted to bytes
46 | ///
47 | public byte[] PasswordPhraseBytes
48 | {
49 | get => Encoding.ASCII.GetBytes(PasswordPhrase);
50 | }
51 |
52 | ///
53 | /// Convert PKCS#8 DER private key to PKCS#8 PEM
54 | ///
55 | ///
56 | public string GetPemRepresentation()
57 | {
58 | var keyPem = new string(PemEncoding.Write(Flags.PemPrivateKey, RsaPrivateKey.ExportPkcs8PrivateKey()));
59 | return keyPem;
60 | }
61 |
62 | ///
63 | /// Sign some data
64 | ///
65 | /// string to be signed
66 | /// Hash algorithm to use for signing
67 | /// Signature padding to use
68 | /// signed bytes
69 | public byte[] SignData(string toSign, HashAlgorithmName algorithm, RSASignaturePadding padding)
70 | {
71 | //get bytes array to sing
72 | var bytesToSign = toSign.GetBytes();
73 |
74 | //Sing and get signed bytes array
75 | var signedBytes = RsaPrivateKey.SignData(bytesToSign, algorithm, padding);
76 |
77 | //Converts signed bytes to base64
78 | return signedBytes;
79 | }
80 |
81 | ///
82 | /// Verify the signature of some data
83 | ///
84 | /// original data in bytes
85 | /// signed data in bytes
86 | /// Hash algorithm to use for verification
87 | /// Signature padding to use
88 | /// True when the signature is valid, otherwise false
89 | public bool VerifyData(byte[] dataToVerify, byte[] signedData, HashAlgorithmName algorithm, RSASignaturePadding padding)
90 | {
91 | try
92 | {
93 | //Validation
94 | var isValid = RsaPrivateKey.VerifyData(dataToVerify, signedData, algorithm, padding);
95 |
96 |
97 | return isValid;
98 | }
99 | catch (CryptographicException e)
100 | {
101 | Console.WriteLine(e.Message);
102 | return false;
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/Core/ICertificate.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Fiscalapi.Credentials.Core;
5 |
6 | public interface ICertificate
7 | {
8 | ///
9 | /// The result of reading the bytes from the .cer file and converting them to base64
10 | ///
11 | string PlainBase64 { get; }
12 |
13 | ///
14 | /// The equivalent of reading the bytes from the .cer file and converting them to base64
15 | ///
16 | byte[] CertificatePlainBytes { get; }
17 |
18 | ///
19 | /// RFC as parsed from subject/x500UniqueIdentifier
20 | /// see https://oidref.com/2.5.4.45
21 | ///
22 | string Rfc { get; }
23 |
24 | ///
25 | /// Organization = 'razón social'
26 | ///
27 | string Organization
28 | {
29 | // CN: CommonName
30 | // OU: OrganizationalUnit
31 | // O: Organization
32 | // L: Locality
33 | // S: StateOrProvinceName
34 | // C: CountryName
35 | get;
36 | }
37 |
38 | ///
39 | /// OrganizationalUnit = 'Sucursal'
40 | /// As of 2019-08-01 is known that only CSD have OU (Organization Unit)
41 | ///
42 | string OrganizationalUnit
43 | {
44 | // CN: CommonName
45 | // OU: OrganizationalUnit
46 | // O: Organization
47 | // L: Locality
48 | // S: StateOrProvinceName
49 | // C: CountryName
50 |
51 | get;
52 | }
53 |
54 | ///
55 | /// All serial number
56 | ///
57 | string SerialNumber { get; }
58 |
59 | ///
60 | /// Certificate number as Mexican tax authority (SAT) require.
61 | ///
62 | string CertificateNumber { get; }
63 |
64 | ///
65 | /// Issuer data parsed into KeyValuePair collection
66 | ///
67 | List> IssuerKeyValuePairs { get; }
68 |
69 | ///
70 | /// Raw X509Certificate2 Issuer property
71 | ///
72 | string Issuer { get; }
73 |
74 | ///
75 | /// Subject data parsed into KeyValuePair collection
76 | /// see https://oidref.com/2.5.4.45
77 | ///
78 | List> SubjectKeyValuePairs { get; }
79 |
80 | ///
81 | /// Raw X509Certificate2 Subject property
82 | /// see https://oidref.com/2.5.4.45
83 | ///
84 | string Subject { get; }
85 |
86 | ///
87 | /// Certificate version
88 | ///
89 | int Version { get; }
90 |
91 | ///
92 | /// Valid start date
93 | ///
94 | DateTime ValidFrom { get; }
95 |
96 | ///
97 | /// Valid end date
98 | ///
99 | DateTime ValidTo { get; }
100 |
101 | ///
102 | /// Raw Data Length
103 | ///
104 | int RawDataLength { get; }
105 |
106 | ///
107 | /// RawDataBytes
108 | ///
109 | byte[] RawDataBytes { get; }
110 |
111 | ///
112 | /// True if ValidTo date is less than the current date
113 | ///
114 | bool IsValid();
115 |
116 | ///
117 | /// True when is a FIEL certificate
118 | ///
119 | bool IsFiel();
120 |
121 | ///
122 | /// Convert X.509 DER base64 or X.509 DER to X.509 PEM
123 | ///
124 | ///
125 | string GetPemRepresentation();
126 | }
--------------------------------------------------------------------------------
/Core/ICredential.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Cryptography;
2 | using Fiscalapi.Credentials.Common;
3 |
4 | namespace Fiscalapi.Credentials.Core;
5 |
6 | public interface ICredential
7 | {
8 | ///
9 | /// Private key password
10 | ///
11 | string PasswordPhrase { get; }
12 |
13 | ///
14 | /// Dotnetcfdi certificate wrapper
15 | ///
16 | ICertificate Certificate { get; }
17 |
18 | ///
19 | /// Dotnetcfdi PrivateKey wrapper
20 | ///
21 | IPrivateKey PrivateKey { get; }
22 |
23 | string PemCertificate { get; }
24 | string PemPrivateKey { get; }
25 |
26 | ///
27 | /// Fiel whe credential.certificate is a FIEL certificate otherwise csd
28 | ///
29 | CredentialType CredentialType { get; }
30 |
31 | ///
32 | /// Signature algorithm used for signing data. Default is SHA256 (for invoicing).
33 | ///
34 | HashAlgorithmName SignatureAlgorithm { get; set; }
35 |
36 | ///
37 | /// Signature padding used for signing data. Default is Pkcs1 (used for both invoicing and XML downloader).
38 | ///
39 | RSASignaturePadding SignaturePadding { get; set; }
40 |
41 | byte[] CreatePFX();
42 |
43 | ///
44 | /// Sign some data
45 | ///
46 | /// string to be signed
47 | /// signed bytes
48 | /// see CredentialSettings class to see signature parameters
49 | byte[] SignData(string toSign);
50 |
51 | ///
52 | /// Verify the signature of some data
53 | ///
54 | /// original data in bytes
55 | /// signed data in bytes
56 | /// True when the signature is valid, otherwise false
57 | bool VerifyData(byte[] dataToVerify, byte[] signedData);
58 |
59 | ///
60 | /// True if Certificate.ValidTo date is less than the current date
61 | ///
62 | bool IsValid();
63 |
64 | ///
65 | /// True when is a FIEL certificate
66 | ///
67 | bool IsFiel();
68 |
69 | ///
70 | /// True when certificate.ValidTo date is less than the current date and is a FIEL certificate
71 | ///
72 | ///
73 | bool IsValidFiel();
74 |
75 | ///
76 | /// Convert the input string to a byte array and compute the hash.
77 | ///
78 | /// data to hashing
79 | /// encoded b64 hash
80 | string CreateHash(string input);
81 |
82 | ///
83 | /// Verify a hash against a string.
84 | ///
85 | /// data to hashing
86 | /// encoded b64 hash
87 | /// true when computed hash is same of input hash otherwise false
88 | bool VerifyHash(string input, string hash);
89 |
90 |
91 | ///
92 | /// Transform XML documents into Pipe character in accordance with the schemes established in Mexican legislation.
93 | ///
94 | /// Xml file as string
95 | /// cadena original
96 | public string GetOriginalStringByXmlString(string xmlAsString);
97 |
98 |
99 | ///
100 | /// Configure the Signature algorithm to do invoicing, using the donetcfdi/invoicing library.
101 | /// Sets HashAlgorithmName.SHA256 for signing invoices. Returns this for fluent interface.
102 | ///
103 | public ICredential ConfigureAlgorithmForInvoicing();
104 |
105 | ///
106 | /// Configure the Signature algorithm to do xml-downloader, using the donetcfdi/xml-downloader library.
107 | /// Sets HashAlgorithmName.SHA1 for downloading xml. Returns this for fluent interface.
108 | ///
109 | public ICredential ConfigureAlgorithmForXmlDownloader();
110 | }
--------------------------------------------------------------------------------
/Common/HelpersToVerify.cs:
--------------------------------------------------------------------------------
1 | //using StringBuilder = Chilkat.StringBuilder;
2 |
3 | //namespace Credencials.Common
4 | //{
5 | // class Credentials
6 | // {
7 | // private byte[] certificateBytes;
8 | // private byte[] keyBytes;
9 | // private string password;
10 |
11 | // public Credentials(byte[] certificateBytes, byte[] keyBytes, string password)
12 | // {
13 | // this.certificateBytes = certificateBytes;
14 | // this.keyBytes = keyBytes;
15 | // this.password = password;
16 | // }
17 |
18 | // public string GetCertificateNumber()
19 | // {
20 | // Cert certificate = LoadCertificate();
21 | // return GetCertificateNumber(certificate);
22 | // }
23 | // private string GetCertificateNumber(Cert certificate)
24 | // {
25 | // string hexadecimalString = certificate.SerialNumber;
26 | // StringBuilder sb = new StringBuilder();
27 | // for (int i = 0; i <= hexadecimalString.Length - 2; i += 2)
28 | // {
29 | // sb.Append(Convert.ToString(Convert.ToChar(Int32.Parse(hexadecimalString.Substring(i, 2), System.Globalization.NumberStyles.HexNumber))));
30 | // }
31 | // return sb.ToString();
32 | // }
33 | // public string GetCertificateName()
34 | // {
35 | // Cert certificate = LoadCertificate();
36 | // return GetCertificateName(certificate);
37 | // }
38 | // private string GetCertificateName(Cert certificate)
39 | // {
40 | // string nameCertificate = "";
41 | // string[] subjects = certificate.SubjectDN.Trim().Split(',');
42 | // for (int i = 0; i < subjects.Length; i++)
43 | // {
44 | // string[] strConceptoTemp = subjects[i].Split('=');
45 | // if (strConceptoTemp[0].Trim() == "OID.2.5.4.41")
46 | // {
47 | // nameCertificate = strConceptoTemp[1].Trim().Split('/')[0];
48 | // //Bug Fix replace "
49 | // nameCertificate = nameCertificate.Replace("\"", "");
50 | // break;
51 | // }
52 | // }
53 | // return nameCertificate;
54 | // }
55 | // public string GetCertificateTaxId()
56 | // {
57 | // Cert certificate = LoadCertificate();
58 | // return GetCertificateTaxId(certificate);
59 | // }
60 | // private string GetCertificateTaxId(Cert certificate)
61 | // {
62 | // string taxIdCertificate = "";
63 | // string[] subjects = certificate.SubjectDN.Trim().Split(',');
64 | // for (int i = 0; i < subjects.Length; i++)
65 | // {
66 | // string[] strConceptoTemp = subjects[i].Split('=');
67 | // if (strConceptoTemp[0].Trim() == "OID.2.5.4.45")
68 | // {
69 | // taxIdCertificate = strConceptoTemp[1].Trim().Split('/')[0];
70 | // taxIdCertificate = taxIdCertificate.Replace("\"", "");
71 | // break;
72 | // }
73 | // }
74 | // return taxIdCertificate.Trim().ToUpper();
75 | // }
76 | // public string GetCertificateValidFrom()
77 | // {
78 | // Cert certificate = LoadCertificate();
79 | // return GetCertificateValidFrom(certificate);
80 | // }
81 | // private string GetCertificateValidFrom(Cert certificate)
82 | // {
83 | // return
84 | // certificate.ValidFromStr;
85 | // }
86 | // public string GetCertificateValidTo()
87 | // {
88 | // Cert certificate = LoadCertificate();
89 | // return GetCertificateValidTo(certificate);
90 | // }
91 | // public string GetCertificateValidTo(Cert certificate)
92 | // {
93 | // return
94 | // certificate.ValidToStr;
95 | // }
96 | // public bool IsCSD()
97 | // {
98 | // Cert certificate = LoadCertificate();
99 | // return IsCSD(certificate);
100 | // }
101 | // private bool IsCSD(Cert certificate)
102 | // {
103 | // try
104 | // {
105 | // var subjects = certificate.SubjectDN.Split(',');
106 | // return subjects.Any(w => w.Trim().StartsWith("OU="));
107 | // }
108 | // catch
109 | // {
110 | // return false;
111 | // }
112 | // }
113 | // public bool IsFiel()
114 | // {
115 | // Cert certificate = LoadCertificate();
116 | // return IsFiel(certificate);
117 | // }
118 | // private bool IsFiel(Cert certificate)
119 | // {
120 | // try
121 | // {
122 | // var subjects = certificate.SubjectDN.Split(',');
123 | // return subjects.Any(w => !w.Trim().StartsWith("OU="));
124 | // }
125 | // catch
126 | // {
127 | // return false;
128 | // }
129 | // }
130 | // public bool IsValidKeyPair()
131 | // {
132 | // var publicKey = this.certificateBytes;
133 | // var privateKey = this.keyBytes;
134 |
135 | // if (publicKey == null)
136 | // throw new ArgumentException("publicKey is empty");
137 | // if (privateKey == null)
138 | // throw new ArgumentException("privateKey is empty");
139 | // if (string.IsNullOrEmpty(this.password))
140 | // throw new ArgumentException("password is empty");
141 |
142 | // Cert cert = LoadCertificate();
143 | // CertChain certChain = cert.GetCertChain();
144 |
145 | // PrivateKey privKey = new PrivateKey();
146 | // var success = privKey.LoadPkcs8Encrypted(privateKey, this.password);
147 | // if (!success)
148 | // throw new Exception("Private Key is not valid." + privKey.LastErrorText);
149 | // var modulusPrivateKey = GetModulusFromPublicKey(privKey.GetPublicKey());
150 | // var modulusPublicKey = GetModulusFromPublicKey(cert.ExportPublicKey());
151 | // return (modulusPrivateKey.Equals(modulusPublicKey));
152 | // }
153 | // private Cert LoadCertificate()
154 | // {
155 | // Cert certificate = new Cert();
156 | // var successLoad = certificate.LoadFromBinary(this.certificateBytes);
157 | // if (!successLoad) throw new Exception($"Invalid Certificate.{certificate.LastErrorText}");
158 | // return certificate;
159 | // }
160 | // private string GetModulusFromPublicKey(PublicKey publicKey)
161 | // {
162 | // Xml xml = new Xml();
163 | // xml.LoadXml(publicKey.GetXml());
164 | // string modulus = xml.GetChildContent("Modulus");
165 | // // To convert to hex:
166 | // BinData binDat = new BinData();
167 | // binDat.Clear();
168 | // binDat.AppendEncoded(modulus, "base64");
169 | // return binDat.GetEncoded("hex");
170 | // }
171 | // }
172 | //}
173 |
--------------------------------------------------------------------------------
/Core/Certificate.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Security.Cryptography;
5 | using System.Security.Cryptography.X509Certificates;
6 | using System.Text;
7 | using Fiscalapi.Credentials.Common;
8 |
9 | namespace Fiscalapi.Credentials.Core
10 | {
11 | ///
12 | /// Represents a wrapper for FIEL and CSD certificate.
13 | ///
14 | public sealed class Certificate : ICertificate
15 | {
16 | private readonly X509Certificate2 _x509Certificate2;
17 |
18 |
19 | public Certificate(string plainBase64)
20 | {
21 | PlainBase64 = plainBase64;
22 | _x509Certificate2 = new X509Certificate2(CertificatePlainBytes);
23 | }
24 |
25 | ///
26 | /// The result of reading the bytes from the .cer file and converting them to base64
27 | ///
28 | public string PlainBase64 { get; }
29 |
30 |
31 | ///
32 | /// The equivalent of reading the bytes from the .cer file and converting them to base64
33 | ///
34 | public byte[] CertificatePlainBytes
35 | {
36 | get => Convert.FromBase64String(PlainBase64);
37 | }
38 |
39 |
40 | ///
41 | /// RFC as parsed from subject/x500UniqueIdentifier or OID.2.5.4.45
42 | /// This ensures cross-platform compatibility (Windows/Linux).
43 | ///
44 | public string Rfc
45 | {
46 | get
47 | {
48 | // Windows "OID.2.5.4.45"
49 | var rfcPair = SubjectKeyValuePairs.FirstOrDefault(x =>
50 | x.Key.Equals("OID.2.5.4.45", StringComparison.OrdinalIgnoreCase));
51 |
52 | // Linux "x500UniqueIdentifier"
53 | if (string.IsNullOrEmpty(rfcPair.Value))
54 | {
55 | rfcPair = SubjectKeyValuePairs.FirstOrDefault(x =>
56 | x.Key.Equals("x500UniqueIdentifier", StringComparison.OrdinalIgnoreCase));
57 | }
58 |
59 | // First 13 chars
60 | var value = rfcPair.Value;
61 |
62 | if (!string.IsNullOrEmpty(value) && value.Length >= 12)
63 | {
64 | return value[..Math.Min(value.Length, 13)].Trim();
65 | }
66 |
67 | throw new Exception("Fiscalapi.Credentials wasn't able to resolve ICertificate.Rfc ");
68 | }
69 | }
70 |
71 |
72 | ///
73 | /// Organization = 'razón social'
74 | ///
75 | public string Organization
76 | {
77 | // CN: CommonName
78 | // OU: OrganizationalUnit
79 | // O: Organization
80 | // L: Locality
81 | // S: StateOrProvinceName
82 | // C: CountryName
83 | get => ExistsKey(SubjectKeyValuePairs, "O")
84 | ? SubjectKeyValuePairs.FirstOrDefault(x => x.Key.Equals("O")).Value.Trim()
85 | : string.Empty;
86 | }
87 |
88 | ///
89 | /// OrganizationalUnit = 'Sucursal'
90 | /// As of 2019-08-01 is known that only CSD have OU (Organization Unit)
91 | ///
92 | public string OrganizationalUnit
93 | {
94 | // CN: CommonName
95 | // OU: OrganizationalUnit
96 | // O: Organization
97 | // L: Locality
98 | // S: StateOrProvinceName
99 | // C: CountryName
100 |
101 | get => ExistsKey(SubjectKeyValuePairs, "OU")
102 | ? SubjectKeyValuePairs.FirstOrDefault(x => x.Key.Equals("OU")).Value.Trim()
103 | : string.Empty;
104 | }
105 |
106 | private static bool ExistsKey(IEnumerable> keyValuePairs, string key)
107 | {
108 | return keyValuePairs.Any(pair => pair.Key.Equals(key));
109 | }
110 |
111 |
112 | ///
113 | /// All serial number
114 | ///
115 | public string SerialNumber
116 | {
117 | get => _x509Certificate2.SerialNumber;
118 | }
119 |
120 | ///
121 | /// Certificate number as Mexican tax authority (SAT) require.
122 | ///
123 | public string CertificateNumber
124 | {
125 | get => Encoding.ASCII.GetString(_x509Certificate2.GetSerialNumber().Reverse().ToArray());
126 | }
127 |
128 |
129 | ///
130 | /// Issuer data parsed into KeyValuePair collection
131 | ///
132 | public List> IssuerKeyValuePairs
133 | {
134 | get => _x509Certificate2.Issuer.Split(',')
135 | .Select(x => new KeyValuePair(x.Split('=')[0].Trim(), x.Split('=')[1].Trim())).ToList();
136 | }
137 |
138 | ///
139 | /// Raw X509Certificate2 Issuer property
140 | ///
141 | public string Issuer
142 | {
143 | get => _x509Certificate2.Issuer;
144 | }
145 |
146 |
147 | ///
148 | /// Subject data parsed into KeyValuePair collection
149 | /// see https://oidref.com/2.5.4.45
150 | ///
151 | public List> SubjectKeyValuePairs
152 | {
153 | get => _x509Certificate2.Subject.Split(',')
154 | .Select(x => new KeyValuePair(x.Split('=')[0].Trim(), x.Split('=')[1].Trim())).ToList();
155 | }
156 |
157 | ///
158 | /// Raw X509Certificate2 Subject property
159 | /// see https://oidref.com/2.5.4.45
160 | ///
161 | public string Subject
162 | {
163 | get => _x509Certificate2.Subject;
164 | }
165 |
166 |
167 | ///
168 | /// Certificate version
169 | ///
170 | public int Version
171 | {
172 | get => _x509Certificate2.Version;
173 | }
174 |
175 | ///
176 | /// Valid start date
177 | ///
178 | public DateTime ValidFrom
179 | {
180 | get => _x509Certificate2.NotBefore;
181 | }
182 |
183 | ///
184 | /// Valid end date
185 | ///
186 | public DateTime ValidTo
187 | {
188 | get => _x509Certificate2.NotAfter;
189 | }
190 |
191 | ///
192 | /// True if ValidTo date is less than the current date
193 | ///
194 | public bool IsValid()
195 | {
196 | return ValidTo > DateTime.Now;
197 | }
198 |
199 | ///
200 | /// True when is a FIEL certificate
201 | ///
202 | public bool IsFiel()
203 | {
204 | return string.IsNullOrEmpty(OrganizationalUnit);
205 | }
206 |
207 |
208 | ///
209 | /// Raw Data Length
210 | ///
211 | public int RawDataLength
212 | {
213 | get => _x509Certificate2.RawData.Length;
214 | }
215 |
216 | ///
217 | /// RawDataBytes
218 | ///
219 | public byte[] RawDataBytes
220 | {
221 | get => _x509Certificate2.RawData;
222 | }
223 |
224 | ///
225 | /// Convert X.509 DER base64 or X.509 DER to X.509 PEM
226 | ///
227 | ///
228 | public string GetPemRepresentation()
229 | {
230 | var certPem = new string(PemEncoding.Write(Flags.PemCertificate, _x509Certificate2.RawData));
231 |
232 | return certPem;
233 | }
234 | }
235 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Oo]ut/
33 | [Ll]og/
34 | [Ll]ogs/
35 |
36 | # Visual Studio 2015/2017 cache/options directory
37 | .vs/
38 | # Uncomment if you have tasks that create the project's static files in wwwroot
39 | #wwwroot/
40 |
41 | # Visual Studio 2017 auto generated files
42 | Generated\ Files/
43 |
44 | # MSTest test Results
45 | [Tt]est[Rr]esult*/
46 | [Bb]uild[Ll]og.*
47 |
48 | # NUnit
49 | *.VisualState.xml
50 | TestResult.xml
51 | nunit-*.xml
52 |
53 | # Build Results of an ATL Project
54 | [Dd]ebugPS/
55 | [Rr]eleasePS/
56 | dlldata.c
57 |
58 | # Benchmark Results
59 | BenchmarkDotNet.Artifacts/
60 |
61 | # .NET Core
62 | project.lock.json
63 | project.fragment.lock.json
64 | artifacts/
65 |
66 | # ASP.NET Scaffolding
67 | ScaffoldingReadMe.txt
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio LightSwitch build output
298 | **/*.HTMLClient/GeneratedArtifacts
299 | **/*.DesktopClient/GeneratedArtifacts
300 | **/*.DesktopClient/ModelManifest.xml
301 | **/*.Server/GeneratedArtifacts
302 | **/*.Server/ModelManifest.xml
303 | _Pvt_Extensions
304 |
305 | # Paket dependency manager
306 | .paket/paket.exe
307 | paket-files/
308 |
309 | # FAKE - F# Make
310 | .fake/
311 |
312 | # CodeRush personal settings
313 | .cr/personal
314 |
315 | # Python Tools for Visual Studio (PTVS)
316 | __pycache__/
317 | *.pyc
318 |
319 | # Cake - Uncomment if you are using it
320 | # tools/**
321 | # !tools/packages.config
322 |
323 | # Tabs Studio
324 | *.tss
325 |
326 | # Telerik's JustMock configuration file
327 | *.jmconfig
328 |
329 | # BizTalk build output
330 | *.btp.cs
331 | *.btm.cs
332 | *.odx.cs
333 | *.xsd.cs
334 |
335 | # OpenCover UI analysis results
336 | OpenCover/
337 |
338 | # Azure Stream Analytics local run output
339 | ASALocalRun/
340 |
341 | # MSBuild Binary and Structured Log
342 | *.binlog
343 |
344 | # NVidia Nsight GPU debugger configuration file
345 | *.nvuser
346 |
347 | # MFractors (Xamarin productivity tool) working folder
348 | .mfractor/
349 |
350 | # Local History for Visual Studio
351 | .localhistory/
352 |
353 | # BeatPulse healthcheck temp database
354 | healthchecksdb
355 |
356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
357 | MigrationBackup/
358 |
359 | # Ionide (cross platform F# VS Code tools) working folder
360 | .ionide/
361 |
362 | # Fody - auto-generated XML schema
363 | FodyWeavers.xsd
--------------------------------------------------------------------------------
/Core/Credential.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Security.Cryptography;
4 | using System.Security.Cryptography.X509Certificates;
5 | using System.Text;
6 | using System.Xml;
7 | using System.Xml.Xsl;
8 | using Fiscalapi.Credentials.Common;
9 |
10 | namespace Fiscalapi.Credentials.Core;
11 |
12 | ///
13 | /// Represents a wrapper for certificate and private key. Something like 'FIEL and CSD'
14 | ///
15 | public class Credential : ICredential
16 | {
17 | //certificate and private key into X509Certificate2 object
18 | private readonly X509Certificate2 certificateWithPrivateKey;
19 |
20 |
21 | public Credential(ICertificate certificate, IPrivateKey privateKey)
22 | {
23 | Certificate = certificate;
24 | PrivateKey = privateKey;
25 | PasswordPhrase = privateKey.PasswordPhrase;
26 |
27 | PemCertificate = certificate.GetPemRepresentation();
28 | PemPrivateKey = privateKey.GetPemRepresentation();
29 |
30 | certificateWithPrivateKey = X509Certificate2.CreateFromPem(PemCertificate, PemPrivateKey);
31 |
32 | // Default algorithm is SHA256 (for invoicing)
33 | SignatureAlgorithm = HashAlgorithmName.SHA256;
34 |
35 | // Default padding is Pkcs1 (used for both invoicing and XML downloader)
36 | SignaturePadding = RSASignaturePadding.Pkcs1;
37 | }
38 |
39 | ///
40 | /// Private key password
41 | ///
42 | public string PasswordPhrase { get; }
43 |
44 | ///
45 | /// Dotnetcfdi certificate wrapper
46 | ///
47 | public ICertificate Certificate { get; }
48 |
49 | ///
50 | /// Dotnetcfdi PrivateKey wrapper
51 | ///
52 | public IPrivateKey PrivateKey { get; }
53 |
54 |
55 | public string PemCertificate { get; }
56 |
57 | public string PemPrivateKey { get; }
58 |
59 | ///
60 | /// Signature algorithm used for signing data. Default is SHA256 (for invoicing).
61 | ///
62 | public HashAlgorithmName SignatureAlgorithm { get; set; }
63 |
64 | ///
65 | /// Signature padding used for signing data. Default is Pkcs1 (used for both invoicing and XML downloader).
66 | ///
67 | public RSASignaturePadding SignaturePadding { get; set; }
68 |
69 | public byte[] CreatePFX()
70 | {
71 | var pfxBytes = certificateWithPrivateKey.Export(X509ContentType.Pfx, PasswordPhrase);
72 |
73 | return pfxBytes;
74 | }
75 |
76 | private string GetCertificatePemRepresentation()
77 | {
78 | return Certificate.GetPemRepresentation();
79 | }
80 |
81 | private string GetPrivateKeyPemRepresentation()
82 | {
83 | return PrivateKey.GetPemRepresentation();
84 | }
85 |
86 | ///
87 | /// Sign some data
88 | ///
89 | /// string to be signed
90 | /// signed bytes
91 | public byte[] SignData(string toSign)
92 | {
93 | //Sing and get signed bytes array
94 | var signedBytes = PrivateKey.SignData(toSign, SignatureAlgorithm, SignaturePadding);
95 |
96 | return signedBytes;
97 | }
98 |
99 | ///
100 | /// Verify the signature of some data
101 | ///
102 | /// original data in bytes
103 | /// signed data in bytes
104 | /// True when the signature is valid, otherwise false
105 | public bool VerifyData(byte[] dataToVerify, byte[] signedData)
106 | {
107 | var isValid = PrivateKey.VerifyData(dataToVerify, signedData, SignatureAlgorithm, SignaturePadding);
108 |
109 | return isValid;
110 | }
111 |
112 |
113 | ///
114 | /// True if Certificate.ValidTo date is less than the current date
115 | ///
116 | public bool IsValid()
117 | {
118 | return Certificate.IsValid();
119 | }
120 |
121 | ///
122 | /// True when is a FIEL certificate
123 | ///
124 | public bool IsFiel()
125 | {
126 | return Certificate.IsFiel();
127 | }
128 |
129 | ///
130 | /// True when certificate.ValidTo date is less than the current date and is a FIEL certificate
131 | ///
132 | ///
133 | public bool IsValidFiel()
134 | {
135 | return IsFiel() && IsValid();
136 | }
137 |
138 | ///
139 | /// Fiel whe credential.certificate is a FIEL certificate otherwise csd
140 | ///
141 | public CredentialType CredentialType
142 | {
143 | get => Certificate.IsFiel() ? CredentialType.Fiel : CredentialType.Csd;
144 | }
145 |
146 | ///
147 | /// Convert the input string to a byte array and compute the hash.
148 | ///
149 | /// data to hashing
150 | /// encoded b64 hash
151 | public string CreateHash(string input)
152 | {
153 | if (string.IsNullOrEmpty(input))
154 | throw new ArgumentException("Input cannot be null or empty", nameof(input));
155 |
156 | var inputBytes = Encoding.UTF8.GetBytes(input);
157 |
158 | using var hashAlgorithm = SHA1.Create();
159 |
160 | var hashBytes = hashAlgorithm.ComputeHash(inputBytes);
161 | var encodedBytes = hashBytes.ToBase64String();
162 |
163 | return encodedBytes;
164 | }
165 |
166 | //public string CreateHash(string input)
167 | //{
168 | // var inputBytes = Encoding.UTF8.GetBytes(input);
169 |
170 | // var hashBytes = CredentialSettings.HashAlgorithm.ComputeHash(inputBytes);
171 |
172 | // var encodedBytes = hashBytes.ToBase64String();
173 |
174 | // return encodedBytes;
175 | //}
176 |
177 | ///
178 | /// Verify a hash against a string.
179 | ///
180 | /// data to hashing
181 | /// encoded b64 hash
182 | /// true when computed hash is same of input hash otherwise false
183 | public bool VerifyHash(string input, string hash)
184 | {
185 | // Hash the input.
186 | var hashOfInput = CreateHash(input);
187 |
188 | // Create a StringComparer an compare the hashes.
189 | var comparer = StringComparer.OrdinalIgnoreCase;
190 |
191 | return comparer.Compare(hashOfInput, hash) == 0;
192 | }
193 |
194 |
195 | ///
196 | /// Transform XML documents into Pipe character in accordance with the schemes established in Mexican legislation.
197 | ///
198 | /// Xml file as string
199 | /// cadena original
200 | public string GetOriginalStringByXmlString(string xmlAsString)
201 | {
202 | if (string.IsNullOrEmpty(CredentialSettings.OriginalStringPath))
203 | throw new ArgumentNullException(nameof(CredentialSettings.OriginalStringPath),
204 | "The path to cadenaoriginal.xslt file cannot be null or empty.");
205 |
206 |
207 | if (string.IsNullOrEmpty(xmlAsString))
208 | throw new ArgumentNullException(nameof(xmlAsString),
209 | "The xml to calculate the original string cannot be null or empty.");
210 |
211 | using var stringReader = new StringReader(xmlAsString);
212 | using var xmlReader = XmlReader.Create(stringReader);
213 | using var stringWriter = new Utf8StringWriter();
214 |
215 | var xsltSettings = new XsltSettings
216 | {
217 | EnableDocumentFunction = true,
218 | EnableScript = true
219 | };
220 | var resolver = new XmlUrlResolver();
221 | var transformer = new XslCompiledTransform();
222 |
223 | transformer.Load(CredentialSettings.OriginalStringPath, xsltSettings, resolver);
224 | transformer.Transform(xmlReader, null, stringWriter);
225 | return stringWriter.ToString();
226 | }
227 |
228 | ///
229 | /// Configure the Signature algorithm to do invoicing, using the donetcfdi/invoicing library.
230 | /// Sets HashAlgorithmName.SHA256 for signing invoices. Returns this for fluent interface.
231 | ///
232 | public ICredential ConfigureAlgorithmForInvoicing()
233 | {
234 | SignatureAlgorithm = HashAlgorithmName.SHA256;
235 | return this;
236 | }
237 |
238 | ///
239 | /// Configure the Signature algorithm to do xml-downloader, using the donetcfdi/xml-downloader library.
240 | /// Sets HashAlgorithmName.SHA1 for downloading xml. Returns this for fluent interface.
241 | ///
242 | public ICredential ConfigureAlgorithmForXmlDownloader()
243 | {
244 | SignatureAlgorithm = HashAlgorithmName.SHA1;
245 | return this;
246 | }
247 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Fiscalapi Credentials
2 | [](https://dotnet.microsoft.com/download/dotnet/6.0)
3 | [](https://dotnet.microsoft.com/download/dotnet/8.0)
4 | [](https://dotnet.microsoft.com/download/dotnet/9.0)
5 | [](https://www.nuget.org/packages/Fiscalapi.Credentials)
6 | [](https://github.com/FiscalAPI/fiscalapi-credentials-net/blob/main/LICENSE)
7 |
8 | Biblioteca para trabajar con archivos **CSD** y **FIEL** del SAT de manera sencilla en .NET. **`Credentials`** simplifica la firma (sellado), la verificación de firmas, el cálculo de hashes (por ejemplo, para servicios de descarga masiva de XML y metadatos), así como la obtención de información relevante de los certificados y llaves públicas del SAT.
9 |
10 | La firma digital es un proceso criptográfico que garantiza la autenticidad, integridad y no repudio de un documento o mensaje. En México, el SAT requiere que los contribuyentes utilicen un **Certificado de Sello Digital (CSD)** para firmar (sellar) las facturas, mientras que una **Firma Electrónica Avanzada (FIEL)** se utiliza para firmar documentos de cualquier otro tipo (contratos, acuerdos, cotizaciones, correos, etc) de manera legalmente válida.
11 |
12 | ## Tabla de Contenido
13 |
14 | 1. [Acerca de la Librería](#Características)
15 | 2. [Instalación](#Instalación)
16 | 3. [Uso Básico](#uso-básico)
17 | - [Certificado (`Certificate`)](#uso-del-certificado)
18 | - [Clave Privada (`PrivateKey`)](#uso-de-la-clave-privada)
19 | - [Credencial (`Credential`)](#uso-del-objeto-credential)
20 | 4. [Acerca de los Archivos CSD y FIEL](#acerca-de-los-archivos-de-certificado-y-llave-privada)
21 | 5. [Compatibilidad](#compatibilidad)
22 | 6. [Roadmap](#roadmap)
23 | 7. [Contribuciones](#contribuciones)
24 | 8. [🤝 Contribuir](#-contribuir)
25 | 9. [🐛 Reportar Problemas](#-reportar-problemas)
26 | 10. [📄 Licencia](#-licencia)
27 | 11. [🔗 Enlaces Útiles](#-enlaces-útiles)
28 |
29 |
30 | ## 🚀 Características
31 |
32 | - **Firmar (sellar) documentos**: Utilizar CSD o FIEL para generar firmas digitales que cumplen con los lineamientos del SAT.
33 | - **Verificar firmas**: Validar que la firma fue generada correctamente con la llave privada asociada.
34 | - **Calcular hashes**: Útil para servicios de descarga masiva de XML del SAT, comparaciones de integridad, etc.
35 | - **Obtener datos del certificado**: Número de serie, fecha de vigencia, RFC, razón social, entre otros.
36 | - **Generar archivos PFX (PKCS#12)** a partir de los archivos proporcionados por el SAT sin necesidad de `openssl`.
37 |
38 | ### Clases Principales
39 |
40 | 1. **`Certificate`**
41 | - Maneja todo lo relacionado al `.cer` (X.509 DER).
42 | - Obtiene número de certificado, versión, periodo de vigencia, etc.
43 | - Convierte de X.509 DER a X.509 PEM.
44 |
45 | 2. **`PrivateKey`**
46 | - Maneja todo lo relacionado al `.key` (PKCS#8 DER).
47 | - Convierte la clave de PKCS#8 DER a PKCS#8 PEM.
48 | - Requiere la contraseña de la llave privada para operar.
49 |
50 | 3. **`Credential`**
51 | - Une `Certificate` y `PrivateKey`.
52 | - Permite firmar, validar firmas, crear archivos PFX, etc.
53 | - Identifica si es CSD o FIEL y verifica su vigencia.
54 |
55 | ## 📦Instalación
56 |
57 | **NuGet Package Manager**:
58 |
59 | ```bash
60 | NuGet\Install-Package Fiscalapi.Credentials
61 | ```
62 |
63 | **.NET CLI**:
64 |
65 | ```bash
66 | dotnet add package Fiscalapi.Credentials
67 | ```
68 |
69 | ## Ejemplos de uso
70 |
71 | ### Uso del Certificado
72 |
73 | ```csharp
74 | // Cargar el archivo .cer
75 | var cerPath = @"C:\Users\Usuario\Desktop\cer.cer";
76 | var cerBytes = File.ReadAllBytes(cerPath);
77 | var cerBase64 = Convert.ToBase64String(cerBytes);
78 |
79 | // Crear instancia de Certificate
80 | var certificate = new Certificate(cerBase64);
81 | // (Por ejemplo, cerBase64 puede guardarse en BD y luego recuperarse)
82 |
83 | // Mostrar información básica del certificado
84 | Console.WriteLine($"PlainBase64: {certificate.PlainBase64}");
85 | Console.WriteLine($"RFC: {certificate.Rfc}");
86 | Console.WriteLine($"Razón Social: {certificate.Organization}");
87 | Console.WriteLine($"Serial Number: {certificate.SerialNumber}");
88 | Console.WriteLine($"Certificate Number: {certificate.CertificateNumber}");
89 | Console.WriteLine($"Válido desde: {certificate.ValidFrom}");
90 | Console.WriteLine($"Válido hasta: {certificate.ValidTo}");
91 | Console.WriteLine($"¿Es FIEL?: {certificate.IsFiel()}");
92 | Console.WriteLine($"¿Está vigente?: {certificate.IsValid()}"); // ValidTo > DateTime.Now
93 |
94 | // Convertir X.509 DER base64 a X.509 PEM
95 | var pemCertificate = certificate.GetPemRepresentation();
96 | File.WriteAllText("MyPemCertificate.pem", pemCertificate);
97 | ```
98 |
99 | ### Uso de la Clave Privada
100 |
101 | ```csharp
102 | // Cargar el archivo .key
103 | var keyPath = @"C:\Users\Usuario\Desktop\key.key";
104 | var keyBytes = File.ReadAllBytes(keyPath);
105 | var keyBase64 = Convert.ToBase64String(keyBytes);
106 |
107 | // Crear instancia de PrivateKey con la contraseña
108 | var privateKey = new PrivateKey(keyBase64, "TuPasswordDeLaLlave");
109 |
110 | // Convertir PKCS#8 DER a PKCS#8 PEM
111 | var PemPrivateKey = privateKey.GetPemRepresentation();
112 | File.WriteAllText("MyPemPrivateKey.pem", PemPrivateKey);
113 | ```
114 |
115 | ### Uso del Objeto Credential
116 |
117 | ```csharp
118 | // Crear instancia de Credential a partir de certificate y privateKey
119 | var cred = new Credential(certificate, privateKey);
120 |
121 | var dataToSign = "Hola Mundo"; // Reemplazar con cadena original u otro contenido
122 |
123 | // Firmar datos
124 | var signedBytes = cred.SignData(dataToSign);
125 |
126 | // Verificar firma
127 | var originalDataBytes = Encoding.UTF8.GetBytes(dataToSign);
128 | var isValidSignature = cred.VerifyData(originalDataBytes, signedBytes);
129 | Console.WriteLine($"¿Firma Válida?: {isValidSignature}");
130 |
131 | // Crear archivo PFX (PKCS#12)
132 | var pfxBytes = cred.CreatePFX();
133 | File.WriteAllBytes("MyPFX.pfx", pfxBytes);
134 |
135 | // Calcular y verificar hash (por ejemplo, para descarga masiva XML)
136 | var dataToHash = "XML canonical representation";
137 | var hashBase64 = cred.CreateHash(dataToHash);
138 | var isHashValid = cred.VerifyHash(dataToHash, hashBase64);
139 | Console.WriteLine($"¿Hash Válido?: {isHashValid}");
140 |
141 | // Información adicional
142 | Console.WriteLine($"Tipo de Credencial: {cred.CredentialType}"); // Enum: Fiel || Csd
143 | Console.WriteLine($"¿Es FIEL válida?: {cred.IsValidFiel()}");
144 | ```
145 |
146 |
147 | ## Acerca de los Archivos de Certificado y Llave Privada
148 |
149 | Los certificados provistos por el SAT suelen estar en formato **X.509 DER** (`.cer`), mientras que las llaves privadas están en **PKCS#8 DER** (`.key`). Estos formatos **no** se pueden usar directamente en la mayoría de las bibliotecas de C#, pero **`Credentials`** resuelve este problema convirtiéndolos internamente a **PEM** (`.pem`) sin requerir `openssl`.
150 |
151 | Esta conversión consiste básicamente en:
152 |
153 | 1. Codificar en **Base64** el contenido DER.
154 | 2. Separar en líneas de 64 caracteres.
155 | 3. Agregar las cabeceras y pies específicos para certificados y llaves privadas.
156 |
157 | Por lo tanto, no necesitas realizar la conversión manual ni depender de utilerías externas para utilizar tus archivos **CSD** o **FIEL**.
158 |
159 |
160 | ## Compatibilidad
161 |
162 | - Compatible con **.NET 6**, **.NET 8** y **.NET 9** WinForms, WPF, Console, ASP.NET, Blazor, MVC, WebApi.
163 | - Mantenemos la compatibilidad con al menos la versión LTS más reciente de .NET.
164 | - Se sigue el [**Versionado Semántico 2.0.0**]([docs/SEMVER.md](https://learn.microsoft.com/en-us/nuget/concepts/package-versioning?tabs=semver20sort)), por lo que puedes confiar en que las versiones nuevas no romperán tu aplicación de forma inesperada.
165 | ## Roadmap
166 |
167 | - [x] Conversión de **X.509 DER** a **X.509 PEM** (SAT .cer).
168 | - [x] Conversión de **PKCS#8 DER** a **PKCS#8 PEM** (SAT .key).
169 | - [x] Creación de archivo .PFX (PKCS#12) a partir de los archivos X.509 PEM y PKCS#8 PEM.
170 | - [x] Firma de datos con `SHA256withRSA`.
171 | - [x] Verificación de datos firmados.
172 | - [x] Cálculo y verificación de hash para servicios SAT de descarga masiva de XML.
173 | - [ ] Persistencia de CSD y FIEL utilizando Entity Framework Core y bases de datos relacionales.
174 |
175 |
176 | ## 🤝 Contribuir
177 |
178 | 1. Haz un fork del repositorio.
179 | 2. Crea una rama para tu feature: `git checkout -b feature/AmazingFeature`.
180 | 3. Realiza commits de tus cambios: `git commit -m 'Add some AmazingFeature'`.
181 | 4. Sube tu rama: `git push origin feature/AmazingFeature`.
182 | 5. Abre un Pull Request en GitHub.
183 |
184 |
185 | ## 🐛 Reportar Problemas
186 |
187 | 1. Asegúrate de usar la última versión del SDK.
188 | 2. Verifica si el problema ya fue reportado.
189 | 3. Proporciona un ejemplo mínimo reproducible.
190 | 4. Incluye los mensajes de error completos.
191 |
192 |
193 | ## 📄 Licencia
194 |
195 | Este proyecto está licenciado bajo la Licencia **MPL**. Consulta el archivo [LICENSE](LICENSE.txt) para más detalles.
196 |
197 |
198 | ## 🔗 Enlaces Útiles
199 |
200 | - [Documentación Oficial](https://docs.fiscalapi.com)
201 | - [Portal de FiscalAPI](https://fiscalapi.com)
202 | - [Facturar en WinForms/Console](https://github.com/FiscalAPI/fiscalapi-samples-net-winforms)
203 | - [Facturar en ASP.NET](https://github.com/FiscalAPI/fiscalapi-samples-net-aspnet)
204 |
205 | ---
206 |
207 | Desarrollado con ❤️ por [Fiscalapi](https://www.fiscalapi.com)
208 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
374 |
--------------------------------------------------------------------------------