using System;
using System.Security.Cryptography;
class Helo
{
private static string GetPemPrivateKey()
{
string privatekey = @"-----BEGIN PRIVATE KEY-----
.....
-----END PRIVATE KEY-----";
privatekey = privatekey.Replace("-----BEGIN PRIVATE KEY-----", "")
.Replace("-----END PRIVATE KEY-----", "")
.Replace("\n", "")
.Replace("\r", "");
return privatekey;
}
static void Main(string[] args)
{
string message = "hello world";
byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
Console.WriteLine( Convert.FromBase64String(GetPemPrivateKey()));
using (ECDsa ecdsa = ECDsa.Create())
{
ecdsa.ImportPkcs8PrivateKey(Convert.FromBase64String(GetPemPrivateKey()), out _);
byte[] signature = ecdsa.SignData(data, HashAlgorithmName.SHA256);
Console.WriteLine("Signature: " + Convert.ToBase64String(signature));
}
}
}
If I sign using the .NET code and try to verify it using the following GO CODE:
package main
import (
"crypto/ecdsa"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/base64"
"encoding/pem"
"fmt"
"log"
"math/big"
"strings"
)
const ()
var publicKey = []byte(`-----BEGIN PUBLIC KEY-----
....
-----END PUBLIC KEY-----`)
type Verifier struct {
publicKey *ecdsa.PublicKey
}
func newVerifier(publicKeyPEM []byte) (*Verifier, error) {
block, _ := pem.Decode(publicKeyPEM)
if block == nil || !strings.Contains(block.Type, "PUBLIC KEY") {
return nil, fmt.Errorf("invalid public key file")
}
publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
ecdsaPublicKey, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("invalid public key type")
}
return &Verifier{publicKey: ecdsaPublicKey}, nil
}
func main() {
v, err := newVerifier(publicKey)
if err != nil {
log.Println("Error creating verifier:", err)
return
}
fmt.Println("v", v.publicKey)
signature := "SIGNATURE_HERE"
data := []byte("your data here") // This is the data that was signed
if err := v.VerifySignature(signature, data); err != nil {
log.Println("Error verifying signature:", err)
} else {
log.Println("Signature is valid.")
}
}
func (v *Verifier) VerifySignature(signature string, rawData []byte) error {
sigBytes, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
log.Printf("Error decoding base64 signature: %v", err)
return err
}
hash := sha256.Sum256(rawData)
var ecdsaSig struct {
R, S *big.Int
}
if _, err := asn1.Unmarshal(sigBytes, &ecdsaSig); err != nil {
log.Printf("Error decoding ASN.1 signature: %v", err)
return fmt.Errorf("error decoding signature")
}
if !ecdsa.Verify(v.publicKey, hash[:], ecdsaSig.R, ecdsaSig.S) {
return fmt.Errorf("signature is invalid")
}
// Signature verification successful
return nil
}
it doesn't work and gives the error:
tags don't match (16 vs {class:3 tag:1 length:39 isCompound:false}) {optional:false explicit:false application:false private:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false} @2
.
However, if I sign with SIGNER and then verify with VERIFIER, it works.
I want to make the .NET code compatible with VERIFIER. When I try to verify with VERIFIER2, this Go code works fine.
I want to use VERIFIER and do not want to change the verification code. Changes to the C# code are acceptable.
The C# code generates a signature in P1363 format, the Go reference code with which you want to verify requires an ASN.1/DER encoded ECDSA signature. For an explanation of both formats and their relationship, see here.
C# supports both formats, whereby P1363 is used by default, i.e. the ASN.1/DER format must be explicitly specified with DSASignatureFormat
(s. here, supported as of .NET 5):
...
byte[] signature = ecdsa.SignData(data, HashAlgorithmName.SHA256, DSASignatureFormat.Rfc3279DerSequence);
...
With this change to the C# code, verification with the Go code works.
BTW, the C# code can also be further simplified, as .NET also supports the import of PEM keys with ImportFromPem()
(supported as of .NET 5).