Monitoring Kafka SSL Certs Validity - apache-kafka

Imagine you have a simple Kafka Cluster with 3 Brokers. You use the SSL Certs for client authentication and Kafka ACLs. Also the SSL is enabled for inter-broker communication. What would be the recommended way to Monitor the Validity/Expiration of the Certs used?
Thanks in advance!

For now, just have written a small Java app, that does the checks and retrieves the certificate expiring within given amount of days, by scheduled calls of the following method, for each of the used JKS files:
List<X509Certificate> getCertificatesThatExpireWithin(final int minCertsValidityInDays,
final File keystoreFile,final String keyStorePassword) throws MyAppException {
final List<X509Certificate> expiringCerts = new LinkedList<>();
final java.util.Date maxDateTime = java.util.Date.from(java.time.LocalDate.now()
.plusDays(minCertsValidityInDays).atStartOfDay(ZoneId.systemDefault()).toInstant());
try (final FileInputStream is = new FileInputStream(keystoreFile)) {
final KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(is, keyStorePassword.toCharArray());
final Enumeration<String> keystoreAliases = keystore.aliases();
while (keystoreAliases.hasMoreElements()) {
final String alias = keystoreAliases.nextElement();
final Certificate cert = keystore.getCertificate(alias);
if (cert instanceof X509Certificate) {
X509Certificate x509Cert = (X509Certificate) cert;
if (!x509Cert.getNotAfter().after(maxDateTime)) {
expiringCerts.add(x509Cert);
}
}
}
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
LOGGER.error("Can not check the validity of the certificates in " + keystoreFile.getPath() + " due to", e);
throw new MyAppException(
"Can not check the validity of the certificates in " + keystoreFile.getPath() + " due to", e);
}
return expiringCerts;
}

Related

.netcore 3 client certificate with HttpClient

I have a .netcore 3 project (WorkerService Template) which sends JSON data to a REST endpoint. The requests are sent via a HttpClient and configured to use a client certificate which the server requires. The server response is always 200 and HTML characters. According to the server managers the request is redirected to the home page of the web server, because the client machine is being correctly handled with a specific user but no certificate is available. I am using the following code:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("client").ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
X509Certificate2 certificate = GetCertificate(Configuration.CertificateSubjectKeyIdentifier);
handler.ClientCertificates.Add(certificate);
return handler;
}
}
GetCertificate retrieves the certificate from the Certificate Store:
private X509Certificate2 GetCertificate(string subjectIdentifier)
{
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
var collection = store.Certificates;
var certificates = collection.Find(X509FindType.FindBySubjectKeyIdentifier, subjectIdentifier, true);
foreach (var certificate in certificates)
{
if (DateTime.Compare(DateTime.Parse(certificate.GetExpirationDateString()), DateTime.Now) >= 0)
{
Logger.LogInformation($"Loaded X.509 certificate {certificate.Subject} issued by {certificate.Issuer}, valid from {certificate.GetEffectiveDateString()} to {certificate.GetExpirationDateString()}");
return certificate;
}
}
Logger.LogError($"X.509 certificate not loaded: No valid certificate could be found.");
return null;
}
Code which sends a request:
public async Task<ResponseData> PostAsync<T>(string url, T dataToSend)
{
ResponseData result = null;
HttpResponseMessage httpResponseMessage = null;
try
{
var errorHttp = false;
HttpClient httpClient;
using (httpClient = HttpClientFactory.CreateClient("client)) // IHttpClientFactory initialized in ctor
{
HttpContent httpContent;
using (httpContent = CreateJsonHttpContent(dataToSend, MediaType.ApplicationJson)) //build JSON from data
{
httpResponseMessage = await httpClient.PostAsync(url, httpContent).ConfigureAwait(false);
result = BuildResponseData(httpResponseMessage); //writes response data in a class
if (httpResponseMessage?.IsSuccessStatusCode == true)
{
result.Content = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
}
else
{
errorHttp = true;
}
if (errorHttp)
{
var httpRequestException = new HttpRequestException($"The http request to {url} was not successful.");
Logger.LogError($"{httpRequestException.Message} : {httpRequestException.InnerException}");
Logger.LogError(httpRequestException.StackTrace);
}
}
}
}
catch (SocketException socketException)
{
Logger.LogError($"{socketException.Message} : {socketException.InnerException}");
result = new ResponseData(socketException);
}
catch (WebException wex)
{
Logger.LogError($"{wex.Message} : {wex.InnerException}");
if (wex.Status == WebExceptionStatus.ConnectFailure || wex.Status == WebExceptionStatus.Timeout)
{
Logger.LogError($"Cannot connect to the rest service : {WebExceptionStatus.Timeout}");
}
}
catch (Exception ex)
{
LogException(ref ex);
result = new ResponseData(ex);
}
finally
{
httpResponseMessage?.Dispose();
}
return result;
}
The class which uses the PostAsync method is also registered in the ServiceCollection. Any ideas what could be wrong here? Could it also be that the certificate is not being handled correctly on the server side?
My strong suspection is the misconfiguration on client (your) end. Your application reads for certificate from LocalMachine store. By default, only local administrator and SYSTEM account can read/use private keys for certificates installed in LocalMachine store.
Either, install the certificate in CurrentUser store of a user account under which the client application is running, or explicitly grant private key permissions to user account under which the client application is running. To do this:
Open Certificates MMC snap-in under LocalMachine context (certlm.msc)
Expand Personal\Certificates
Select desired certificate, right-click and then Manage Private Keys menu item.
Grant Read permissions to user account under which the client application is running.
In this case, you don't need to modify your code or move certificate between stores.

keyStore.getKey() returns NULL

I want to sign a PDF with a self generated certificate. In this process I need a keystore and private key. The signing will be made with PDFBox by using the class CreateSignature()
To generate a keyStore with a self generated certificate I am using this:
public KeyStore generateSampleKeyStoreWith509Certificate() throws KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException, UnrecoverableEntryException {
X509Certificate cert;
PrivateKey caKey;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGenerator.initialize(1024, new SecureRandom());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
caKey = keyPair.getPrivate();
Date notBefore = new Date();
Date notAfter = new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 365);
SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
X509v3CertificateBuilder newGen = new X509v3CertificateBuilder(new X500Name(issuer), serial, notBefore,
notAfter, new X500Name(subject), spkInfo);
ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider("BC")
.build(caKey);
X509CertificateHolder certHolder = newGen.build(sigGen);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream is1 = new ByteArrayInputStream(certHolder.getEncoded());
cert = (X509Certificate) cf.generateCertificate(is1);
is1.close();
} catch (OperatorCreationException | CertificateException | IOException | NoSuchProviderException
| NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, password);
keyStore.setCertificateEntry("SelfSigned", cert);
return keyStore;
}
The certificate is entered correctly, but shouldn't there be a key, too? Or am I wrong that the keystore should be holding a key?
I'm just figuring this sibject, so I'm thankful of every bit of help.
X509Certificate[] certChain = new X509Certificate[1];
certChain[0] = cert;
keyStore.setKeyEntry("SelfSigned",caKey, password, certChain);
Adding the above code, at the bottom, enters the privateKey previously created to the keystore. It seems most examples on the internet assume loading a keystore with privatekey already entered before.

How to validate SAML assertion signatures

How to validate SAML assertion signatures?
for (Assertion assertion : samlResponse.getAssertions()) {
try {
if (assertion.getSignature() != null) {
Optional<X509Certificate> x509Certificate = assertion.getSignature().getKeyInfo().getX509Datas()
.stream()
.findFirst()
.map(x509Data -> x509Data.getX509Certificates()
.stream()
.findFirst()
.orElse(null)
);
if (x509Certificate.isPresent()) {
BasicX509Credential credential = new BasicX509Credential();
credential.setEntityCertificate(KeyInfoHelper.getCertificate(x509Certificate.get()));
// what pub key credential to use here?
SignatureValidator validator = new SignatureValidator(credential);
validator.validate(assertion.getSignature());
}
}
} catch (ValidationException | CertificateException e) {
throw new SAMLException(e.getMessage(), e);
}
}
Basically what to put in new SignatureValidator(credential)
As far as I understand, A SAML assertion with KeyInfo supplied and a X809 cert should at least validate (SAML: Why is the certificate within the Signature?)
I also have an x509 cert from the idps metadata which I guess should general be used if there is no x509 cert in the assertion or within a trust chain (?)
Basically neither the x509 cert in the assertion nor the cert from the idp metadata seems to work. What am I missing here?
Turned out I did everything correctly.
When printing an opensaml object xml you should NOT use the following code:
public static String xmlObjectToString(XMLObject xmlObject) {
try {
Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(xmlObject);
StringWriter sw = new StringWriter();
Element authDOM = marshaller.marshall(xmlObject);
toString(sw, authDOM);
return sw.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void toString(StringWriter rspWrt, Element authDOM) throws ParserConfigurationException, TransformerException {
DOMSource domSource = new DOMSource(authDOM);
StreamResult result = new StreamResult(rspWrt);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
}
The above code changes some internal states of the original object
Instead go for
org.opensaml.xml.util.XMLHelper.prettyPrintXML(message.getDOM())

AES Encryption and decryption using jks file

I have one small doubt as i am new to AES.
I encrypted a string using one certificate with some password lets say , 'xxx'.
Now i duplicated the certificate by changing the password of it.
When i try to decrypt the encrypted string with the duplicated cert, it says Bad padding exception.Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
However, when i use the original cert, it decrypts properly.
Could anyone please guide me on it?
public SecretKey retrieveKey(String password, byte[] certFile) throws Exception {
try {
String alias = null;
certPass = password;
char[] pass = certPass.toCharArray();
KeyStore keyStore = KeyStore.getInstance("jceks");
InputStream inputStream = new ByteArrayInputStream(certFile);
keyStore.load(inputStream, pass);
Enumeration enumeration = keyStore.aliases();
while (enumeration.hasMoreElements()) {
alias = (String) enumeration.nextElement();
}
Certificate cert = keyStore.getCertificate(alias);
Key key = cert.getPublicKey();
aesSecretKey = new SecretKeySpec(key.getEncoded(), algorithm);
byte[] encoded = aesSecretKey.getEncoded();
byte[] encryptionKey = Arrays.copyOfRange(encoded, encoded.length - 16, encoded.length);
aesSecretKey = new SecretKeySpec(encryptionKey, algorithm);
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw e;
}
return aesSecretKey;
}
You should use RSA to wrap / unwrap the AES key. The public key is not identical to the AES key, so the following code is certainly incorrect:
Key key = cert.getPublicKey();
aesSecretKey = new SecretKeySpec(key.getEncoded(), algorithm);

WSO2IS JWT access token

I am trying get a JWT access token from WSO2 IS. I followed instructions from msf4j Oauth2 Security Sample, and managed to get a JWT acces token by resource owner password grant type.
but I have problem authenticating the token externally.
it seems that the token had not been signed by the default "wso2carbon.jks".
also, my claim configurations in the "service providers" was not reflected in jwt content
so my questions: how to config the JWT signing certificate in WSO2IS?
and also:
How to manipulate the claims in the JWT?
I do not want to turn to the "introspect" endpoint out of performance concern, and my strategy is to just trust the IS, only to make sure(locally) of the authenticity of the JWT token
please advise
thanks
You can follow [1] to get JWT Access Tokens(Self contained access tokens) using WSO2 Identity Server
[1] https://medium.com/#hasinthaindrajee/self-contained-access-tokens-with-wso2-identity-server-82111631d5b6
well, it seems to be my own fault.
I had been using the jose4j JWT package, and kept getting verification failed message.
after further checking into the msf4j implementation, I switched over to nimbus-jose-jwt JWT package, and got it done,
below are my implementation.
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
public class JwtParser {
private static final String KEYSTORE = System.getProperty("javax.net.ssl.trustStore");
private static final String KEYSTORE_PASSWORD = System.getProperty("javax.net.ssl.trustStorePassword");
private static Map<String, JWSVerifier> verifiers = getVerifiers();
public static JWTClaimsSet verify(String jwt) throws Exception {
SignedJWT signedJWT = SignedJWT.parse(jwt);
if (!new Date().before(signedJWT.getJWTClaimsSet().getExpirationTime())) {
new Exception("token has expired");
}
boolean notYet = true;
for(Iterator<JWSVerifier> it = verifiers.values().iterator(); notYet && it.hasNext();){
JWSVerifier verifier = it.next();
notYet = !signedJWT.verify(verifier);
}
if(notYet){
throw new Exception("token verification failed");
}
JWTClaimsSet claims = signedJWT.getJWTClaimsSet();
if (claims == null) {
// Do something with claims
throw new Exception("non valid payload in token, failed");
}
return claims;
}
private static Map<String, JWSVerifier> getVerifiers(){
Map<String, JWSVerifier> verifiers = new HashMap<>();
try (InputStream inputStream = new FileInputStream(KEYSTORE)) {
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(inputStream, KEYSTORE_PASSWORD.toCharArray());
Enumeration<String> aliases = keystore.aliases();
while(aliases.hasMoreElements()){
String alias = aliases.nextElement();
if(!keystore.isCertificateEntry(alias)){
continue;
}
Certificate cert = keystore.getCertificate(alias);
if(cert == null){
continue;
}
PublicKey key = cert.getPublicKey();
verifiers.put(alias, new RSASSAVerifier((RSAPublicKey)key));
}
}catch(KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e){
//TODO: report the exception
}
return verifiers;
}
}