0

I'm using self signed certificate ECDH_secP384r1 for signing token. Here is the PowerShell that I create the certificate:

$Cert = New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname $Certname -NotAfter $ExpireDate -KeyAlgorithm ECDH_secP384r1

Now in my .net core application first I load the certificate:

private readonly string _certificateSubjectName;

public X509Certificate2 GetSigningCertificate()
{

    using (var store = new X509Store(StoreLocation.LocalMachine))
    {
        store.Open(OpenFlags.ReadOnly);
        var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, _certificateSubjectName, false);
        return certificates[0];

    }
}

Now by having the certificate I got an error in creating SigningCredentials: I tried to follow this way

public string GetSignedToken(IEnumerable<Claim> claims)
{
    var signingCertificate = GetSigningCertificate();
    byte[] certBytes = signingCertificate.Export(X509ContentType.Pkcs12);

    var privateECDsa = LoadPrivateKey(certBytes);
    var signingCredentials = new SigningCredentials(new ECDsaSecurityKey(privateECDsa), SecurityAlgorithms.EcdsaSha384);

    var token = new JwtSecurityToken(
                issuer: _issuer,
                audience: _audience,
                claims: claims,
                expires: DateTime.Now.AddMinutes(_expiryMinutes),
                signingCredentials: signingCredentials);

    var securityTokenHandler = new JwtSecurityTokenHandler();
    var rawJwtToken = securityTokenHandler.WriteToken(token);
    return rawJwtToken ;
}


private static ECDsa LoadPrivateKey(byte[] key)
{

    var privKeyInt = new Org.BouncyCastle.Math.BigInteger(+1, key);
    var parameters = SecNamedCurves.GetByName("secP384r1");
    var ecPoint = parameters.G.Multiply(privKeyInt);
    var privKeyX = ecPoint.Normalize().XCoord.ToBigInteger().ToByteArrayUnsigned();
    var privKeyY = ecPoint.Normalize().YCoord.ToBigInteger().ToByteArrayUnsigned();

    var curve = ECCurve.NamedCurves.nistP384;
    var d = privKeyInt.ToByteArrayUnsigned();
    var q = new ECPoint
    {
        X = privKeyX,
        Y = privKeyY
    };

    var eCParameters = new ECParameters
    {
        Curve = curve,
        D = d,
        Q = q
    };


    var eCDsa = ECDsa.Create(eCParameters); //In this line I got an exception
    return eCDsa;

}

But I got an exception in ECDsa.Create:

The specified key parameters are not valid. Q.X and Q.Y are required fields. Q.X, Q.Y must be the same length. If D is specified it must be the same length as Q.X and Q.Y for named curves or the same length as Order for explicit curves.

UPDATE:

I also tried this way to fix sizes:

var d = FixSize(privKeyInt.ToByteArrayUnsigned(), privKeyX.Length);

but in this condition input[0] != 0 I got an exception, my input[0] is not 0 Also the input.Length is 1250 and expected size is 48

What I missed? any idea please.

Saeid
  • 13,224
  • 32
  • 107
  • 173
  • Are Q.X and Q.Y the same length? And are they the same length as D? Bouncy Castle is probably eliminating leading zero bytes that ECParameters considers important. – bartonjs Apr 02 '20 at 15:20
  • Does this answer your question? [Translating Elliptic Curve parameters (BC to MS)](https://stackoverflow.com/questions/48542233/translating-elliptic-curve-parameters-bc-to-ms) – bartonjs Apr 02 '20 at 15:21
  • @bartonjs Check my updated question plz. fix sizes not works for me properly – Saeid Apr 02 '20 at 15:27
  • Hard to say without seeing the code in context. For secp384r1 the expected size is `384/8` (48). – bartonjs Apr 02 '20 at 15:29
  • @bartonjs I'm trying to figure out based on your answers, But in the first step in my loaded certificate: `signingCertificate` I got an exception in `signingCertificate.PrivateKey` is that normal behavior? should I load the certificate differently? – Saeid Apr 02 '20 at 16:03
  • @bartonjs I update the question, would you please look at that. Thank you! – Saeid Apr 02 '20 at 16:21
  • the input.Length is 1250 and expected size is 48 – Saeid Apr 02 '20 at 16:36
  • Seems like your key isn’t D, then, but some other format (probably a full PKCS#8 blob). D would be 48 bytes (or smaller). – bartonjs Apr 02 '20 at 18:38
  • Actually, if it’s over 1kb that’s way too big for a PKCS#8. It’s possibly a PFX/PKCS#12. Nevertheless, you need to start with understanding what data you have. This is correct if it was actually the raw D value (if you put in the array resizing code). – bartonjs Apr 02 '20 at 18:41

2 Answers2

1

Ok, after some search I found an easy solution, I put it as an answer here:

using System.Security.Cryptography.X509Certificates;

public string GetSignedToken(IEnumerable<Claim> claims)
{
    var signingCertificate = GetSigningCertificate();
    var securityKey = new ECDsaSecurityKey(signingCertificate.GetECDsaPrivateKey());
    var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.EcdsaSha384);

     var token = new JwtSecurityToken(
          issuer: _issuer,
          audience: _audience,
          claims: claims,
          expires: DateTime.Now.AddMinutes(_expiryMinutes),
          signingCredentials: signingCredentials);

    var securityTokenHandler = new JwtSecurityTokenHandler();
    var rawJwtToken = securityTokenHandler.WriteToken(token);

    return rawJwtToken;
}

And for validation:

using System.Security.Cryptography.X509Certificates;

public TokenValidationParameters GetTokenValidationParameters()
{
    var signingCertificate = GetSigningCertificate();
    var securityKey = new ECDsaSecurityKey(signingCertificate.GetECDsaPublicKey());

    var validationParameters = new TokenValidationParameters()
    {
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = securityKey,
        ValidateIssuer = true,
        ValidIssuer = _issuer,
        ValidateAudience = true,
        ValidAudience = _audience
    };

    return validationParameters;
}
Saeid
  • 13,224
  • 32
  • 107
  • 173
0

The following code will help you, you can generate an algorithm using bouncy castle library:

private static ECDsa GetEllipticCurveAlgorithm(string privateKey)
{
    var keyParams = (ECPrivateKeyParameters)PrivateKeyFactory
        .CreateKey(Convert.FromBase64String(privateKey));

    var normalizedECPoint = keyParams.Parameters.G.Multiply(keyParams.D).Normalize();

    return ECDsa.Create(new ECParameters
    {
        Curve = ECCurve.CreateFromValue(keyParams.PublicKeyParamSet.Id),
        D = keyParams.D.ToByteArrayUnsigned(),
        Q =
    {
        X = normalizedECPoint.XCoord.GetEncoded(),
        Y = normalizedECPoint.YCoord.GetEncoded()
    }
    });
}

and generate the token in the following way:

var signatureAlgorithm = GetEllipticCurveAlgorithm(privateKey);

            ECDsaSecurityKey eCDsaSecurityKey = new ECDsaSecurityKey(signatureAlgorithm)
            {
                KeyId = settings.Apple.KeyId
            };

            var handler = new JwtSecurityTokenHandler();   
            var token = handler.CreateJwtSecurityToken(
                issuer: iss,
                audience: AUD,
                subject: new ClaimsIdentity(new List<Claim> { new Claim("sub", sub) }),
                expires: DateTime.UtcNow.AddMinutes(5), 
                issuedAt: DateTime.UtcNow,
                notBefore: DateTime.UtcNow,
                signingCredentials: new SigningCredentials(eCDsaSecurityKey, SecurityAlgorithms.EcdsaSha256));
iknow
  • 8,358
  • 12
  • 41
  • 68
Shah Zain
  • 388
  • 4
  • 10