"TLS_RSA_WITH_AES_128_CBC_SHA256" cipher suite is supported by java 8 default providers. Ref - https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html#SunJSSEProvider.
Also I have following program to verify that.
But when I try to get the cipher for the same algorithm it gives error.
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
public class CipherSuitesInfoGenerator {
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException {
SSLContext context = SSLContext.getDefault();
SSLSocketFactory sf = context.getSocketFactory();
String[] cipherSuites = sf.getSupportedCipherSuites();
String cipherName = "TLS_RSA_WITH_AES_128_CBC_SHA256";
for (String s : cipherSuites) {
if (s.equals(cipherName)) {
System.out.println(cipherName + " is supported");
try {
Cipher cipher = Cipher.getInstance(cipherName);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
}
}
}
}
The output is:
TLS_RSA_WITH_AES_128_CBC_SHA256 is supported
Cannot find any provider supporting TLS_RSA_WITH_AES_128_CBC_SHA256
A ciphersuite is something that is used internally in a JSSE provider, it defines the primitives used within the TLS protocol. It's not a Cipher, a Cipher instance in Java represents one primitive used for encryption/decryption such as AES or RSA.
Related
I have written a code in beanshell for encryption and decryption in Jmeter but somehow its not working. I am getting error : In file: inline evaluation of: ``import java.security.InvalidAlgorithmParameterException; import java.security.In . . . '' Encountered "}" at line 19, column 82.
I have added the crypto jar in test plan but issue persists.
Attached is the code.
Working :
I am generating a random string(R1) and encryption it with a public key using RSA algo.
Using the key R1, I need to encrypt the request body using AES algo, CBC mode, PKCS7Padding.
package com.sample.feedbackrating;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.UUID;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Crypto {
public static IvParameterSpec generateIv() {
byte[] iv = new byte[16];
return new IvParameterSpec(iv);
}
public static String encrypt(String algorithm, String input, String secretKey, IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException
{
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, new
SecretKeySpec(secretKey.getBytes(), "AES"), iv);
byte[] cipherText = cipher.doFinal(input.getBytes());
return Base64.getEncoder().encodeToString(cipherText);
}
public static String decrypt(String algorithm, String cipherText, String secretKey, IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException,InvalidAlgorithmParameterException, InvalidKeyException,BadPaddingException, IllegalBlockSizeException
{
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretKey.getBytes(), "AES"), iv);
byte[] plainText = cipher.doFinal(Base64.getDecoder().decode(cipherText));
return new String(plainText);
}
public static String generateSecretKey() {
return UUID.randomUUID().toString().replace("-", "");
}
}
Encountered "}" is a syntax error so double check your Beanshell script for eventual missing semicolons or closing brackets or whatever. You can use online lint tool like this one to see where exactly the problem is
In general I cannot reproduce your issue:
So it's either a copy-paste issue or a Beanshell-related problem, be aware that starting from JMeter 3.1 you're supposed to use JSR223 Test Elements and Groovy language for scripting, especially for "heavy" tasks like cryptographic operations.
java version 1.8
import java.io.ByteArrayInputStream;
import java.security.KeyFactory;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.interfaces.RSAPublicKey;
import java.util.Base64;
/**
*
* #author user
*/
public class Test {
public static void main(String[] args) throws Exception {
String publicKeyB64 = "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";
String data = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjU2MzU4ODUyMzRCOTI1MkRERTAwNTc2NkQ5RDlGMjc2NTY1RjYzRTIiLCJ4NXQiOiJWaldJVWpTNUpTM2VBRmRtMmRueWRsWmZZLUkiLCJ0eXAiOiJKV1QifQ.eyJhcHBjdHhzZW5kZXIiOiIwMDAwMDAwMi0wMDAwLTBmZjEtY2UwMC0wMDAwMDAwMDAwMDBAOGU5MDAzNTgtOWJjMC00YmQyLTlmMzQtNDU5OTY3ODk5MDVkIiwiaXNicm93c2VyaG9zdGVkYXBwIjoiVHJ1ZSIsImFwcGN0eCI6IntcIm1zZXhjaHVpZFwiOlwiYTVkYWY0YzItZTdiYS00NDE5LTljYmUtYjQ4NWE2ZTNiNGQ1XCIsXCJ2ZXJzaW9uXCI6XCJFeElkVG9rLlYxXCIsXCJhbXVybFwiOlwiaHR0cHM6Ly9vdXRsb29rLm9mZmljZTM2NS5jb206NDQzL2F1dG9kaXNjb3Zlci9tZXRhZGF0YS9qc29uLzFcIn0iLCJuYmYiOjE1OTMwMjA4MzAsImV4cCI6MTU5MzA0OTYzMCwiaXNzIjoiMDAwMDAwMDItMDAwMC0wZmYxLWNlMDAtMDAwMDAwMDAwMDAwQDhlOTAwMzU4LTliYzAtNGJkMi05ZjM0LTQ1OTk2Nzg5OTA1ZCIsImF1ZCI6Imh0dHBzOi8vbWFpbGFwcC5mb3JjZS5jb20vY2xpZW50cy9tYWlsYXBwL2FjdGl2YXRpb24ifQ";
String signature = "oifeWN-wnci-WCS6XPLC-74ynG9rmXdePVfMMxX9WsAXOK5tvRpNmNsPqu9bllufAIUhgpNIYtHEB39-_o2pzpugyfkohLJfPovEQa-1X8zzudjxY3j5tRyISzVuwL9l6zROhJIdOvXsoarzcuQ38j492H7ol7auTvfDI4Mwu4Ri_e2rbiFYWsYU36Htj39DEiOvpQ12z7Onu-Yrj1inICTDtJsOdv9HWrO2nZ5IwRdl45Tt0Ks3ZrBeStleNVCXQdU_xKvYtbO02fUfgkFrIzEUZYHgXbK8sE2m0phmeU7Wp-KdW4jVVl9yLQtB98_FJJfw9tI4Oys2NBBY2sLyhQ";
RSAPublicKey secret = null;
try {
secret = null;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(new ByteArrayInputStream(Base64.getMimeDecoder().decode(publicKeyB64)));
secret = (RSAPublicKey) cert.getPublicKey();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
KeyFactory kf = KeyFactory.getInstance("RSA");
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initVerify(secret);
privateSignature.update(data.getBytes("UTF-8"));
System.out.println(privateSignature.verify(Base64.getMimeDecoder().decode(signature.getBytes())));
}
}
Getting the following exception:
Exception in thread "main" java.security.SignatureException: Signature length not correct: got 248 but was expecting 256
at sun.security.rsa.RSASignature.engineVerify(RSASignature.java:211)
at java.security.Signature$Delegate.engineVerify(Signature.java:1394)
at java.security.Signature.verify(Signature.java:771)
at com.ciphercloud.plugin.salesforce.rewriters.Test.main(Test.java:35)
One more question:
Earlier, I used the Base64.getDecoder().decode(signature.getBytes())) but it's throwing an error, so I changed to Base64.getMimeDecoder().decode(signature.getBytes())). May I know why it faild with Base64.getDecoder().decode()?
The problem here is the wrong decoding.
Neither Mime (Base64.getMimeDecoder().decode()) nor Base64 (Base64.getDecoder().decode()) are correct.
JWT uses Base64url encoding, not Base64. The difference are the - and _ characters, which are used in base64url to replace the + and / of the Base64 standard. Also the padding = is omitted in Base64url encoding.
The correct way to decode the JWT header, payload and signature is to use the Base64url decoder:
Base64.getUrlDecoder().withoutPadding().decode()
I have been trying to convert a Java class into DLL using IKVM.
IKVM converts that easily without errors. But while running it in VS, it throws the following exceptions:
System.TypeInitializationException: 'The type initializer for 'sun.security.jca.Providers' threw an exception.
**1 of 2 Inner Exceptions** : TypeInitializationException: The type initializer for 'java.security.Security' threw an exception.
**2 of 2 Inner Exceptions** : - InnerException {"Method not found: 'Void System.IO.FileStream..ctor(System.String, System.IO.FileMode, System.Security.AccessControl.FileSystemRights, System.IO.FileShare, Int32, System.IO.FileOptions)'."} System.Exception {System.MissingMethodException}.
I can't understand the reason of these Exceptions.
My Java Code is :
package aesgzip;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.Scanner;
public class AESGzip {
private static final String key = MyKeyString;
private static final String IV = MyIV;
public static byte[] decrypt(byte[] encrypted) {
try {
IvParameterSpec IVSpec = new IvParameterSpec(IV.getBytes("UTF-8"));
SecretKeySpec KEYSpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(2, KEYSpec, IVSpec);
return cipher.doFinal(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}
My C# Code is :
using System;
using aesgzip;
namespace AESTest
{
public partial class Form1 : Form
{
public Form1()
{
byte[] b = Convert.FromBase64String(myBASE64String);
byte[] x = AESGzip.decrypt(b);
Console.WriteLine(x);
}
}
}
I wrote a client/server Java program using ServerSocket and Socket objects. I then modified the code to use SSLServerSocket and 'SSLSocket` however I am getting different Exceptions thrown including:
javax.net.ssl.SSLHandshakeException: no cipher suites in common
I am hoping to do as much programmatically as I can. I am also okay with self signed certificates.
One tutorial I followed suggested creating a certificate with the keytool java application, then moving that file into your java project. I have done that with the terminal command keytool -genkey -alias zastore -keyalg RSA -keystore za.store. I assigned the password to be password.
I then call the function System.setProperty in hopes of the SSLSockets working but it still does not.
Here is my server code
public class Server implements Runnable
{
private SSLServerSocket serverSocket;
private int portNumber;
private Thread acceptThread;
private LinkedList<Connection> connections;
private ConnectionListener connectionListener;
public Server(int port, ConnectionListener connectionListener)
{
this.connectionListener = connectionListener;
portNumber = port;
connections = new LinkedList<Connection>();
try
{
System.setProperty("javax.net.ssl.trustStore", "za.store");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
SSLServerSocketFactory sslssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
serverSocket = (SSLServerSocket) sslssf.createServerSocket(portNumber,15);
}
catch (IOException e)
{
e.printStackTrace();
}
}
public void startListening()
{
acceptThread = new Thread(this);
acceptThread.start();
}
public void stopListening()
{
for(Connection c:connections)
{
c.stopListeningAndDisconnect();
}
try
{
serverSocket.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void run()
{
try
{
while(true)
{
SSLSocket s = (SSLSocket) serverSocket.accept();
Connection c = new Connection(s,connectionListener);
connections.add(c);
System.out.println("New Connection Established From"+s.getInetAddress().toString());
}
}
catch(java.net.SocketException e)
{
System.out.println("Listening thread terminated with exception.");
}
catch(IOException e)
{
e.printStackTrace();
}
}
public void removeConnection(Connection c)
{
connections.remove(c);
}
public void printConnections()
{
System.out.println("Number of connections "+connections.toString());
for(int i=0; i<connections.size(); i++)
{
System.out.println(connections.toString());
}
}
}
And then a snipbit of my client code that connects when a button is pressed:
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == connect)
{
try
{
System.setProperty("javax.net.ssl.trustStore", "za.store");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
SSLSocketFactory sslsf = (SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket s = (SSLSocket)sslsf.createSocket(ipBox.getText(), Integer.parseInt(portBox.getText()));
Connection c = new Connection(s,parent);
parent.connectionSuccessful(c);
}
catch (NumberFormatException e1)
{
JOptionPane.showMessageDialog(this, "Error! Port number must be a number", "Error", JOptionPane.ERROR_MESSAGE);
}
catch (UnknownHostException e1)
{
JOptionPane.showMessageDialog(this, "Error! Unable to find that host", "Error", JOptionPane.ERROR_MESSAGE);
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
}
One Stackoverflow article suggested that my server "doesn't have a certificate." I don't know what that means or how to go about getting one and locating it in the right place.
The following error may come due to various reasons:
javax.net.ssl.SSLHandshakeException: no cipher suites in common
The points to check while debugging:
The keystore and truststore are correctly loaded and used to create the socket connections
The certificate is compatible with the enabled cipher suites
At least one common cipher suite should be enabled in the client and the server and that cipher suite should be also compatible with the certificate
For e.g. in the following example, I am using Java 8 with the default set of cipher suites. The certificate I have generated is using ECDSA and SHA384, and hence when the TLS connection is established between the server and the client, I can see the negotiated cipher suite is TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 by enabling the debug (System.setProperty("javax.net.debug", "ssl");).
Following is a working example:
As a first step, a key pair and a certificate need to be created. For testing purpose, let's create a self-signed certificate and let's use the same certificate for both the server and the client:
keytool -genkeypair -alias server -keyalg EC \
-sigalg SHA384withECDSA -keysize 256 -keystore servercert.p12 \
-storetype pkcs12 -v -storepass abc123 -validity 10000 -ext san=ip:127.0.0.1
Let's now create the server:
package com.sapbasu.javastudy;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Objects;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManagerFactory;
/*
* keytool -genkeypair -alias server -keyalg EC \
* -sigalg SHA384withECDSA -keysize 256 -keystore servercert.p12 \
* -storetype pkcs12 -v -storepass abc123 -validity 10000 -ext san=ip:127.0.0.1
*/
public class TLSServer {
public void serve(int port, String tlsVersion, String trustStoreName,
char[] trustStorePassword, String keyStoreName, char[] keyStorePassword)
throws Exception {
Objects.requireNonNull(tlsVersion, "TLS version is mandatory");
if (port <= 0) {
throw new IllegalArgumentException(
"Port number cannot be less than or equal to 0");
}
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream tstore = TLSServer.class
.getResourceAsStream("/" + trustStoreName);
trustStore.load(tstore, trustStorePassword);
tstore.close();
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream kstore = TLSServer.class
.getResourceAsStream("/" + keyStoreName);
keyStore.load(kstore, keyStorePassword);
KeyManagerFactory kmf = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyStorePassword);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
SecureRandom.getInstanceStrong());
SSLServerSocketFactory factory = ctx.getServerSocketFactory();
try (ServerSocket listener = factory.createServerSocket(port)) {
SSLServerSocket sslListener = (SSLServerSocket) listener;
sslListener.setNeedClientAuth(true);
sslListener.setEnabledProtocols(new String[] {tlsVersion});
// NIO to be implemented
while (true) {
try (Socket socket = sslListener.accept()) {
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("Hello World!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
Now create the client:
package com.sapbasu.javastudy;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Objects;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;
public class TLSClient {
public String request(InetAddress serverHost, int serverPort,
String tlsVersion, String trustStoreName, char[] trustStorePassword,
String keyStoreName, char[] keyStorePassword) throws Exception {
Objects.requireNonNull(tlsVersion, "TLS version is mandatory");
Objects.requireNonNull(serverHost, "Server host cannot be null");
if (serverPort <= 0) {
throw new IllegalArgumentException(
"Server port cannot be lesss than or equal to 0");
}
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream tstore = TLSClient.class
.getResourceAsStream("/" + trustStoreName);
trustStore.load(tstore, trustStorePassword);
tstore.close();
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream kstore = TLSClient.class
.getResourceAsStream("/" + keyStoreName);
keyStore.load(kstore, keyStorePassword);
KeyManagerFactory kmf = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyStorePassword);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
SecureRandom.getInstanceStrong());
SocketFactory factory = ctx.getSocketFactory();
try (Socket connection = factory.createSocket(serverHost, serverPort)) {
((SSLSocket) connection).setEnabledProtocols(new String[] {tlsVersion});
SSLParameters sslParams = new SSLParameters();
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
((SSLSocket) connection).setSSLParameters(sslParams);
BufferedReader input = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
return input.readLine();
}
}
}
Finally, here's is a JUnit test to test the connection:
package com.sapbasu.javastudy;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.net.InetAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.Test;
public class TLSServerClientTest {
private static final int SERVER_PORT = 8444;
private static final String TLS_VERSION = "TLSv1.2";
private static final int SERVER_COUNT = 1;
private static final String SERVER_HOST_NAME = "127.0.0.1";
private static final String TRUST_STORE_NAME = "servercert.p12";
private static final char[] TRUST_STORE_PWD = new char[] {'a', 'b', 'c', '1',
'2', '3'};
private static final String KEY_STORE_NAME = "servercert.p12";
private static final char[] KEY_STORE_PWD = new char[] {'a', 'b', 'c', '1',
'2', '3'};
#Test
public void whenClientSendsServerRequest_givenServerIsUp_returnsHelloWorld()
throws Exception {
TLSServer server = new TLSServer();
TLSClient client = new TLSClient();
System.setProperty("javax.net.debug", "ssl");
ExecutorService serverExecutor = Executors.newFixedThreadPool(SERVER_COUNT);
serverExecutor.submit(() -> {
try {
server.serve(SERVER_PORT, TLS_VERSION, TRUST_STORE_NAME,
TRUST_STORE_PWD, KEY_STORE_NAME, KEY_STORE_PWD);
} catch (Exception e) {
e.printStackTrace();
}
});
try {
String returnedValue = client.request(
InetAddress.getByName(SERVER_HOST_NAME), SERVER_PORT, TLS_VERSION,
TRUST_STORE_NAME, TRUST_STORE_PWD, KEY_STORE_NAME, KEY_STORE_PWD);
assertEquals("Hello World!", returnedValue);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
Note: The certificate (servercert.p12 in this example) should be in the classpath. In this example, I've kept it in the test/resources folder of Maven folder structure so that the JUnit test can get it in the classpath.
Cipher Suite Background
When using TLS/SSL, the cryptographic algorithms to be used are determined by cipher suites. The server supports a set of cipher suites (you can enable or disable certain suites as per your needs and security level you want). The client also supports a set of cipher suites. During connection setup, the cipher suite to be used is negotiated between the client and the server. The client preference will be honored given that the server supports that particular cipher suite.
You'd find the list of cipher suites supported by the Sun Providers upto Java 8 here.
A typical cipher suite name looks like this:
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
Here,
ECDHE stands for Elliptic Curve Diffie Hellman Ephemeral. It's a Key exchange algorithm. The Elliptic variant (the first E) is used for performance, whereas the Ephemeral variant (the last E) is for forward secrecy. Forward secrecy means that if an attacker keeps recording all the communications over TLS and at a later point of time somehow gets hold of the private key, he/she cannot decrypt the past recorded communications.
ECDSA is a digital signature algorithm used for signing the key and is used for authenticating (verifying the integrity of) the shared secret. ECDSA is weaker and slower than the other authentication algorithms like HMAC. Yet it is used for shared key authentication because it does not need the verifier know the secret key used to create the authentication tag. The server can very well use its private key to verify the integrity of the message.
AES_128_GCM - Once a common secret key is shared between both the parties (usually a browser and a web server), a symmetric block cipher algorithm is used to encrypt the message exchanges between the parties. In this particular case, the block cipher AES with 128 bit key and GCM authentication mode is used.
SHA256 - Hashing algorithm for the PRF
You are setting the system properties wrong.
The server needs javax.net.keyStore (not trustStore) and javax.net.keyStorePassword -- and sometimes javax.net.keyStoreType but probably not in your case. For a server using self-signed cert, the client needs the truststore to contain that cert, and does not need any keystore at all. Although it's not documented, if these are on the same system you can use the server keystore (containing a privateKeyEntry) as the client truststore and JSSE automatically treats the privateKey's leaf cert as a trustedCert. In any other situation you need to extract the server's cert (not key), copy/send it to the client, and use keytool -importcert to put the cert (not key) into a keystore file which is used as truststore; this can be either a custom file (identified with the sysprops) or the default truststore at JRE/lib/security/cacerts (except on current Windows if JRE is under \Program Files[ (x86)] as is the installer default, because Windows now messes with people who try to change files there).
And these sysprops (when used) must be set BEFORE THE JSSE CLASSES ARE LOADED -- setting them when you call SSL[Server]SocketFactory is usually far too late. Usually they are set on the commandline that invokes Java with the -Dname=value option; that is guaranteed to be done early enough. If you can't do that, put the setProperty calls at the beginning of your main method, or the equivalent for a GUI.
This manifests as 'no common/shared cipher' because without a key+cert the server cannot support the ciphersuites that do server authentication, and Java/JSSE by default only enables ciphersuites that do server authentication because the others are insecure in many/most situations.
I'm sure I've answered the second part before, but can't find a dupe. The first part has occurred in many answers, but is usually not clearly presented in the question.
I am totally new to security and signature verification and so far I couldn't find a place which explained the basics of signature verification. I need to verify the signature of a file by obtaining the public key from the appropriate certificate available from certificate store. the tutorial in Java (https://docs.oracle.com/javase/tutorial/security/apisign/versig.html) doesn't teach how to obtain a certificate from the trusted certificate store and verify using that. I went through Bouncy castle WIKI http://www.bouncycastle.org/wiki/display/JA1/BC+Version+2+APIs but its not really explanatory for a beginner. How do I do this? Given a signed file, how can I check for its public key from the certificate store and verify if its the right person who has sent the file? Please advice.
Because you did not provide what build management you use, I assume it will be Maven.
First, include BouncyCastle in your dependency
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.53</version>
</dependency>
After that, you need to make a util class that you will be using for sign or verify the certificate. Something like this:
package your.pack.location;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* Author: harunalfat
*/
public class SignatureUtils {
private static final Logger log = LogManager.getLogger(SignatureUtils.class);
public static String sign(String plainText, PrivateKey privateKey) throws Exception {
byte[] data = plainText.getBytes("ISO-8859-1");
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initSign(privateKey);
signature.update(data);
return Base64.toBase64String(signature.sign());
}
public static boolean verify(String plainText, String signString, PublicKey publicKey) throws Exception{
byte[] data = plainText.getBytes("ISO-8859-1");
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initVerify(publicKey);
signature.update(data);
byte[] signByte = Base64.decode(signString);
return signature.verify(signByte);
}
private static PemObject getPemObjectFromResource(String fileLocation) throws IOException {
Resource resource = new ClassPathResource(fileLocation);
InputStream is = resource.getInputStream();
PemObject pemObject = new PemReader(new InputStreamReader( is )).readPemObject();
return pemObject;
}
private static X509EncodedKeySpec getPubKeySpec(String fileLocation) throws IOException, NoSuchAlgorithmException {
PemObject pemObject = getPemObjectFromResource(fileLocation);
byte[] data = pemObject.getContent();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(data);
return keySpec;
}
private static PKCS8EncodedKeySpec getPriKeySpec(String fileLocation) throws IOException, NoSuchAlgorithmException {
PemObject pemObject = getPemObjectFromResource(fileLocation);
byte[] data = pemObject.getContent();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(data);
return keySpec;
}
public static PublicKey getPublicKey(String fileLocation) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
KeyFactory kf = KeyFactory.getInstance("RSA");
KeySpec keySpec = getPubKeySpec(fileLocation);
return kf.generatePublic(keySpec);
}
public static PrivateKey getPrivateKey(String fileLocation) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
KeyFactory kf = KeyFactory.getInstance("RSA");
KeySpec keySpec = getPriKeySpec(fileLocation);
return kf.generatePrivate(keySpec);
}
}
And then you will use it like this
package your.another.pack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.util.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import static org.junit.Assert.assertTrue;
/**
* Author: harunalfat
*/
public class SignatureUtilsTest {
private static final Logger log = LogManager.getLogger(SignatureUtilsTest.class);
private static final String PLAIN = "attack at dawn";
#Test
public void testSignAndVerify() throws Exception {
Security.addProvider(new BouncyCastleProvider()); // <-- IMPORTANT!!! This will add BouncyCastle as provider in Java Security
PrivateKey privateKey = SignatureUtils.getPrivateKey("key/private2.pem"); // This is located on src/main/resources/key/private2.pem
PublicKey publicKey = SignatureUtils.getPublicKey("key/public2.pem"); // This is located on src/main/resources/key/public2.pem
// In this example, I use junit test, so it will be on src/test/resources/...
log.info("Private Key : "+Base64.encodeBase64String(privateKey.getEncoded()));
log.info("Public Key : "+Base64.encodeBase64String(publicKey.getEncoded()));
String sign = SignatureUtils.sign(PLAIN, privateKey);
log.info("Plain String : "+PLAIN);
log.info("Sign : "+sign);
boolean result = SignatureUtils.verify(PLAIN,sign, publicKey);
log.info("Result : "+result);
assertTrue(result);
}
}
Of course, you can change the Signature instance with another Algorithm. In my case I use "SHA1WithRSA", but you get the point right?
With this, someone will encrypt their data using their private key, and send it to you. After that, you will verify the data using the public key they give.
In example, Bob send to you message about money amount he sent to you ($5000), and sign it using their private key, become encrypted. When the data arrived to you, you know Bob supposed to send $5000, then you verify the encrypted data with text $5000 and public key Bob share, but is it really $5000 OR does it comes from Bob?
If the data has been changed, OR when someday you ask for some Money to Bob, but the message tapped by someone else and s/he send you the amount message with private key other than Bob's, you will know.
Feel free to ask :)