How to store only public key in keystore using java code with corresponding alias? - java-security

I had created keystore ,i want to store only publickey in keystore , and don't want to store private key.because private key will be in client device and he will share public key using rest API.
Also how should i keep multiple public key in one keystore.with different aliases
If some one has sample code,or sample link it will be great helpful for me.

It can't be done, at least not in the format of a raw java.security.PublicKey. You can only store 3 types of entries in a JKS keystore: PrivateKeyEntry (for asymmetrical private keys), SecretKey (for symmetrical secret keys) and TrustedCertificateEntry (for "public" keys). They all implement the java.security.KeyStore.Entry interface.
The bottom line is: you need to associate your public key with a certificate, which you can create, and then store the certificate in the keystore as a separate entry.
Creating certificates is a bit tricky, but an example can be found here: Creating an X509 Certificate in Java without BouncyCastle?

This may be closer to what you are looking for to call a REST web service with SSL taken from here Importing PEM certificate into Java KeyStore programmatically :
private static SSLContext createSSLContext(String certString) throws IOException {
try {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
ByteArrayInputStream inStream = new ByteArrayInputStream(certString.getBytes(StandardCharsets.UTF_8));
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(inStream);
KeyStore store = KeyStore.getInstance("JKS");
store.load(null);
store.setCertificateEntry("certificate", cert);
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(store, "".toCharArray())
.build();
return sslContext;
} catch (IOException | NoSuchAlgorithmException | CertificateException | KeyStoreException |
KeyManagementException | UnrecoverableKeyException e) {
throw new IOException(e);
}
}

Related

How to access a confluent schema registry server secured with a password using Spring cloud stream?

I'm using spring cloud stream alongside Aiven's schema registry which uses confluent's schema registry. Aiven's schema registry is secured with a password. Based on these instructions, these two config parameters need to be set to successfully access the schema registry server.
props.put("basic.auth.credentials.source", "USER_INFO");
props.put("basic.auth.user.info", "avnadmin:schema-reg-password");
Everything is fine when I only use vanilla java's kafka drivers, but if I use Spring cloud stream, I don't know how to inject these two parameters. At the moment, I'm putting "basic.auth.user.info" and "basic.auth.credentials.source" under "spring.cloud.stream.kafka.binder.configuration" in the application.yml file.
Doing this, I'm getting "401 Unauthorized" on the line where the schema wants to get registered.
Update 1:
Based on 'Ali n's suggestion, I updated the way SchemaRegistryClient's bean was configured so that it becomes aware of the SSL context.
#Bean
public SchemaRegistryClient schemaRegistryClient(
#Value("${spring.cloud.stream.schemaRegistryClient.endpoint}") String endpoint) {
try {
final KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream(
new File("path/to/client.keystore.p12")),
"secret".toCharArray());
final KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream(
new File("path/to/client.truststore.jks")),
"secret".toCharArray());
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLContext sslContext = SSLContextBuilder
.create()
.loadKeyMaterial(keyStore, "secret".toCharArray())
.loadTrustMaterial(trustStore, acceptingTrustStrategy)
.build();
HttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
httpClient);
ConfluentSchemaRegistryClient schemaRegistryClient = new ConfluentSchemaRegistryClient(
new RestTemplate(requestFactory));
schemaRegistryClient.setEndpoint(endpoint);
return schemaRegistryClient;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
This helped getting rid of the error on app's startup and registered the schema. However, whenever the app wanted to push a message to Kafka, a new error was thrown again. Finally this was also fixed by mmelsen's answer.
I ran into the same problem as the situation I was in was to connect to a secured schema registry hosted by aiven and secured by basic auth. In order for me to make it work I had to configure the following properties:
spring.kafka.properties.schema.registry.url=https://***.aiven***.com:port
spring.kafka.properties.basic.auth.credentials.source=USER_INFO
spring.kafka.properties.basic.auth.user.info=username:password
the other properties for my binder are:
spring.cloud.stream.binders.input.type=kafka
spring.cloud.stream.binders.input.environment.spring.cloud.stream.kafka.binder.brokers=https://***.aiven***.com:port <-- different from the before mentioned port
spring.cloud.stream.binders.input.environment.spring.cloud.stream.kafka.binder.configuration.security.protocol=SSL
spring.cloud.stream.binders.input.environment.spring.cloud.stream.kafka.binder.configuration.ssl.truststore.location=truststore.jks
spring.cloud.stream.binders.input.environment.spring.cloud.stream.kafka.binder.configuration.ssl.truststore.password=secret
spring.cloud.stream.binders.input.environment.spring.cloud.stream.kafka.binder.configuration.ssl.keystore.type=PKCS12
spring.cloud.stream.binders.input.environment.spring.cloud.stream.kafka.binder.configuration.ssl.keystore.location=clientkeystore.p12
spring.cloud.stream.binders.input.environment.spring.cloud.stream.kafka.binder.configuration.ssl.keystore.password=secret
spring.cloud.stream.binders.input.environment.spring.cloud.stream.kafka.binder.configuration.ssl.key.password=secret
spring.cloud.stream.binders.input.environment.spring.cloud.stream.kafka.binder.configuration.value.deserializer=io.confluent.kafka.serializers.KafkaAvroDeserializer
spring.cloud.stream.binders.input.environment.spring.cloud.stream.kafka.streams.binder.autoCreateTopics=false
what actually happens is that Spring cloud stream will add the spring.kafka.properties.basic* to the DefaultKafkaConsumerFactory and that will add the config to the KafkaConsumer. At some point during the initialization of the spring kafka, a CachedSchemaRegistryClient is created that is provisioned with these properties. This Client contains a method called configureRestService that will check if a map of properties contains "basic.auth.credentials.source". As we provide this through the spring.kafka.properties it will find this property and will take care of creating the appropriate headers when accessing the schema registry's endpoint.
hope this works out for you as well.
I'm using spring cloud version Greenwich.SR1, spring-boot-starter 2.1.4.RELEASE, avro-version 1.8.2 and confluent.version 5.2.1
The binder configuration only handles well-known consumer and producer properties.
You can set arbitrary properties at the binding level.
spring.cloud.stream.kafka.binding.<binding>.consumer.configuration.basic.auth...
Since Aiven uses SSL for Kafka security protocol, it is required to use certificates for the authentication.
You can follow this page to understand how it works. In the nutshell, you need to run the following command to generate certificates and import them:
openssl pkcs12 -export -inkey service.key -in service.cert -out client.keystore.p12 -name service_key
keytool -import -file ca.pem -alias CA -keystore client.truststore.jks
Then you can use the following properties to make use of the certificates:
spring.cloud.stream.kafka.streams.binder:
configuration:
security.protocol: SSL
ssl.truststore.location: client.truststore.jks
ssl.truststore.password: secret
ssl.keystore.type: PKCS12
ssl.keystore.location: client.keystore.p12
ssl.keystore.password: secret
ssl.key.password: secret
key.serializer: org.apache.kafka.common.serialization.StringSerializer
value.serializer: org.apache.kafka.common.serialization.StringSerializer

How to define connection resource in terraform with key inline

I am using the connection resource in terraform template
connection {
user = "ubuntu"
private_key = "${file("test.pem")}"
agent = "false"
timeout = "30s"
}
Instead of reading the key from the file I know we can paste the key contents directly but what about the line breaks in the key string. How to paste the contents inline?
Although keeping private key information directly inside configuration is not a best-practice, it is possible to use the "heredoc" multi-line string style to include multi-line strings:
connection {
user = "ubuntu"
private_key = <<-EOK
-----BEGIN RSA PRIVATE KEY-----
....
-----END RSA PRIVATE KEY-----
EOK
agent = "false"
timeout = "30s"
}
The EOK string here is an arbitrary marker chosen because it does not exist in the key, and is intended to stand for "End of key". You can choose any label you like as long as the introducer and the end marker match.
If the machine that is being connected to is created within the same Terraform configuration as the one where it is being provisioned (which is usually the case) an alternative is to generate dynamically a key at creation time, thus avoiding the need to place a literal key in the configuration. The tls_private_key resource can be used to do this:
resource "tls_private_key" "example" {
algorithm = "RSA"
}
resource "some_compute_resource" "example" {
# question didn't specify which provider is in use, so this is a generalized example
public_key = "${tls_private_key.example.public_key_openssh}"
connection {
user = "ubuntu"
private_key = "${tls_private_key.example.private_key_pem}"
agent = false
timeout = "30s"
}
}
In this case, the generated private key is saved as part of the Terraform state rather than as part of the configuration. This means the configuration does not contain any sensitive information and can thus be more freely shared, but it is important to ensure that the state file is stored securely to prevent unauthorized access to the created instance.

How to resolve error Salt must be 8 bytes long

I am writing a program to sign a pdf using certificate (pfx file). For few of the certificates I am getting below exception.
java.security.InvalidAlgorithmParameterException: Salt must be at least 8 bytes long
This happens when I execute the below code.
Keystore ks = KeyStore.getInstance("pkcs12");
I am getting an exception in the below java file at line number 123.
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/com/sun/crypto/provider/HmacPKCS12PBESHA1.java?av=h
Your keystore has one or more certificate(s) that has a salt length which is less than 8. The crypto program requires atleast 8 bytes.
I would recommend creating a new keystore with just the one certificate that you need and try signing with that.
I resolved the exception using pkcs12-DEF keystore. I have added my code lines below.
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
KeyStore ks = KeyStore.getInstance("pkcs12-DEF");
Earlier I had not added BountyCastleProvider to Security, because of which I was not able to get instance of pkcs12-DEF keystore.
Apart from this I have also downloaded jar files from http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html location and replaced it with jar files present in Java\Jdk1.7\jre\lib\security. These are JCE 7 Unlimited strength policy files.

Certificate in HiveMQ ClientData object

I'm currently digging into HiveMQ Plugin development. I developed custom functionality based on AfterLoginCallback. I configured a working TLS connection and I'm able to connect with the clients certificate.
mosquitto_pub.exe -t test -m "testMessage" --cafile myCertificates/hivemq-server-cert.pem --cert myCertificates/sender.crt --key myCertificates/sender.key -p 8883"
However, when I debug the AfterLoginCallback code I find that my "ClientData -> certificate" is "null" throwing a IllegalStateExcpetion when accessed.
[INFO] java.lang.IllegalStateException: Optional.get() cannot be called on an absent value
[INFO] at com.google.common.base.Absent.get(Unknown Source)
[INFO] at mycode.hivemq.plugins.first_plugin.callbacks.AfterLoginCallbackTest.afterSuccessfulLogin(AfterLoginCallbackTest.java:33)
Can anyone explain please, why the certificate is null?
Thanks,
Lomungo
In the callback to check the credentials the clientData must be handled as ClientCredentialData
Here is an example:
public class AuthorizationCallback implements OnAuthenticationCallback, OnAuthorizationCallback {
#Override
public Boolean checkCredentials(#NotNull final ClientCredentialsData clientData) throws AuthenticationException {
//Throw out clients which didn't provide a client certificate
if (!clientData.getCertificate().isPresent()) {
log.debug("Client {} didn't provide a client certificate. Disconnecting client", clientData.getClientId());
throw AuthenticationExceptions.WRONG_CERTIFICATE;
}
final Certificate certificate = clientData.getCertificate().get().certificate();
...
}
}
Hope that helps!

Rex and identity files

I'm trying to configure a fi-ware cloud instance using Rex. What these instances (and probably other OpenStack-based systems) prove is a "identity file", a single private key that you can use to connect to them. I have been using variations of this:
user "root";
private_key "/home/jmerelo/.ssh/jj-iv.pem";
public_key "/home/one/public/key.dsa";
key_auth;
group fiware => "130.206.x.y";
desc "Install git";
task "git", group => "fiware", sub {
install "git";
};
where the private key is the one provided by fi-ware, and the public key is, well, whatever I thought of, or nothing.
If no public key is provided, error is
[2014-11-30 11:45:45] WARN - Error running task/batch: No public_key file defined. at /home/jmerelo/perl5/perlbrew/perls/perl-5.20.0/lib/site_perl/5.20.0/Rex/Task.pm line 621.
at /home/jmerelo/perl5/perlbrew/perls/perl-5.20.0/lib/site_perl/5.20.0/Rex/TaskList/Base.pm line 273.
which is quite obviously true. But if I try other public keys, error is:
[2014-11-30 11:48:37] WARN - Error running task/batch: Wrong username/password or wrong key on 130.206.127.211. Or root is not permitted to login over SSH. at /home/jmerelo/perl5/perlbrew/perls/perl-5.20.0/lib/site_perl/5.20.0/Rex/TaskList/Base.pm line 273.
Using
ssh -i ~/.ssh/jj-iv.pem root#130.206.x.y
connects correctly to the instance. So maybe the question is "Can Rex use a single private key to connect to a host?"
Finally, I generated a public key from the private key using, as suggested by the documentation,
$ sshkey-gen -y -f /path/to/your/private.key >public.key
and then using that public.key in the Rexfile