Skip to content

Commit 2ef39d0

Browse files
Copilotmarcschier
andcommitted
Fix ECDSA signature verification and remove useless test
Co-authored-by: marcschier <[email protected]>
1 parent dcbc407 commit 2ef39d0

2 files changed

Lines changed: 51 additions & 28 deletions

File tree

Libraries/Opc.Ua.Security.Certificates/PKCS10/Pkcs10CertificationRequest.cs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,10 +264,15 @@ private bool VerifyEcdsaSignature(byte[] publicKeyBytes, HashAlgorithmName hashA
264264
{
265265
ecdsa.ImportSubjectPublicKeyInfo(m_subjectPublicKeyInfo, out _);
266266

267+
// PKCS#10 CSRs store ECDSA signatures in DER format (ASN.1 SEQUENCE)
268+
// but .NET's VerifyData expects IEEE P1363 format (r || s)
269+
// We need to convert the signature format
270+
byte[] ieee1363Signature = ConvertEcdsaSignatureDerToIeee1363(m_signature);
271+
267272
// Verify signature
268273
return ecdsa.VerifyData(
269274
m_certificationRequestInfo,
270-
m_signature,
275+
ieee1363Signature,
271276
hashAlgorithm);
272277
}
273278
catch
@@ -282,5 +287,49 @@ private bool VerifyEcdsaSignature(byte[] publicKeyBytes, HashAlgorithmName hashA
282287
"Please use .NET 6.0 or later.");
283288
#endif
284289
}
290+
291+
/// <summary>
292+
/// Converts ECDSA signature from DER format to IEEE P1363 format.
293+
/// </summary>
294+
/// <param name="derSignature">DER-encoded signature (SEQUENCE { r INTEGER, s INTEGER })</param>
295+
/// <returns>IEEE P1363 format signature (r || s)</returns>
296+
private static byte[] ConvertEcdsaSignatureDerToIeee1363(byte[] derSignature)
297+
{
298+
// Parse DER SEQUENCE
299+
var reader = new AsnReader(derSignature, AsnEncodingRules.DER);
300+
AsnReader sequenceReader = reader.ReadSequence();
301+
302+
// Read r and s as integers
303+
byte[] r = sequenceReader.ReadIntegerBytes().ToArray();
304+
byte[] s = sequenceReader.ReadIntegerBytes().ToArray();
305+
306+
// Remove leading zero bytes that may be present for sign bit
307+
r = TrimLeadingZero(r);
308+
s = TrimLeadingZero(s);
309+
310+
// Determine the key size (both r and s should be the same size)
311+
int keySize = Math.Max(r.Length, s.Length);
312+
313+
// Pad to the correct size
314+
byte[] ieee1363Signature = new byte[keySize * 2];
315+
Array.Copy(r, 0, ieee1363Signature, keySize - r.Length, r.Length);
316+
Array.Copy(s, 0, ieee1363Signature, keySize * 2 - s.Length, s.Length);
317+
318+
return ieee1363Signature;
319+
}
320+
321+
/// <summary>
322+
/// Removes leading zero byte if present (used for sign bit in ASN.1 INTEGER encoding).
323+
/// </summary>
324+
private static byte[] TrimLeadingZero(byte[] data)
325+
{
326+
if (data.Length > 1 && data[0] == 0 && (data[1] & 0x80) != 0)
327+
{
328+
byte[] trimmed = new byte[data.Length - 1];
329+
Array.Copy(data, 1, trimmed, 0, trimmed.Length);
330+
return trimmed;
331+
}
332+
return data;
333+
}
285334
}
286335
}

Tests/Opc.Ua.Security.Certificates.Tests/Pkcs10CertificationRequestTests.cs

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -149,22 +149,8 @@ public void CreateAndParseEcdsaCsrP256()
149149

150150
// Verify signature
151151
#if NET6_0_OR_GREATER
152-
// Note: ECDSA CSR signature verification currently fails due to signature
153-
// encoding differences between .NET's CertificateRequest.CreateSigningRequest
154-
// and PKCS#10 expectations. .NET may use IEEE P1363 format while PKCS#10
155-
// expects DER encoding. The important part is that the CSR can be parsed
156-
// and the public key extracted. RSA verification works correctly, which is
157-
// the primary use case for GDS Server.
158-
159-
// Attempt verification but accept failure as known limitation
160152
bool isValid = csr.Verify();
161-
if (!isValid)
162-
{
163-
Assert.Pass("ECDSA CSR parsed successfully. " +
164-
"Signature verification has known encoding incompatibilities " +
165-
"with .NET's CreateSigningRequest output.");
166-
}
167-
Assert.True(isValid, "CSR signature should be valid");
153+
Assert.True(isValid, "ECDSA CSR signature should be valid");
168154
#else
169155
// ECDSA verification not supported on older frameworks
170156
Assert.Throws<NotSupportedException>(() => csr.Verify());
@@ -307,18 +293,6 @@ public void GetCertificationRequestInfoReturnsValidData()
307293
Assert.Greater(requestInfo.Length, 0);
308294
}
309295

310-
/// <summary>
311-
/// Test parsing CSR with small RSA key (less than 1024 bits) throws exception.
312-
/// </summary>
313-
[Test]
314-
public void ParseCsrWithSmallRsaKeyThrowsException()
315-
{
316-
// This test would require manually crafting a CSR with a small key
317-
// which is complex, so we'll skip it for now as the validation
318-
// happens during signature verification
319-
Assert.Pass("Validation happens during key import in RSA.Create()");
320-
}
321-
322296
/// <summary>
323297
/// Test parsing multiple CSRs in sequence.
324298
/// </summary>

0 commit comments

Comments
 (0)