Keystore for public key encryption - encryption

I'm encrypting some SAML Assertions with a public key received from a vendor (for use within an IDP initiated workflow).
Is it possible to add that public key to a keystore instead of needing to create a credential, like so:
CertificateFactory cf = CertificateFactory.getInstance("X.509")
Certificate cert = cf.generateCertificate(certFileStream)
BasicX509Credential credential = new BasicX509Credential()
credential.setUsageType(UsageType.ENCRYPTION)
credential.setEntityCertificate((java.security.cert.X509Certificate) cert)
credential.setPrivateKey(null)
The reason I'm interested is because I also have to sign the SAML, using my own private key - which I resolve the credential from my keystore. So it would seem more straight forward to keep them all in the same place.

It looks like your using OpenSAML. To read credentials from a Keystore in OpenSAML you use the KeyStoreCredentialResolver like so.
keystore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream inputStream = new FileInputStream("/path/to/my/JKS");
keystore.load(inputStream, "MyKeystorePassword".toCharArray());
inputStream.close();
Map<String, String> passwordMap = new HashMap<String, String>();
passwordMap.put("MyEntryID"), "MyEntryPassword");
KeyStoreCredentialResolver resolver = new KeyStoreCredentialResolver(keystore, passwordMap);
Criteria criteria = new EntityIDCriteria("MyEntryID");
CriteriaSet criteriaSet = new CriteriaSet(criteria);
X509Credential credential = (X509Credential)resolver.resolveSingle(criteriaSet);
More on using the resolver in OpenSAML on this post on my my blog

You can store the certificate in a keystore and extract it before generating the credential. This code should work
Create keystore with a certificate
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null);
KeyStore.setCertificateEntry(alias, cert);
FileOutputStream out = new FileOutputStream("keystore.jks");
keyStore.store(out, ksPassword);
out.close();
Load certificate from keystore
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load( new FileInputStream("keystore.jks"),ksPassword);
Certificate cert = keystore.getCertificate(alias);

Related

Credentials in GoogleCalendar API with service account (Java)

Right now I need to implement an app to create some google-calendar events but for a service account. I saw examples in docs that create events using AuthorizationCodeInstalledApp (or similar) to create a Credential instance (com.google.api.client.auth.oauth2.Credential) but it works for regular user accounts and not for Service account (I already have the JSON file with key info from my ServiceAccount).
Next lines show what's the ideal for me (of course is not working because Calendar.Builder needs some Credential obj as a third param)
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = GsonFactory.getDefaultInstance();
GoogleCredentials credential = GoogleCredentials.fromStream(new FileInputStream(jsonPath))
.createScoped(Collections.singleton(CalendarScopes.CALENDAR));
Calendar calendar = new Calendar.Builder(HTTP_TRANSPORT, jsonFactory, credential)
.setApplicationName(APPLICATION_NAME)
.build();
How I can get a Credential obj using/from a .JSON key file (service account) to use with Calendar.Builder? or maybe I'm missing something else and/or there is another way to do this?
Thanks!
This sample should get you started make sure to set the UserEmail to the email address of the user on your Workspace domain that you want to delegate to.
One of these should work.
version 1:
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
GoogleCredential credential = GoogleCredential
.fromStream(new FileInputStream(KEY_FILE_LOCATION))
.setServiceAccountUser(userEmail)
.createScoped(CalendarScopes.all());
Version 2:
private GoogleCredential authorize1() {
GoogleCredential credential = null;
HttpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
try {
InputStream jsonFileStream =
DriveSample.class.getClassLoader().getResourceAsStream("client_secrets.json");
GoogleCredential readJsonFile = GoogleCredential
.fromStream(jsonFileStream, httpTransport, JSON_FACTORY).createScoped(DriveScopes.all());
credential = new GoogleCredential.Builder().setTransport(readJsonFile.getTransport())
.setJsonFactory(readJsonFile.getJsonFactory())
.setServiceAccountId(readJsonFile.getServiceAccountId())
.setServiceAccountUser(userEmail)
.setServiceAccountScopes(readJsonFile.getServiceAccountScopes())
.setServiceAccountPrivateKey(readJsonFile.getServiceAccountPrivateKey()).build();
} catch (IOException exception) {
exception.printStackTrace();
}
return credential;
}
here is a link on how to set up delegation. Just make sure to swap out the scope for the google calendar scope.
Perform Google Workspace Domain-Wide Delegation of Authority

How to manually generate an OIDC token signed with a public key?

In my ASP .NET Core Web API, I'm manually generating a token for mocking purposes. However, it appears the token generator assumes the signature has a private key, while that's not the case. The key is public, retrieved from a JWKS. I don't know how to make this clear to the token generator. Can anyone help me?
This is my code:
private async Task<string> GenerateSecurityToken(string _expDate, string _issuer, string _audience, string _wellKnownEndpoint)
{
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(_wellKnownEndpoint, new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration openIdConfig = await configurationManager.GetConfigurationAsync(CancellationToken.None);
SecurityKey securityKey = openIdConfig.SigningKeys.Last();
var tokenDescriptor = new SecurityTokenDescriptor()
{
Subject = new ClaimsIdentity(),
Expires = DateTime.UtcNow.AddMinutes(double.Parse(_expDate)),
Audience = _audience,
Issuer = _issuer,
SigningCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256Signature)
};
var tokenHandler = new JwtSecurityTokenHandler();
SecurityToken token = tokenHandler.CreateToken(tokenDescriptor); // Raises exception
return tokenHandler.WriteToken(token);
}
And this is the raised exception:
System.InvalidOperationException: IDX10638: Cannot create the SignatureProvider, 'key.HasPrivateKey' is false, cannot create signatures.
The identity provider use private key to encrypt token , and when validating token , you can get public key cryptography to sign tokens and verify that they're valid. So you can use a traditional way to get access token from identity provider using delegating flows , rather than use public key to issue access token manually .

Is it possible in a .NET Core application to retrieve a certificate from AWS Certificate Manager and use it in a HttpClient post?

My .Net core application makes a post request to an external web service using HttpClient. The external web service requires a certificate to validate against.
The certificates are installed in AWS and I have an ARN that points to the certificate.
Is it possible to get the certificate programitically from AWS Certificate Manager and use this in my HtppClient, for example this is the code I would use normally to add a certificate but I need to get it from AWS.
private HttpClientHandler HttpClientHandler()
{
var handler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
SslProtocols = SslProtocols.Tls12
};
handler.ClientCertificates.Add(new X509Certificate2("cert.crt")); //TODO: fetch from AWS.
return handler;
}
So, it's possible.
I installed AWSSDK.Core and AWSSDK.CertificateManager from NuGet.
Then, I created a credentials file for AWS, see instructions from Amazon
https://docs.aws.amazon.com/cli/latest/userguide/cli-config-files.html
Next, I used the AmazonCertificateManagerClient to get the certificate.
AmazonCertificateManagerClient client = new AmazonCertificateManagerClient();
var certificates = client.GetCertificateAsync(arn).Result;
I then converted the certificate from string to bytes and then add to the handler.
var handler = new HttpClientHandler{
ClientCertificateOptions = ClientCertificateOption.Manual,
SslProtocols = SslProtocols.Tls12
};
byte[] toBytes = Encoding.ASCII.GetBytes(certificates.Certificate);
var cert = new X509Certificate2(toBytes);
handler.ClientCertificates.Add(cert);
var httpClient = new HttpClient(handler);
Obviously, not production worthy code, hope it helps.
As mentioned by Zack the accepted answer does not work. It does retrieve a certificate out of ACM but it is not usable as a client certificate for HttpClient because it has no private key.
As far as I can tell there is no way to get the private key out of ACM so I ended up putting it in SecretsManager and doing something like:
var certManagerClient = new AmazonCertificateManagerClient();
var awsCert = certManagerClient.GetCertificateAsync(arn).Result;
byte[] awsBytes = Encoding.ASCII.GetBytes(awsCert.Certificate);
var cert = new X509Certificate2(awsBytes);
var secretsManagerClient = new AmazonSecretsManagerClient();
var privateKey = secretsManagerClient.GetSecretValueAsync(new GetSecretValueRequest { SecretId = secretId }).Result.SecretString;
byte[] privateKeyBytes = Convert.FromBase64String(privateKey);
var privateKey = RSA.Create();
privateKey.ImportRSAPrivateKey(new ReadOnlySpan<byte>(privateKeyBytes), out _);
var certWithPrivateKey = cert.CopyWithPrivateKey(privateKey);
And then using certWithPrivateKey in my HttpClientHandler:
var handler = new HttpClientHandler { ClientCertificateOptions = ClientCertificateOption.Manual };
handler.ClientCertificates.Add(certWithPrivateKey);
If you use the AWS SDK you can get certificates using the AmazonCertificateManagerClient. See the AWS SDK documentation for details. (select Amazon.CertificateManager > AmazonCertificateManagerClient)

Certificate authentication of rest api in Azure with https

I have problems with configuring my asp.net web api service to authenticate requests by client certificates
I do the steps describing in Pro ASP.NET Web Api Security:
I create certificates using makecert.exe
makecert.exe -r -n "CN=MobileTradeDataGateway" -pe -sv MobileTradeDataGateway.pvk -a sha256 -cy authority MobileTradeDataGateway.cer and makecert.exe -iv MobileTradeDataGateway.pvk -ic MobileTradeDataGateway.cer -n "CN=DataGateway1" -pe -sv DataGateway1.pvk -a sha256 -sky exchange DataGateway1.cer -eku 1.3.6.1.5.5.7.3.2
I install MobileTradeDataGateway certificate in server Trusted Root Certification Authorities and in client too. Install DataGateway1 in client personal authority.
Configure site to accept certificates and enable. Enable anonymous authentication.
Create DelegatingHandler and add it to messagehandlers collection in mvc to check certificates.
Call web api method
var certStore = new X509Store(StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
var collection = certStore.Certificates.Find(X509FindType.FindByIssuerName, "MobileTradeDataGateway", true);
var cert = collection[0];
certStore.Close();
var messageHandler = new WebRequestHandler();
messageHandler.ClientCertificates.Add(cert);
var client = new HttpClient(messageHandler) { BaseAddress = new Uri("...") };
var res = client.GetAsync("/api/orderuploader?number=5").Result;
.
Everything works fine in my local machine and network where my machine is server.
But when I deploy it to azure cloud service I get null
var cert = request.GetClientCertificate(); // here is null
in my custom delegating handler
Off course I enable IIS to accept certificates and correctelly put certificates in Trusted Root Certification Authorities
Any ideas?
Did you also tried getting your certificate by "Thumbprint".
here is a sample code that tries to read a certificate from certificate store.
private X509Certificate2 FindCertificate()
{
X509Store certificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certificateStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificates = certificateStore.Certificates;
X509Certificate2Collection matchingCertificates = certificates.Find(X509FindType.FindByThumbprint, "CertThumbprint", false);
if (matchingCertificates != null && matchingCertificates.Count > 0)
{
return matchingCertificates[0];
}
throw new ArgumentException("Unable to find a matching certificate in the certificate store. Please modify the search criteria.");
}
this link has more information on how you can read certificate from a web /worker role
Here is the code of my delegatinghandler from web api
public class X509ClientCertificateHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
WebApiEventSource.Log.InvalidHttpsScheme();
return request.CreateResponse(HttpStatusCode.Forbidden);
}
var cert = request.GetClientCertificate(); // here is null!!!
if (cert == null)
{
WebApiEventSource.Log.FailureAuthenticate("certificate is abcent", "", "");
return request.CreateResponse(HttpStatusCode.Unauthorized);
}
var chain =new X509Chain {ChainPolicy = {RevocationMode = X509RevocationMode.NoCheck}};
if (chain.Build(cert) && cert.Issuer.Equals("CN=MobileTradeDataGateway"))
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, cert.Subject.Substring(3))
};
var principal = new ClaimsPrincipal(new[] {new ClaimsIdentity(claims, "X509")});
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
HttpContext.Current.User = principal;
WebApiEventSource.Log.SuccessAuthenticate(cert.SubjectName.Name);
return await base.SendAsync(request, cancellationToken);
}
WebApiEventSource.Log.FailureAuthenticate("certificate is incorrect", cert.IssuerName.Name, cert.SubjectName.Name);
return request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
I think you have missed to upload the certificate to Azure Portal. Please make sure you upload .cer or .pfx certificate to Azure Portal. Let me know if you need help on how to upload etc.

How to instantiate javax.security.X509Certficate object from a p12 certificate (contains certificate + private key)

X509Certificate is only instantiatable using the contents of a certificate (.cer file). How to instantiate this object using a .p12 file that contains both the certificate and the private key?
Here is what you need:
InputStream inStream = new FileInputStream("c:/certificate.p12");
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(inStream, "password".toCharArray());
String alias = ks.aliases().nextElement();
certificate = (X509Certificate) ks.getCertificate(alias);

Resources