BouncyCastle Timestamp with OCSP embedded - c#

There:
I am building TimeStamp Authorization by BouncyCastle (c#), everything is ok, however, I can't figure out how I can embed OCSP response when sign by timestamp key, by default, it just use CRL which defined in timestamp certificate.
TimeStampTokenGenerator tokenGen = new TimeStampTokenGenerator(theIssuerPrivateKey, cert, TspAlgorithms.Sha512, "1.2");
IList certList = new ArrayList();
certList.Add(cert);
IX509Store x509Certs = X509StoreFactory.Create("Certificate/Collection", new X509CollectionStoreParameters(certList));
tokenGen.SetCertificates(x509Certs);
TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
reqGen.SetCertReq(true);
TimeStampRequest req = reqGen.Generate(TspAlgorithms.Sha512, content);
TimeStampResponseGenerator respGen = new TimeStampResponseGenerator(tokenGen, TspAlgorithms.Allowed);
TimeStampResponse resp = respGen.Generate(req, new BigInteger("23"), signTime);
//embedded some ocsp response to tokenGen maybe? But how?
Please help....

Related

How to use client certificate for HTTPs requests in UWP app

I'm writing an app that needs to make some HTTPs requests that use a client certificate. However, I can't find any documents on how to install the certificate and then load it for use. I know that you can use the certificate by making a HttpBaseProtocolFilter and adding a certificate but how do you load the certificate for use here? And if you have a .pfx file with your client certificate, how do you install it with your package?
Thanks in advance!
For what it's worth, I ended up figuring this out using a mix of the Portable.BouncyCastle NuGet package and some UWP APIs. Some sample (pseudo-ish) code for what I did is below:
// Asymmetric key pair
RsaKeyPairGenerator keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(
new KeyGenerationParameters(
new SecureRandom(new CryptoApiRandomGenerator()), 2048));
AsymmetricCipherKeyPair keyPair = keyPairGenerator.GenerateKeyPair();
// Create certificate
X509V3CertificateGenerator generator = new X509V3CertificateGenerator();
generator.SetSubjectDN("foo");
generator.SetIssuerDN("foo");
generator.SetSerialNumber(new BigInteger("12345").Abs());
generator.SetNotBefore(DateTime.UtcNow);
generator.SetNotAfter(DateTime.UtcNow + TimeSpan.FromYears(1));
generator.SetPublicKey(keyPair.Public);
BouncyCastleX509Certificate certificate =
generator.Generate(
new Asn1SignatureFactory("SHA1WithRSA", keyPair.Private));
// Create PKCS12 certificate bytes.
Pkcs12Store store = new Pkcs12Store();
X509CertificateEntry certificateEntry = new X509CertificateEntry(certificate);
string friendlyName = "Friendly Name";
string password = "password";
store.SetCertificateEntry(friendlyName, certificateEntry);
store.SetKeyEntry(
friendlyName,
new AsymmetricKeyEntry(keyPair.Private),
new X509CertificateEntry[] { certificateEntry });
string pfxData;
using (MemoryStream memoryStream = new MemoryStream(512))
{
store.Save(memoryStream, password.ToCharArray(), this.SecureRandom);
pfxData = CryptographicBuffer.EncodeToBase64String(memoryStream.ToArray().AsBuffer());
}
// Add the certificate to the cert store
await CertificateEnrollmentManager.ImportPfxDataAsync(
pfxData,
password,
ExportOption.NotExportable,
KeyProtectionLevel.NoConsent,
InstallOptions.DeleteExpired,
friendlyName);
// Read the UWP cert from the cert store
Certificate uwpCertificate =
(await CertificateStores.FindAllAsync(
new CertificateQuery { FriendlyName = friendlyName }))[0];
// Create the UWP HTTP client.
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted);
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName);
filter.ClientCertificate = uwpCertificate;
HttpClient httpClient = new HttpClient(filter);
// Profit!

How to add PFX installed certificate to Windows.Web.Http.HttpClient

i have manually installed pfx extention certificate to my machine . How to get and pass in Windows.Web.Http.HttpClient
i tried following way but no luck
var myFilter = new HttpBaseProtocolFilter();
CertificateQuery certQuery = new Windows.Security.Cryptography.Certificates.CertificateQuery();
certQuery.FriendlyName = "TEST"; // This is the friendly name of the certificate that was just installed.
IReadOnlyList<Certificate> certificates = await Windows.Security.Cryptography.Certificates.CertificateStores.FindAllAsync(certQuery);
var client = new HttpClient(certificates[0]);
Can someone help me on this how to add manually installed certificate in httpclient ?
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs = store.Certificates.Find(
X509FindType.FindByIssuerName, "TEST", false);
//Change FindType to your liking, it doesn't support FriendlyName,
//maybe use your method?
WebRequestHandler handler = new WebRequestHandler();
X509Certificate2 certificate = GetMyX509Certificate();
handler.ClientCertificates.Add(certificate);
HttpClient client = new HttpClient(handler);
There you have it. Maybe use your certificate for the Add method but I don't know if they're compatible.

Google OAuth2 with Server to Server authentication returns "invalid_grant"

I am trying to follow the steps outlined here in order to acquire the access token for use with the Google Calendar API with OAuth2. After attempting to put together and sign the jwt, I always end up with a 400 "Bad request" response, with the error "invalid_grant".
I have followed the steps quite closely and have meticulously checked each line multiple times. I have also exhaustively looked through each and every post on the topic that I could find. I am so thoroughly stumped that I have written my first ever SO question after many years of finding solutions online.
Commonly proposed solutions that I have already tried:
1) My system clock is sync'd with ntp time
2) I am using the email for iss, NOT the client ID.
3) My issued times and expiration times are in UTC
4) I did look into the access_type=offline parameter, but it does not seem to apply in this server-to-server scenario.
5) I did not specify the prn parameter.
6) Various other misc things
I am aware that there are Google libraries that help manage this, but I have reasons for why I need to get this working by signing the jwt myself without using the provided libraries. Also, many of the questions and samples I've seen thus far seem to be using accounts.google.com/o/oauth2/auth for the base url, whereas the documentation I linked above seems to specify that the request go to www.googleapis.com/oauth2/v3/token instead (so it seems that many of the existing questions might apply to a different scenario). In any case, I'm totally stumped and don't know what else to try. Here is my C# code, with a few specific strings redacted.
public static string GetBase64UrlEncoded(byte[] input)
{
string value = Convert.ToBase64String(input);
value = value.Replace("=", string.Empty).Replace('+', '-').Replace('/', '_');
return value;
}
static void Main(string[] args)
{
DateTime baseTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
DateTime now = DateTime.Now.ToUniversalTime();
int ticksIat = ((int)now.Subtract(baseTime).TotalSeconds);
int ticksExp = ((int)now.AddMinutes(55).Subtract(baseTime).TotalSeconds);
string jwtHeader = #"{""typ"":""JWT"", ""alg"":""RS256""}";
string jwtClaimSet = string.Format(#"{{""iss"":""************-********************************#developer.gserviceaccount.com""," +
#"""scope"":""https://www.googleapis.com/auth/calendar.readonly""," +
#"""aud"":""https://www.googleapis.com/oauth2/v3/token"",""exp"":{0},""iat"":{1}}}", ticksExp, ticksIat);
byte[] headerBytes = Encoding.UTF8.GetBytes(jwtHeader);
string base64jwtHeader = GetBase64UrlEncoded(headerBytes);
byte[] claimSetBytes = Encoding.UTF8.GetBytes(jwtClaimSet);
string base64jwtClaimSet = GetBase64UrlEncoded(claimSetBytes);
string signingInputString = base64jwtHeader + "." + base64jwtClaimSet;
byte[] signingInputBytes = Encoding.UTF8.GetBytes(signingInputString);
X509Certificate2 pkCert = new X509Certificate2("<path to cert>.p12", "notasecret");
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)pkCert.PrivateKey;
CspParameters cspParam = new CspParameters
{
KeyContainerName = rsa.CspKeyContainerInfo.KeyContainerName,
KeyNumber = rsa.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2
};
RSACryptoServiceProvider cryptoServiceProvider = new RSACryptoServiceProvider(cspParam) { PersistKeyInCsp = false };
byte[] signatureBytes = cryptoServiceProvider.SignData(signingInputBytes, "SHA256");
string signatureString = GetBase64UrlEncoded(signatureBytes);
string finalJwt = signingInputString + "." + signatureString;
HttpClient client = new HttpClient();
string url = "https://www.googleapis.com/oauth2/v3/token?grant_type=urn%3aietf%3aparams%3aoauth%3agrant-type%3ajwt-bearer&assertion=" + finalJwt;
HttpResponseMessage message = client.PostAsync(url, new StringContent(string.Empty)).Result;
string result = message.Content.ReadAsStringAsync().Result;
}
This is using a google "Service Account" which I set up on my google account, with a generated private key and its corresponding .p12 file used directly.
Has anyone gotten this approach to work? I would strongly appreciate any help at all!
You're POSTing to the token endpoint but the parameters are sent as part of the query string. You should send the parameters as URL form-encoded values in the POST body. For example:
var params = new List<KeyValuePair<string, string>>();
params.Add(new KeyValuePair<string, string>("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"));
params.Add(new KeyValuePair<string, string>("assertion", finalJwt));
var content = new FormUrlEncodedContent(pairs);
var message = client.PostAsync(url, content).Result;
try this to get access token-
String serviceAccountEmail = "xxxxxxx.gserviceaccount.com";
String keyFilePath = System.Web.HttpContext.Current.Server.MapPath("~/Content/Security/file.p12"); ////.p12 file location
if (!File.Exists(keyFilePath))
{
Console.WriteLine("An Error occurred - Key file does not exist");
return null;
}
string[] scopes = new string[] {
CloudVideoIntelligenceService.Scope.CloudPlatform, ///CloudVideoIntelligence scope
YouTubeService.Scope.YoutubeForceSsl, ///Youtube scope
TranslateService.Scope.CloudTranslation ///Translation scope
};
var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes
}.FromCertificate(certificate));
var token = Google.Apis.Auth.OAuth2.GoogleCredential.FromServiceAccountCredential(credential).UnderlyingCredential.GetAccessTokenForRequestAsync().Result;//retrive token
return token;

Hand-crafted Chef API requests - getting "invalid signature for user"

For various reasons I need to be able to make Chef API requests in C#. I've followed the guides here (Header specification) and here (Bash example) but have reached a dead end. Every request I send comes back 401 Unauthorized with the content
{"error":["Invalid signature for user or client 'myuser'"]}
I have also configured Knife locally to use Fiddler as a HTTP proxy so I can inspect Knife HTTP requests and have copied the visible headers as accurately as possible, but naturally I cannot see the canonical header it generated nor the one the server expects.
However, I have confirmed in this way that the hashes I'm generating for the content (empty) and path are the same as Knife is generating.
Here's my code. Loading the RSA private key from PEM format is using an extension method taken from Christian Etter's blog - run out of links sorry.
const string path = "/cookbooks"
const string basePath = "https://chefserver.internal:443";
var timestamp = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssZ");
var method = "GET";
var clientName = "myuser";
var hashedPath = ToBase64EncodedSha1String(path);
var hashedBody = ToBase64EncodedSha1String(String.Empty);
var canonicalHeader = String.Format("Method:{0}\nHashed Path:{1}\nX-Ops-Content-Hash:{2}\nX-Ops-Timestamp:{3}\nX-Ops-UserId:{4}",
method, hashedPath, hashedBody, timestamp, clientName);
var privateKey = File.ReadAllText("C:\\chef\\myuser.private.pem");
string signature;
byte[] rawData;
using (var rsa = new RSACryptoServiceProvider())
{
rsa.PersistKeyInCsp = false;
rsa.LoadPrivateKeyPEM(privateKey);
using (var sha1 = new SHA1CryptoServiceProvider())
{
rawData = rsa.SignData(Encoding.UTF8.GetBytes(canonicalHeader), sha1);
signature = Convert.ToBase64String(rawData);
}
}
var client = new HttpClient();
var message = new HttpRequestMessage();
message.Method = HttpMethod.Get;
message.RequestUri = new Uri(basePath + path);
message.Headers.Add("Accept", "application/json");
message.Headers.Add("Host", "chefserver.internal:443");
message.Headers.Add("X-Chef-Version", "11.12.4");
message.Headers.Add("X-Ops-Timestamp", timestamp);
message.Headers.Add("X-Ops-Sign", "version=1.0;");
message.Headers.Add("X-Ops-Userid", clientName);
message.Headers.Add("X-Ops-Content-Hash", hashedBody);
message.Headers.Add("User-Agent", "Chef Knife/11.4.0 (ruby-1.9.2-p320; ohai-6.16.0; x86_64-darwin11.3.0; +http://opscode.com)");
var currentItem = new StringBuilder();
var i = 1;
foreach (var l in signature)
{
currentItem.Append(l);
if (currentItem.Length == 60)
{
message.Headers.Add("X-Ops-Authorization-" + i, currentItem.ToString());
i++;
currentItem = new StringBuilder();
}
}
message.Headers.Add("X-Ops-Authorization-" + i, currentItem.ToString());
var response = await client.SendAsync(message);
var content = await response.Content.ReadAsStringAsync();
And the helper
private string ToBase64EncodedSha1String(string input)
{
return
Convert.ToBase64String(new SHA1CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(input)));
}
I had a similar problem and found that the .NET crypto libraries did not support what was needed to make this work. I ended up using the BouncyCastle crypto libraries to accomplish this.
I made a simple C# class library that wraps this up. Take a look at that here: https://github.com/mattberther/dotnet-chef-api
I'm not super familiar with C# crypto, but the docs on SHA1CryptoServiceProvider seem to show that is is signing a SHA1 hash. Chef doesn't use signed hashes, you actually need to do an RSA signature on canonicalHeader itself.
SignData(Byte[], Object): Computes the hash value of the specified byte array using the specified hash algorithm, and signs the resulting hash value.
Is there a null or pass-through hash you can use?

Need signature after SAML token in client request

I have a serialized SOAP request message with a SAML token holder-of-key that works against a vendor service. I want to create a demonstration program in C# to produce a similar request. To do this, I want to write a client that creates its own SAML token.
I've got a SAML2 token created successfully from a self signed cert and I am able to associate it to the request using the ChannelFactoryOperations.CreateChannelWithIssuedToken approach (.Net 4.0). Everything is working great but I can't figure out the C# required to place the signature after the assertion and use the SAML token as the signature KeyIdentifier to sign the timestamp. I'm not even sure what I am asking, but it seems like the signature after the token itself should be the easy part. But, the only way I've gotten the SAML to come out in the request is by declaring it of type BearerKey. But BearerKey appears to omit the signature after the Assertion. It seems I want SymmetricKey, but the token "has no keys." How do I make a signature element like this appear after the Assertion?
Here URI="#_1" is referring to the WS-Security timestamp (not shown) above.
Hi folks I can't believe I finally figured all of this out. This code loads up a self signed cert, generates a SAML token and then endorses the message with the SAML token. The problem I was having was with the "token has no keys" error. That was solved by creating an issuerToken and a key and passing that in to the token constructor. See below. I think the most helpful information I found online is this great post here http://devproconnections.com/development/generating-saml-tokens-wif-part-2
X509Certificate2 cert = new X509Certificate2("C:\\Users\\foobar\\desktop\\test.pfx", "test", X509KeyStorageFlags.MachineKeySet);
RSACryptoServiceProvider rsa = cert.PrivateKey as RSACryptoServiceProvider;
RsaSecurityKey rsaKey = new RsaSecurityKey(rsa);
RsaKeyIdentifierClause rsaClause = new RsaKeyIdentifierClause(rsa);
SecurityKeyIdentifier signingSki = new SecurityKeyIdentifier(new SecurityKeyIdentifierClause[] { rsaClause });
SigningCredentials signingCredentials = new SigningCredentials(rsaKey, SecurityAlgorithms.RsaSha1Signature, SecurityAlgorithms.Sha1Digest, signingSki);
Saml2NameIdentifier saml2NameIdentifier = new Saml2NameIdentifier("C=US,O=hi mom,CN=test", new System.Uri("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"));
Saml2Assertion saml2Assertion2 = new Saml2Assertion(saml2NameIdentifier);
saml2Assertion2.SigningCredentials = signingCredentials;
Saml2Subject saml2Subject = new Saml2Subject();
saml2NameIdentifier = new Saml2NameIdentifier("foo#bar.edu", new System.Uri("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"));
saml2Subject.NameId = saml2NameIdentifier;
Saml2SubjectConfirmationData subjectConfirmationData = new Saml2SubjectConfirmationData();
Saml2SubjectConfirmation subjectConfirmation = new Saml2SubjectConfirmation(new Uri("urn:oasis:names:tc:SAML:2.0:cm:holder-of-key"));
subjectConfirmation.SubjectConfirmationData = subjectConfirmationData;
subjectConfirmationData.KeyIdentifiers.Add(signingSki);
saml2Subject.SubjectConfirmations.Add(subjectConfirmation);
saml2Assertion2.Subject = saml2Subject;
Saml2AuthenticationContext saml2AuthCtxt = new Saml2AuthenticationContext(new Uri("urn:oasis:names:tc:SAML:2.0:ac:classes:X509"));
Saml2AuthenticationStatement saml2AuthStatement = new Saml2AuthenticationStatement(saml2AuthCtxt);
saml2AuthStatement.SessionIndex = "123456";
saml2Assertion2.Statements.Add(saml2AuthStatement);
Saml2AttributeStatement saml2AttStatement = new Saml2AttributeStatement();
Saml2Attribute saml2Attribute = new Saml2Attribute("urn:oasis:names:tc:xspa:1.0:subject:subject-id", "foo bar test");
saml2AttStatement.Attributes.Add(saml2Attribute);
saml2Attribute = new Saml2Attribute("urn:oasis:names:tc:xspa:1.0:subject:organization", "urn:oid:"+senderOid);
saml2AttStatement.Attributes.Add(saml2Attribute);
saml2Attribute = new Saml2Attribute("urn:oasis:names:tc:xspa:1.0:subject:organization-id", "urn:oid:" + senderOid);
saml2AttStatement.Attributes.Add(saml2Attribute);
saml2Attribute = new Saml2Attribute("urn:nhin:names:saml:homeCommunityId", "urn:oid:" + senderOid);
saml2AttStatement.Attributes.Add(saml2Attribute);
saml2Attribute = new Saml2Attribute("urn:oasis:names:tc:xacml:2.0:subject:role");
saml2AttStatement.Attributes.Add(saml2Attribute);
saml2Assertion2.Statements.Add(saml2AttStatement);
List<SecurityKey> keyList = new List<SecurityKey>();
keyList.Add(rsaKey);
ReadOnlyCollection<SecurityKey> keys = new ReadOnlyCollection<SecurityKey>(keyList);
X509SecurityToken issuerToken = new X509SecurityToken(cert);
Saml2SecurityToken token2 = new Saml2SecurityToken(saml2Assertion2,keys,issuerToken);
XcpdRespondingGatewaySyncService.RespondingGatewaySyncClient myClient = new XcpdRespondingGatewaySyncService.RespondingGatewaySyncClient("IRespondingGatewaySync2");
CustomBinding customBinding = myClient.Endpoint.Binding as CustomBinding;
SecurityBindingElement element = customBinding.Elements.Find<SecurityBindingElement>();
IssuedSecurityTokenParameters tokenParameters = element.EndpointSupportingTokenParameters.Signed[0].Clone() as IssuedSecurityTokenParameters;
tokenParameters.TokenType = System.IdentityModel.Tokens.SecurityTokenTypes.Saml;
tokenParameters.RequireDerivedKeys = false;
tokenParameters.KeyType = SecurityKeyType.SymmetricKey;
element.EndpointSupportingTokenParameters.Signed.Clear();
element.EndpointSupportingTokenParameters.Endorsing.Add(tokenParameters);
myClient.ChannelFactory.Credentials.SupportInteractive = false;
myClient.ChannelFactory.ConfigureChannelFactory();
XcpdRespondingGatewaySyncService.IRespondingGatewaySync myChannel = ChannelFactoryOperations.CreateChannelWithIssuedToken(myClient.ChannelFactory, token2);

Categories

Resources