Ecv2DotNet 2.0.0

dotnet add package Ecv2DotNet --version 2.0.0
                    
NuGet\Install-Package Ecv2DotNet -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Ecv2DotNet" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Ecv2DotNet" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Ecv2DotNet" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Ecv2DotNet --version 2.0.0
                    
#r "nuget: Ecv2DotNet, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Ecv2DotNet@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Ecv2DotNet&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Ecv2DotNet&version=2.0.0
                    
Install as a Cake Tool

Ecv2DotNet

Ecv2DotNet is a .NET library that provides a simple way to verify the integrity of payloads that are signed using the Google ECv2SigningOnly protocol. This library is used for validating signatures from Google Pay and Google Wallet callback APIs.

Features

  • ECv2SigningOnly Protocol Support: Validates signatures using Google's ECv2SigningOnly protocol
  • Dependency Injection Ready: Easy integration with .NET's built-in DI container
  • Configurable: Support for custom issuer IDs and public key URLs
  • Async/Await Support: Fully asynchronous validation methods
  • Comprehensive Logging: Detailed logging for debugging and monitoring

Installation

dotnet add package Ecv2DotNet

Quick Start

1. Dependency Injection Setup

using Ecv2DotNet;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateDefaultBuilder(args);

builder.ConfigureServices(services =>
{
    // Basic setup with your issuer ID
    services.AddEcv2Validation("1296031581681466393");
    
    // Or with custom public key URL
    services.AddEcv2Validation(
        issuerId: "1296031581681466393",
        publicKeyUrl: "https://custom.google.keys.url"
    );
    
    // Or with additional configuration
    services.AddEcv2Validation("1296031581681466393", options =>
    {
        options.PublicKeyUrl = "https://pay.google.com/gp/m/issuer/keys";
    });
});

var host = builder.Build();

2. ASP.NET Core Integration

// Program.cs
var builder = WebApplication.CreateBuilder(args);

// Add ECv2 validation services
builder.Services.AddEcv2Validation(
    builder.Configuration["GooglePay:IssuerId"]!
);

var app = builder.Build();

3. Using the Validator

using Ecv2DotNet;

[ApiController]
[Route("api/[controller]")]
public class GooglePayController : ControllerBase
{
    private readonly IEcv2Validator _validator;

    public GooglePayController(IEcv2Validator validator)
    {
        _validator = validator;
    }

    [HttpPost("callback")]
    public async Task<IActionResult> HandleCallback([FromBody] string signatureJson)
    {
        try
        {
            var isValid = await _validator.ValidateSignatureAsync(signatureJson);
            
            if (isValid)
            {
                // Process the valid callback
                return Ok(new { status = "success" });
            }
            else
            {
                return BadRequest(new { error = "Invalid signature" });
            }
        }
        catch (Exception ex)
        {
            return StatusCode(500, new { error = ex.Message });
        }
    }
}

4. Manual Instantiation (without DI)

using Ecv2DotNet;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

// Create configuration
var options = new Ecv2Options
{
    IssuerId = "1296031581681466393",
    PublicKeyUrl = "https://pay.google.com/gp/m/issuer/keys"
};

// Create HTTP client
using var httpClient = new HttpClient();

// Create logger (optional)
using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
var logger = loggerFactory.CreateLogger<Ecv2Validator>();

// Create the validator
var validator = new Ecv2Validator(httpClient, Options.Create(options), logger);

// Validate signature
string signatureJson = """
{
  "signature": "XEUCIQCJi26vl+ak17dsHDbZZnRZxm51duUAPiYLwOIr9rVvAAIgGUfR18gpKTq1+Msav0vPrWvC6x9dDRwWFX/b85+jE1k=",
  "intermediateSigningKey": {
    "signedKey": "{\"keyValue\":\"XFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVsEtOdPMaE+DJDzuCJaO7EJXaHor4Kyklp411iwfBa+5TmdbiEWUXzewA79H0PXjdyRMhKBY99+sh056JB75LQ==\",\"keyExpiration\":\"1754778096000\"}",
    "signatures": [
      "MEUCIC29Ju3bt9kklbbA9QAJZW0hh2zecbHDzGo4hF1zRi1zAiEA6e201l1TEl85Row6XHybfDoewIKC4vYpnrlmUT9WbrE="
    ]
  },
  "protocolVersion": "ECv2SigningOnly",
  "signedMessage": "{\"classId\":\"1296031581681466393.LOYALTY_CLASS_dada6069-0799-44ec-a38d-c482484902e1\",\"objectId\":\"1296031581681466393.LOYALTY_OBJECT_xxxxxxxxxxxxx\",\"eventType\":\"save\",\"expTimeMillis\":1754114831806,\"count\":1,\"nonce\":\"40a8e5af-5b7f-4ea4-b152-63d96858550e\"}"
}
""";

var isValid = await validator.ValidateSignatureAsync(signatureJson);
Console.WriteLine($"Signature is valid: {isValid}");

Configuration

If you include an ILoggerFactory in your DI setup, the library will log detailed information about the validation process. This can help you debug issues with signature validation.

Example:

fail: Ecv2DotNet.Ecv2Validator[0]
      Expiry date is in the past: 08/02/2025 06:07:11 +00:00
fail: Ecv2DotNet.Ecv2Validator[0]
      Signed message has expired: 1754114831806
Signature valid: False

Ecv2Options

public class Ecv2Options
{
    /// <summary>
    /// The issuer ID that should match the one in the signed message
    /// Required: Must be set to your Google Pay issuer ID
    /// </summary>
    public string IssuerId { get; set; } = string.Empty;

    /// <summary>
    /// URL to fetch Google's public keys
    /// Default: "https://pay.google.com/gp/m/issuer/keys"
    /// </summary>
    public string PublicKeyUrl { get; set; } = "https://pay.google.com/gp/m/issuer/keys";

    /// <summary>
    /// Sender ID for Google Pay (constant)
    /// Value: "GooglePayPasses"
    /// </summary>
    public string SenderId { get; } = "GooglePayPasses";

    /// <summary>
    /// Protocol version (constant)
    /// Value: "ECv2SigningOnly"
    /// </summary>
    public string Protocol { get; } = "ECv2SigningOnly";
}

appsettings.json Configuration

{
  "GooglePay": {
    "IssuerId": "1296031581681466393",
    "PublicKeyUrl": "https://pay.google.com/gp/m/issuer/keys"
  }
}

API Reference

IEcv2Validator Interface

public interface IEcv2Validator
{
    /// <summary>
    /// Validates an ECv2 signature from raw JSON string
    /// </summary>
    Task<bool> ValidateSignatureAsync(string signatureJson);

    /// <summary>
    /// Validates an ECv2 signature from a parsed payload
    /// </summary>
    Task<bool> ValidateSignatureAsync(SignaturePayload payload);
}

Extension Methods

// Basic setup
services.AddEcv2Validation(string issuerId);

// With custom public key URL
services.AddEcv2Validation(string issuerId, string publicKeyUrl);

// With configuration callback
services.AddEcv2Validation(string issuerId, Action<Ecv2Options> configureOptions);

Validation Process

The library performs the following validation steps:

  1. Protocol Version Check: Ensures the protocol version is "ECv2SigningOnly"
  2. Recipient ID Validation: Verifies the issuer ID matches in classId or objectId
  3. Expiration Check: Validates that both the signed message and intermediate key haven't expired
  4. Intermediate Signature Verification: Verifies the intermediate signing key against Google's public keys
  5. Message Signature Verification: Verifies the message signature using the intermediate key

Error Handling

The validator returns false for invalid signatures and logs detailed error messages. Common validation failures include:

  • Invalid protocol version
  • Expired signatures or keys
  • Mismatched issuer IDs
  • Invalid cryptographic signatures
  • Network failures when fetching public keys

Dependencies

  • .NET 8.0: Target framework
  • BouncyCastle.Cryptography: For cryptographic operations
  • Microsoft.Extensions.DependencyInjection: For DI support
  • Microsoft.Extensions.Http: For HTTP client factory
  • Microsoft.Extensions.Logging: For logging support
  • System.Text.Json: For JSON serialization

License

MIT License - see LICENSE file for details.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

Support

For issues and feature requests, please use the GitHub issue tracker.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 15 8/2/2025
1.0.0 13 8/2/2025