Wop.Sdk
0.1.7
dotnet add package Wop.Sdk --version 0.1.7
NuGet\Install-Package Wop.Sdk -Version 0.1.7
<PackageReference Include="Wop.Sdk" Version="0.1.7" />
<PackageVersion Include="Wop.Sdk" Version="0.1.7" />
<PackageReference Include="Wop.Sdk" />
paket add Wop.Sdk --version 0.1.7
#r "nuget: Wop.Sdk, 0.1.7"
#:package Wop.Sdk@0.1.7
#addin nuget:?package=Wop.Sdk&version=0.1.7
#tool nuget:?package=Wop.Sdk&version=0.1.7
wop-dotnet-sdk
Official merchant-side .NET SDK for the WOP gateway: encapsulates the protocol core (suite parsing, canonicalRequest, structured signing, content-digest, L2 digital envelope, signature verification & decryption) plus an HttpClient adapter, so merchants integrate without touching wire-level byte formats.
- Targets:
net8.0+netstandard2.0(multi-target) - Crypto dependency (the single blessed path, E5): BouncyCastle.Cryptography (successor of Portable.BouncyCastle)
- Suites (F1):
WOP-RSA3072-SHA256/WOP-RSA4096-SHA256/WOP-SM2-SM3(both international and GM suites fully supported) - Protocol source of truth:
gtsp-wop-gateway/docs/crypto-strategy-spec.md(v0.3-reviewed) andwop-sdk-spec.md(v1.0-ratified)
Quick Start
dotnet add package Wop.Sdk # 0.1.0 (or reference src/Wop.Sdk from source)
using Wop.Sdk;
var client = WopClient.Builder()
.AppKey("your-app-key")
.Suite("WOP-RSA3072-SHA256") // or WOP-SM2-SM3
.MerchantPrivateKey(merchantPrivateKey) // PEM or single-line Base64 (D12)
.PlatformPublicKey(platformPublicKey)
.Build();
// 1) Build the request draft (pure computation, zero network IO; idempotent
// apart from CSPRNG values)
RequestDraft draft = client.BuildRequest(
"POST", "/api/v1/pay",
Encoding.UTF8.GetBytes("{\"orderNo\":\"20260829001\",\"amount\":100}"),
SecurityLevel.L0); // L0 plaintext / L2 full envelope
// 2) Consume draft.Headers / draft.WireBody with your own HTTP stack,
// or use the SDK adapter (DelegatingHandler pluggable):
var transport = new HttpClientTransport(
new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(5) },
"https://wop-gateway.example.com");
var (result, response) = client.Execute(transport, "POST", "/api/v1/pay", body, SecurityLevel.L0);
if (result.Ok)
{
var plaintext = Encoding.UTF8.GetString(result.Plaintext!);
}
Key Preparation (D12 contract)
Keys are passed as strings (PEM or single-line Base64) and parsed inside the SDK; parse failures return explicit configuration errors (for integration self-checks).
| Suite | Merchant private key | Platform public key |
|---|---|---|
WOP-RSA3072-SHA256 |
PKCS#8 DER (Base64/PEM), 3072-bit | X.509 SubjectPublicKeyInfo DER (Base64/PEM) |
WOP-RSA4096-SHA256 |
PKCS#8 DER, 4096-bit | SPKI DER |
WOP-SM2-SM3 |
d = 32-byte big-endian scalar (Base64, within [1, n-1]) | Uncompressed point 04‖X‖Y, 65 bytes (Base64, on-curve validated upfront) |
Notes:
- RSA key size is strictly checked against the suite declaration (3072/4096 mismatch is rejected).
- SM2 public points not on the sm2p256v1 curve are rejected immediately (I5 curve guard).
- .NET's
BitConverter.ToStringproduces uppercase hyphenated output by default — this SDK emits lowercase hex everywhere (D10).
L0 + L2 Examples
// L0 plaintext: signature + digest integrity (digest is the only integrity line for L0, D2)
var l0 = client.BuildRequest("GET", "/api/v1/orders?status=PAID", null, SecurityLevel.L0);
// GET / empty body: x-wop-content-digest header is absent (no "digest of empty string" state)
// L2 full digital envelope: CSPRNG CEK + IV (I4: single IV generation point, never reused
// under the same key) → AES-256-GCM / SM4-GCM full-body encryption (cipher = ciphertext‖tag)
// → DEK payload alg$key$iv wrapped by RSA-OAEP (explicit dual SHA-256 + empty label) / SM2(C1C3C2)
// → wire body {"encrypted":"<base64url>"}, directive header x-wop-encrypt: L2;dek=<base64url>
var l2 = client.BuildRequest("POST", "/api/v1/transfer", secretBody, SecurityLevel.L2);
// Verify gateway responses (F6 fixed order: verify signature → digest recheck → DEK unwrap
// → alg family compare → bulk decrypt)
VerifyResult r = client.VerifyResponse("POST", "/api/v1/transfer", responseHeaders, responseBody);
// Verify platform callbacks (canonical URI = callback URL path, method is always POST)
VerifyResult c = client.VerifyCallback(callbackUrl, headersFromBody, rawBody);
Error handling (I7 fuzzing discipline):
- Explicit (programmable self-checks): config
CONFIG, suite parsingSUITE_PARSE/SUITE_UNSUPPORTED, protocol formatPROTOCOL, digest mismatchDIGEST_MISMATCH, cross-family dek algALG_MISMATCH - Fuzzy (oracle-proof, fixed messages with no cause details): verification
VERIFY_FAILED("签名验证失败" / signature verification failed), decryptionDECRYPT_FAILED("解密失败" / decryption failed) — GCM tag failure / wrong key / DEK unwrap failure share one message
Vector Self-Test (conformance)
Tests consume the same golden-vector copy as gateway CI
(tests/Wop.Sdk.Tests/fixtures/crypto-vectors.json, do not modify):
export PATH="$HOME/.dotnet:$PATH"
dotnet test /p:CollectCoverage=true /p:Threshold=98 '/p:ThresholdType="line,branch"'
Coverage:
- Positive vectors byte-exact: digest, AES-256-GCM / SM4-GCM (fixed key/iv), RSA3072/4096 signatures, SM2 sign & encrypt (fixed-k outputs match vectors byte-for-byte), OAEP unwrap, SM2 decrypt
- Negative vectors all rejected: tampering, 63/65-byte signatures, base64url with
=, cross-family digest/dek, C1C2C3-ordered ciphertext, MGF1-SHA1 trap (OAEP dual SHA-256 pin), DER signatures, off-curve public points - Coverage gate: line + branch ≥ 98%
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- BouncyCastle.Cryptography (>= 2.7.0)
- System.Memory (>= 4.6.3)
-
net8.0
- BouncyCastle.Cryptography (>= 2.7.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.