## Summary
`SignedContentBlob.GetRawSignature()` converts a DER-encoded ECDSA/DSA signature
(`SEQUENCE { r INTEGER, s INTEGER }`) to a raw `r || s` concatenation by stripping the
leading zero (DER sign byte) from each component — but it does **not left-pad the
components back to the fixed curve field size** required by the IEEE P1363 format that
.NET `ECDsa.VerifyHash()` expects.
As a result, whenever the minimal DER encoding of `r` or `s` is shorter than the field
size, `CryptSigner.VerifyData(SignedContentBlob, PublicKey)` receives a signature of the
wrong length and returns `false` for a **cryptographically valid** signature.
Affected code (identical in v4.4.0 and current master):
`src/SysadminsLV.PKI/Cryptography/SignedContentBlob.cs`, `GetRawSignature()`:
var asn = new Asn1Reader(Signature.Value);
asn.MoveNext();
var r = asn.GetPayload().ToList();
if (r[0] == 0) { r.RemoveAt(0); }
asn.MoveNext();
var s = asn.GetPayload().ToList();
if (s[0] == 0) { s.RemoveAt(0); }
var signature = new List<Byte>(r);
signature.AddRange(s);
return signature.ToArray();
## Impact
- For **P-521** (`sha512ECDSA`) the order is ≈ 2^521, so each component is shorter than
66 bytes with probability ≈ 1/2 → roughly **75 % of all P-521 signatures fail to verify**.
- P-256/P-384 are affected too (probability ≈ 1/256 per component).
- User-visible symptom: **PSPKI 4.4.1 `Get-EnterprisePKIHealthStatus` reports
`Status: InvalidIssuer`** for a perfectly valid ECDSA-signed CRL
(`__verifyCDP` → `X509CRL2.VerifySignature()` → this code path), while
`certutil -urlfetch -verify` validates the same chain and CRL successfully.
## Reproduction (real-world data)
Environment: Windows Server (26100), Windows PowerShell 5.1, PSPKI 4.4.1
(SysadminsLV.PKI 4.4.0). Offline ECDSA P-521 root CA, CRL signed with `sha512ECDSA`.
The failing CRL's signature components:
- `r`: DER payload 66 bytes, starts `01 3e 9d …` (full field length — OK)
- `s`: DER payload 66 bytes, starts `00 ee 65 …` (leading zero is the DER sign byte;
actual value is 65 bytes)
`GetRawSignature()` strips the sign byte from `s` and returns a **131-byte** blob.
`ECDsa.VerifyHash()` requires exactly 2 × 66 = **132 bytes** for P-521 → returns `false`.
Independent verification with plain .NET against the same CRL and issuer public key:
- correct IEEE P1363 conversion (each component left-padded to 66 bytes, 132 bytes
total): `ECDsa.VerifyHash()` → **True**
- `GetRawSignature()`-style conversion (131 bytes): `ECDsa.VerifyHash()` → **False**
`Get-EnterprisePKIHealthStatus` consequently reports:
Name : TestRoot2
ChainStatus : NoError
CDP http://…/TestRoot2.crl Status: InvalidIssuer
while `certutil -urlfetch -verify` on the same chain completes successfully
("Leaf certificate revocation check passed").
## Suggested fix
`GetRawSignature()` needs to know the key/field size (or receive it from the caller,
e.g. `CryptSigner`, which has the public key and thus `KeySize`) and left-pad each
component:
int fieldSize = (keySizeBits + 7) / 8;
byte[] p1363 = new byte[2 * fieldSize];
// rTrimmed/sTrimmed = payload with DER sign byte stripped
Buffer.BlockCopy(rTrimmed, 0, p1363, fieldSize - rTrimmed.Length, rTrimmed.Length);
Buffer.BlockCopy(sTrimmed, 0, p1363, 2 * fieldSize - sTrimmed.Length, sTrimmed.Length);
return p1363;
The DSA branch has the same problem (fixed 20-byte components for SHA-1 DSA).
Side note: the `strict` parameter of `X509CRL2.VerifySignature(issuer, strict)` is
documented as "not implemented" — PSPKI's `Get-EnterprisePKIHealthStatus` passes `$true`
expecting issuer/subject name comparison.