How to validate a SHA256 signed result with a public key - c#

I need to validate a user by using an external authorize API. This API requires me to send a SHA-256 hash of something unique on my client side. I should use this to validate the signature later on.
I'm generating the Hash like this. As text I'm sending a unique Session ID:
private static string CalculateSHA256Hash(string text)
{
UnicodeEncoding UE = new UnicodeEncoding();
byte[] hashValue;
byte[] message = UE.GetBytes(text);
SHA256Managed hashString = new SHA256Managed();
string hex = "";
hashValue = hashString.ComputeHash(message);
foreach (byte x in hashValue)
{
hex += String.Format("{0:x2}", x);
}
return hex;
}
From the API I received a public signing key as a string:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA185vTa2xheB
<..>
SLhDuNk0SkiL7f8KbOG3tP9IDmAcAfoZWw/o2XjUxVLp1uw9V/hj4oJoapyS2sW4
cwIDAQAB
-----END PUBLIC KEY-----
I should use this key somehow to validate the response. The response I receive is this 512 character long string:
"57ec50faf72340f9618cf690160ab6b251a2d76fcce61583779804a07f8ac1f520bf09a4f5918a3
<...>
245f8d00b2cce3fa1ecc849f8771be0c8"
Now I should be able to Validate the signature I've received with the Public-key-string and my own hashed value.
How do I do this?
Edit
The hasing & padding scheme used on the API is "SHA256_RSA_PKCS_PSS"

Related

Youtube Pubsub Validate signature

I am creating an open Source Project which uses the youtube pubsub api to get notifications on video uploads. I want to verify that the request does come from youtube and not from a 3rd Party by checking the HMAC SHA1 Signature as described.
So, every time 1 Run my Program, I will generate a Secret later, to debug the problem, i use "test" as my secret string.
I use the following method to check if the provided signature is valid
public static bool Check(string body, string signature)
{
using (var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(Secret)))
{
var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
var hash = Encoding.UTF8.GetString(hashBytes);
Console.WriteLine("Computed Hash " + hash);
return signature.Equals(hash);
}
}
Where body is the request body and signature is a value provided in the request header.
According to https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html#rfc.section.7
If the subscriber supplied a value for hub.secret in their subscription request, the hub MUST generate an HMAC signature of the payload and include that signature in the request headers of the content distribution request. The X-Hub-Signature header's value MUST be in the form sha1=signature where signature is a 40-byte, hexadecimal representation of a SHA1 signature [RFC3174]. The signature MUST be computed using the HMAC algorithm [RFC2104] with the request body as the data and the hub.secret as the key.
I supply my Secret as hub.secret in my subscription request.
So if I understand it correctly, the hub SHOULD use that secret to generate a HMACSHA1 of the payload -> the body.
I want to regenerate the HMAC and should get the same value, right?
It does not work. Also the computed hash value logged by console.WriteLine is something completely different, not alphabetic characters at all, so I guess it might be a problem with the encoding, but I can't figure it out. Thanks for all the help!
The documentation says "where signature is a 40-byte, hexadecimal representation" so instead of converting hashBytes to an UTF8 string you should convert it to a hexadecimal string.
public static bool Check(string body, string signature)
{
using (var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(Secret)))
{
var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
var hash = Convert.ToHexString(hashBytes).ToLowerInvariant();
Console.WriteLine("Computed Hash " + hash);
return signature.Equals(hash);
}
}

PASETO token signature is not valid but the private and public key match

I'm using https://www.nuget.org/packages/Paseto.Core/ and this is how I generate the PASETO token:
public async Task<TokenResponse> GenerateAsync(Client client, TokenRequest tokenRequest, string issuer, string audience)
{
var ed25519pkcs8 = await File.ReadAllTextAsync("private.pem");
var privatePemReader = new PemReader(new StringReader(ed25519pkcs8));
var ed25519pkcs8Parameters = (Ed25519PrivateKeyParameters)privatePemReader.ReadObject();
ISigner signer = new Ed25519Signer();
signer.Init(true, ed25519pkcs8Parameters);
var pasetoToken = new PasetoBuilder()
.Use(ProtocolVersion.V4, Purpose.Public)
.WithKey(signer.GenerateSignature(), Encryption.AsymmetricSecretKey)
.Issuer(issuer)
.Subject(tokenRequest.ClientId)
.Audience(audience)
.NotBefore(DateTime.UtcNow)
.IssuedAt(DateTime.UtcNow)
.Expiration(DateTime.UtcNow.AddSeconds(client.AccessTokenLifetime))
.TokenIdentifier(Guid.NewGuid().ToString())
.AddClaim("client_id", tokenRequest.ClientId)
.AddClaim("scopes", tokenRequest.Scopes)
.Encode();
return new TokenResponse
{
AccessToken = pasetoToken,
Lifetime = client.AccessTokenLifetime,
Scope = tokenRequest.Scopes
};
}
Generated PASETO token looks like that: v4.public.eyJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo0NDMyMyIsInN1YiI6InRlc3RfY3JlZGVudGlhbHMiLCJhdWQiOiJ0ZXN0QXBpUmVzb3VyY2UiLCJuYmYiOiIyMDIyLTA1LTA3VDE4OjM4OjU2LjU0MjM2OTFaIiwiaWF0IjoiMjAyMi0wNS0wN1QxODozODo1Ni41NDI0MzUzWiIsImV4cCI6IjIwMjItMDUtMDdUMTk6Mzg6NTYuNTQyNDcwN1oiLCJqdGkiOiI5ODk3Mzc4Mi1kNWQwLTQzMjktYWY0ZS1kNTU3NGI4Y2Q2YmMiLCJjbGllbnRfaWQiOiJ0ZXN0X2NyZWRlbnRpYWxzIiwic2NvcGVzIjoidGVzdC5yZWFkIn0pQzMpSSXa-inBjgvDBNFgm7tE4w6J-TzzntJfKJErGRfm2ARuswWxJinhQMT-9v5q1ntyk4UtoIMr9ny0t4AH
So I created a test API for validating tokens, and the result always looks like this:
{
"IsValid":false,
"Paseto":null,
"Exception":{
"Expected":null,
"Received":null,
"Message":"The token signature is not valid",
"Data":{
},
"InnerException":null,
"HelpLink":null,
"Source":null,
"HResult":-2146233088,
"StackTrace":null
}
}
This is what validation looks like:
[HttpGet]
public IActionResult DecodePaseto([FromQuery] string token)
{
var ed25519x509 = System.IO.File.ReadAllText("public.pem");
var publicPemReader = new PemReader(new StringReader(ed25519x509));
var ed25519x509Parameters = (Ed25519PublicKeyParameters)publicPemReader.ReadObject();
var paseto = new PasetoBuilder()
.Use(ProtocolVersion.V4, Purpose.Public)
.WithKey(ed25519x509Parameters.GetEncoded(), Encryption.AsymmetricPublicKey)
.Decode(token);
return Ok(JsonConvert.SerializeObject(paseto));
}
Everything seems fine and yet there is sign or validation error. What could be wrong?
Paseto uses the raw public key (32 bytes) and as secret key the concatenation of raw private and raw public key (32 bytes + 32 bytes = 64 bytes), see here for an explanation of the different formats of a secret Ed25519 key.
While the public key is imported correctly in the posted code of the question, as private key the Ed25519 signature generated with the private key for an empty string is used. This is incorrect, but works (in the sense that no exception is thrown) because the signature is 64 bytes in size, which is the same length as the secret key. Of course, verification fails.
The following code shows the correct construction of the secret key for Paseto. For simplicity Linq is applied, but also e.g. Buffer.BlockCopy() can be used:
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using System;
using System.IO;
using System.Linq;
...
string ed25519pkcs8 = #"-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIAYIsKL0xkTkAXDhUN6eDheqODEOGyFZ04jsgFNCFxZf
-----END PRIVATE KEY-----";
PemReader privatePemReader = new PemReader(new StringReader(ed25519pkcs8));
Ed25519PrivateKeyParameters ed25519pkcs8Parameters = (Ed25519PrivateKeyParameters)privatePemReader.ReadObject();
string ed25519x509 = #"-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA3mcwgf2DrWLR3mQ6l2d59bGU6qUStwQrln2+rKlKxoA=
-----END PUBLIC KEY-----";
PemReader publicPemReader = new PemReader(new StringReader(ed25519x509));
Ed25519PublicKeyParameters ed25519x509Parameters = (Ed25519PublicKeyParameters)publicPemReader.ReadObject();
byte[] publicKey = ed25519x509Parameters.GetEncoded(); // raw 32 bytes public key
byte[] secretKey = ed25519pkcs8Parameters.GetEncoded().Concat(publicKey).ToArray(); // raw 32 bytes private key + raw 32 bytes public key
Test:
Using the above secret key, signing is feasible as follows (with arbitrary test data):
using Paseto;
using Paseto.Builder;
...
string pasetoToken = new PasetoBuilder()
.Use(ProtocolVersion.V4, Purpose.Public)
.WithSecretKey(secretKey) // short for .WithKey(secretKey, Encryption.AsymmetricSecretKey)
.Subject("subject")
.Issuer("whoever")
.Audience("https://www.whatever.com/someurl")
.NotBefore(DateTime.UtcNow)
.IssuedAt(DateTime.UtcNow)
.Expiration(DateTime.UtcNow.AddSeconds(3600))
.TokenIdentifier(Guid.NewGuid().ToString())
.AddClaim("client_id", "client_id")
.AddClaim("scopes", "scopes")
.Encode();
Console.WriteLine(pasetoToken);
and verifying using the above public key:
using Paseto;
using Paseto.Builder;
...
PasetoTokenValidationParameters validationParameters = new PasetoTokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = "whoever",
ValidateAudience = true,
ValidAudience = "https://www.whatever.com/someurl"
};
PasetoTokenValidationResult paseto = new PasetoBuilder()
.Use(ProtocolVersion.V4, Purpose.Public)
.WithPublicKey(publicKey) // short for .WithKey(publicKey, Encryption.AsymmetricPublicKey)
.Decode(pasetoToken, validationParameters);
Console.WriteLine(paseto.IsValid ? paseto.Paseto.RawPayload : "Decoding failed");
A possible output of the entire code is:
v4.public.eyJzdWIiOiJzdWJqZWN0IiwiaXNzIjoid2hvZXZlciIsImF1ZCI6Imh0dHBzOi8vd3d3LndoYXRldmVyLmNvbS9zb21ldXJsIiwibmJmIjoiMjAyMi0wNS0wN1QyMjowNzo0NS4yNzA1NjU4WiIsImlhdCI6IjIwMjItMDUtMDdUMjI6MDc6NDUuMjcwNjQ1OVoiLCJleHAiOiIyMDIyLTA1LTA3VDIzOjA3OjQ1LjI3MDY4NzRaIiwianRpIjoiNDU0MWI2NmMtOGRlZi00Mjg3LWFmZGMtYTE3ZDNhMDY3NjYxIiwiY2xpZW50X2lkIjoiY2xpZW50X2lkIiwic2NvcGVzIjoic2NvcGVzIn1RyxW-gjy6va7IA5pL9pZMqcrBjYkYFX16AV7IqTt5Fa5YtQMbIJQkfu24uq7bR2lx0WMLHa0xr2fsJRtdpsAG
{"sub":"subject","iss":"whoever","aud":"https://www.whatever.com/someurl","nbf":"2022-05-07T22:07:45.2705658Z","iat":"2022-05-07T22:07:45.2706459Z","exp":"2022-05-07T23:07:45.2706874Z","jti":"4541b66c-8def-4287-afdc-a17d3a067661","client_id":"client_id","scopes":"scopes"}
Here eyJz...psAG is the Bas64url encoding of payload and concatenated 64 bytes Ed25519 signature.

Hashing by sha1 algorithm with user's secret key in C#

I need to hash a string according to the sha1 algorithm, with the user's secret key, something like: hash = hash (string, secret_key).
I use this code:
byte[] data = sha1.ComputeHash(Encoding.UTF8.GetBytes(input));
But I can not find how to use the secret key in this algorithm.
The hash is the result of applying a hash function to data. It maps arbitrary data to fixed size value. This does not require any kind of secret.
The cryptographic process which uses hashes and secret keys is signing data. You may want to create a hash of your data and a MAC (message authentication code) based on public/private key cryptography using the private key.
A hash tells you only that data was modified or not.
A MAC uses any kind of secret, thus it protects also against manipulation. Generally, MACs only work when the sender and receiver know each other.
You could do something like:
public string SignData(string message, string secret)
{
var encoding = new System.Text.UTF8Encoding();
var keyBytes = encoding.GetBytes(secret);
var messageBytes = encoding.GetBytes(message);
using (var hmacsha1 = new HMACSHA1(keyBytes))
{
var hashMessage = hmacsha1.ComputeHash(messageBytes);
return Convert.ToBase64String(hashMessage);
}
}

Verify pkcs7 Signature received from AS2 trading partner

I am receiving an AS2 message from my trading partner. I have successfully decrypted it and it has following content:
Content-Type: multipart/signed; boundary="----1E2FB76A361B53C9D12B3971C32BC0D8"; protocol="application/pkcs7-signature"; micalg="sha-256"
------1E2FB76A361B53C9D12B3971C32BC0D8
Content-Transfer-Encoding: binary
Content-Disposition: attachment; filename="edi.dat"
Content-Type: application/octet-stream; charset="ascii"; name="edi.dat"
ISA*00* *00* *ZZ*SPSAS2 *ZZ*SPSAS2 *140919*1159*|*00501*100000001*0*P*>
GS*PO*AS2S3REC*AS2S3SEND*20140919*1159*123*X*005010
ST*850*1234
BEG*00*SA*SPSAS2TEST**20121017
REF*IA*TEST
PER*OC*Someone*TE*2844994944
DTM*001*20121025
N1*ST*SomeoneElse
N3*My address is private
N4*ST THOMAS*VI*00801
PO1*54812*1*EA***SK*123546*VP*123546*UP*123456987111
CTT*1
SE*11*1234
GE*1*123
IEA*1*100000001
------1E2FB76A361B53C9D12B3971C32BC0D8
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"
Content-Type: application/pkcs7-signature; name="smime.p7s"
MIIL5gYJKoZIhvcNAQcCoIIL1zCCC9MCAQExDzANBglghkgBZQMEAgEFADALBgkq
hkiG9w0BBwGgggg3MIIIMzCCBxugAwIBAgIQCq+jbObLeCwtsdF61BAtBzANBgkq
hkiG9w0BAQsFADBNMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j
MScwJQYDVQQDEx5EaWdpQ2VydCBTSEEyIFNlY3VyZSBTZXJ2ZXIgQ0EwHhcNMTgw
MjA5MDAwMDAwWhcNMjEwMjE3MTIwMDAwWjCBgjELMAkGA1UEBhMCVVMxEjAQBgNV
BAgTCU1pbm5lc290YTEUMBIGA1UEBxMLTWlubmVhcG9saXMxGzAZBgNVBAoTElNQ
UyBDb21tZXJjZSwgSW5jLjELMAkGA1UECxMCSVQxHzAdBgNVBAMTFnNwc2FzMi5j
b21tZXJjZXZhbi5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCw
1fJZ14+32k+Qutklk2+YYxaPMe2ASv41lfR5945H6M2SmdgFqqF2LPNZqWofcBPQ
HbMWctbBHOW1shOS2XvBRUFT5tuyQKcG4iYz7FEHFGpu4oGyC0eUYSuBBtTQMmRi
zk8bMwwNhRUUyzxa/y3OePK0lEDdaQsSKi8WHLkfkVZBo7Lk9CPyR4WZ8q8uo/qW
tNMDbk1Fn2CtXqYrXFTmLJ81ScXGTp5zoIUhrkGcUzumK+25EBT9p+2XcVMCUj9S
LY1DWyTnWx2mgT0ekeHM0pXV2MPDuUG99SiQB4Q0CH0I3J4ZafP2rCzBVkvNlTFJ
ZojaQyfcP9W73ZiYSgUmQ3mCa/BjACTO8wztu9DBelUNVjSxCYysGUdKNyl6hAAI
OAPe8z0mmDCVA7S4rnTYkliW+2JQdOTaKc0GOiaHQjrd+Gta1aAJHRR1pxjSX2FC
apS3DkiJl4i5vdyPZM53QhS51XCxGHtCCcJ574MIPkthI4SLwKJkk6rSgvb7B0ai
7jbDmvp1FjY0SgKoHyN+QNH1J4rMeK7IUp2N0R+YrJa6kEaocIptSoPToDGKWk+o
qAx65r3T2n21jEfiCzN6JyqxNfE1T35Pekpvu4ioUEfW1ICf6AOQ5oQHWLr/Fhk2
sVzptYw5Lmtkb/sPGP1R/hBy88HAlTAh+QFrRJHHWwIDAQABo4ID1zCCA9MwHwYD
VR0jBBgwFoAUD4BhHIIxYdUvKOeNRji0LOHG2eIwHQYDVR0OBBYEFJs5x7uPOhBe
2Mp1aThYg8urrQ4FMCEGA1UdEQQaMBiCFnNwc2FzMi5jb21tZXJjZXZhbi5jb20w
DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBr
BgNVHR8EZDBiMC+gLaArhilodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc3NjYS1z
aGEyLWc2LmNybDAvoC2gK4YpaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NzY2Et
c2hhMi1nNi5jcmwwTAYDVR0gBEUwQzA3BglghkgBhv1sAQEwKjAoBggrBgEFBQcC
ARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAIBgZngQwBAgIwfAYIKwYB
BQUHAQEEcDBuMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20w
RgYIKwYBBQUHMAKGOmh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
dFNIQTJTZWN1cmVTZXJ2ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADCCAfYGCisGAQQB
1nkCBAIEggHmBIIB4gHgAHYApLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN
3BAAAAFhezY//QAABAMARzBFAiEA/peliWFFZt3DPuaO1JekVr9XQhfW/SeCVONq
Rmju3zECIDByDCYP5dzgszieTnkpP3w5dsOfiVmJ+RRmuNORjb0rAHYAh3W/51l8
+IxDmV+9827/Vo1HVjb/SrVgwbTq/16ggw8AAAFhezZA2gAABAMARzBFAiEAmlnD
uPy34+XDxNZU8r0dD4fI+hFJwxQQkatA9zkIzegCIFxT3jLCCf9pVoEIeWYBOv3C
BjVqsn0jDqXLYSVOJTQVAHYAu9nfvB+KcbWTlCOXqpJ7RzhXlQqrUugakJZkNo4e
0YUAAAFhezZAzAAABAMARzBFAiEAwAWMiOFLxRBqVjLc/cktB7SjMujisPPx5WQA
Bat22NoCIAtVJcU2yoj6c66x8o/YI2lfSdZqF46K51WGb6/J79UdAHYAb1N2rDHw
MRnYmQCkURX/dxUcEdkCwQApBo2yCJo32RMAAAFhezZDCwAABAMARzBFAiAqzLuy
gkPgUSELvuB2xF9bFxPOj2YjBoiggQbUGfCMqgIhAL3VnGsi6xXUBswPEMn0M3P5
Cgnz6UGp2M4ItBE69YQnMA0GCSqGSIb3DQEBCwUAA4IBAQBbxA1NvqhkXddENUT5
qHY1pF91metZG38DwUS7c0xgS/LoY/WZZJ2cv0rEMoGCzRwwYijiiTc1MY6muiGE
hpdNja9Tu3bYJ9Z1bkiJJVKknqcqs64ibIbD762a7L/J0O9DzFKHeUwXAhpzT+rh
Z2eIPSR9BpUbiBpok73F/TvW2beJb2ncEiQJujK+UKlB9XeSsghrMOBiGnNJFB5t
Y155GAQb6oGiwRkkhWiJYZOq6riU5dmUbyqAqaxHij6hZOUzDL/fM5oeOpusedR0
4+eYgTe7LBq2teDNjchSceapKRXbbtz4UNDp0Ce3ZQyU4T4ndAsF9QiQnq7478SO
2ZgpMYIDczCCA28CAQEwYTBNMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
cnQgSW5jMScwJQYDVQQDEx5EaWdpQ2VydCBTSEEyIFNlY3VyZSBTZXJ2ZXIgQ0EC
EAqvo2zmy3gsLbHRetQQLQcwDQYJYIZIAWUDBAIBBQCggeQwGAYJKoZIhvcNAQkD
MQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTgwODMwMTkxMjQ3WjAvBgkq
hkiG9w0BCQQxIgQgh0UxRgyVkgQvRpk0/2ZBwHWFEqKH5Hu6qgpvjX9ETjUweQYJ
KoZIhvcNAQkPMWwwajALBglghkgBZQMEASowCwYJYIZIAWUDBAEWMAsGCWCGSAFl
AwQBAjAKBggqhkiG9w0DBzAOBggqhkiG9w0DAgICAIAwDQYIKoZIhvcNAwICAUAw
BwYFKw4DAgcwDQYIKoZIhvcNAwICASgwDQYJKoZIhvcNAQEBBQAEggIAEb1tlBKO
lSv69TDq4Jlhmtqt2szWKZazOhTMDUo68j3rJ6xJS9so7NKrQv+aeo8DdAblU/G/
OD2U6XS5iDdYg94aIyNEjuCZOfvibV8U1PA6WP+8VTo3vEDP0JoAt/NgWJKiKESS
iQ/8waAcLqxECW1Pa11KxdvO6xhJKpNI/xpl/t0007mYrZDseuLZNcyQjOOrP0Kj
E/21geSIyTTN842R0Zg6qTn6XlkPUq/bSa9MnlCUDNGxFn25Uqv+3nYlJhEiGyHa
GQef4vZL1faz7ljxnx/MJKiWAbq07F7tYEHGyVTkO3MGF/wBe8vDlYs3U1T4v+2g
hjEWiOyREYTt9a4XJ1QcscjbGCRl28BajmxBBHibkSM2o5ztLxPUoiEKjY3XQc6J
oHX3Gmj63qMQPQz58AVoalGtwxOxlJiFc0Y13NIydbxMRuxep0XHVNE5XFQGE1K/
2r5iV4pS1f+EarDxfjzkrUwZFYJLdO27DqIL//6m6uTiIgZBXnzqulWlLxX17MCD
2yUOTRPGqo9eRhnrtbeh3PYGXG0zhGSdmzoTQr2mf2k7EDB/48BGvUWeHGsc7sQT
LUrlnLKqiG1uRB4sWTWMtxsKtH8kGP824ihszLh+13IXlMztCEg+kh472KQENguC
Yxt8DzcvxMi0rIsbsC/Thq3nxpFCGDV7m3Q=
------1E2FB76A361B53C9D12B3971C32BC0D8--
It has the signature in "Base64" format and "pkcs7-signature". I am not able to match signature. I am considering following part as main data:
ISA*00* *00* *ZZ*SPSAS2 *ZZ*SPSAS2 *140919*1159*|*00501*100000001*0*P*>
GS*PO*AS2S3REC*AS2S3SEND*20140919*1159*123*X*005010
ST*850*1234
BEG*00*SA*SPSAS2TEST**20121017
REF*IA*TEST
PER*OC*Someone*TE*2844994944
DTM*001*20121025
N1*ST*SomeoneElse
N3*My address is private
N4*ST THOMAS*VI*00801
PO1*54812*1*EA***SK*123546*VP*123546*UP*123456987111
CTT*1
SE*11*1234
GE*1*123
IEA*1*100000001
and base64 string as sign. and sung following function to match sign:
static bool Verify(string text, byte[] signature, string certPath)
{
X509Certificate2 cert = new X509Certificate2(certPath);
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key;
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Verify the signature with the hash
return csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), signature);
}
But this function is not matching the signature.
Please help me with this problem.
Thank you...
I can't, personally, get the data to validate. (I'm assuming that either there are trailing spaces that aren't copying correctly or that the payload has been altered (e.g. "My address is private"))
The flow that I expect is happening:
The pkcs7 content is a CMS SignedData with detached content.
The signed content was specified in the first stream
So you need to do something like:
byte[] dataBytes = ReadFirstSegment();
byte[] signatureBytes = ReadSecondSegment();
SignedCms cms = new SignedCms(new ContentInfo(dataBytes), detached: true);
cms.Decode(signatureBytes);
// This next line throws a CryptographicException if the signature can't be verified
cms.CheckSignature(true);
SignerInfoCollection signers = cms.SignerInfos;
if (signers.Count != 1)
{
// probably fail
}
if (!IsExpectedCertificate(signers[0].Certificate))
{
// fail
}
// success

Validate Google idToken - certificate verification fails using RSACryptoServiceProvider

I am building login workflow using Google for user. Once user is authenticated, I call GetAuthResponse to get idToken.
https://developers.google.com/identity/sign-in/web/backend-auth
Now, I need to verify certificate against Google certificate. I am using JwtSecurityToken(C#) for the same.
I am referencing for verification - http://blogs.msdn.com/b/alejacma/archive/2008/06/25/how-to-sign-and-verify-the-signature-with-net-and-a-certificate-c.aspx
Issue is - I always gets false from VerifyHash. As, VerifyHash returns just false without any reason, I am not able to find way to verify whether idToken is
valid or not. My code is given below
String strID = ""; // idToken received from Google AuthResponse
JwtSecurityToken token = new JwtSecurityToken(strID);
byte[] text = GetHash(token.RawData);
SHA256Cng sha1 = new SHA256Cng();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
byte[] signature = Encoding.Unicode.GetBytes(token.RawSignature);
// Modulus and exponent value from https://www.googleapis.com/oauth2/v2/certs - second set of keys
String modulus = "uHzGq7cMlx21nydbz9VsW1PItetb9mqvnpLp_8E3Knyk-mjv9DlaPhKGHYlJfHYGzKa2190C5vfsLLb1MIeGfdAv7ftpFsanIWawl8Zo0g-l0m7T2yG_7XerqcVK91lFifeJtgxKI86cPdZkgRy6DaYxMuAwAlhvpi3_UhPvsIwi7M6mxE8nUNpUWodh_YjJNu3wOxKDwbBZuRV2itjY6Z7RjFgJt1CsKF-QjqSVvWjAl0LaCaeMS_8yae0ln5YNeS8rAb6xkmcOuYeyhYsiBzwLRvgpXzEVLjLr631Z99oUHTpP9vWJDpGhfkrClkbmdtZ-ZCwX-eFW6ndd54BJEQ==";
String exponent = "AQAB";
modulus = modulus.Replace('-', '+').Replace('_', '/'); // Else it gives Base64 error
StringBuilder sb = new StringBuilder();
sb.Append("<RSAKeyValue>");
sb.Append("<Modulus>");
sb.Append(modulus);
sb.Append("</Modulus>");
sb.Append("<Exponent>");
sb.Append(exponent);
sb.Append("</Exponent>");
sb.Append("</RSAKeyValue>");
RSACryptoServiceProvider RSAVerifier = new RSACryptoServiceProvider();
RSAVerifier.FromXmlString(sb.ToString());
// Verify the signature with the hash
return RSAVerifier.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA256"), signature);
You might want to try as done in the Google+ Token Verification project - this fork includes a few minor updates that are still in review.
An alternative approach is to just verify the tokens using Google's token verification endpoints:
curl https://www.googleapis.com/oauth2/v2/tokeninfo?id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjkyNGE0NjA2NDgxM2I5YTA5ZmFjZGJiNzYwZGI5OTMwMWU0ZjBkZjAifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTEwNTcwOTc3MjI2ODMwNTc3MjMwIiwiYXpwIjoiMzY0MzgxNDQxMzEwLXRuOGw2ZnY2OWdnOGY3a3VjanJhYTFyZWpmaXRxbGpuLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiYXRfaGFzaCI6IlAzLU1HZTdocWZhUkZ5Si1qcWRidHciLCJhdWQiOiIzNjQzODE0NDEzMTAtdG44bDZmdjY5Z2c4ZjdrdWNqcmFhMXJlamZpdHFsam4uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJjX2hhc2giOiJjd3hsdXBUSkc4N2FnbU1pb0tSYUV3IiwiaWF0IjoxNDM0NDcyODc2LCJleHAiOjE0MzQ0NzY0NzZ9.Gz_WljZOV9NphDdClakLstutEKk65PNpEof7mxM2j-AOfVwh-SS0L5uxIaknFOk4-nDGmip42vrPYgNvbQWKZY63XuCs94YQgVVmTNCTJnao1IavtrhYvpDqGuGKdEB3Wemg5sS81pEthdvHwyxfwLPYukIhT8-u4ESfbFacsRtR77QRIOk-iLJAVYWTROJ05Gpa-EkTunEBVmZyYetbMfSoYkbwFKxYOlHLY-ENz_XfHTGhYhb-GyGrrw0r4FyHb81IWJ6Jf-7w6y3RiUJik7kYRkvnFouXUFSm8GBwxsioi9AAkavUWUk27s15Kcv-_hkPXzVrW5SvR1zoTI_IMw

Categories

Resources