Chat Application - Cryptography - java

I have created a chat application and to finish it I have to implement some Cryptography algorithm to secure the messages between server - client.
My implementation is:
1.Client creates kaypair (public and private key) and sends public key to server.
2.Server gets public key and creates symmetric key encrypted with the public key.
3.Server sends the encrypted key to Client.
4.Client unlocks symmetric key with private key.
5.Client and Server communicate with Symmetric key.
This part of the code is where the server gets the public key and sends the symmetric key encrypted
else if(msg.type.equals("pubKey")){
pubKey = msg.content; //get public key
String key = Arrays.toString(crypt.geteKey());
clients[findClient(ID)].send(new Message("symmKey", "SERVER", key, msg.sender));//! //send symmetric key encrypted with public key
}
Key encryption method:
public void keyEncryption() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
eKey = cipher.doFinal(key.getBlowfishKeyBytes()); //symmetric key encrypted with public key
//System.out.println("2. cipherText= " + bytesToHex(symmKey));
}
How I get encrypted symmetric key from server:
else if(msg.type.equals("symmKey")){
symmKey = (String) msg.content; //get encrypted symmetric key (must unlock with private key)
}
The Server Message class: (Client Message class has "object content" instead of String)
package com.socket;
import java.io.Serializable;
public class Message implements Serializable{
private static final long serialVersionUID = 1L;
public String type, sender,content, recipient;
public Message(String type, String sender, String content, String recipient){
this.type = type; this.sender = sender; this.content = content; this.recipient = recipient;
}
#Override
public String toString(){
return "{type='"+type+"', sender='"+sender+"', content='"+content+"', recipient='"+recipient+"'}";
}
}
Client GUI where I send the key to the Server:
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
//KeyPair
try {
keyPair = new Keypair();
} catch (NoSuchAlgorithmException ex) {
jTextArea1.append("Security Error! You are not safe!");
}
Object pubKey = keyPair.getKeyPair().getPublic();
username = jTextField3.getText();
password = jPasswordField1.getText();
if (!username.isEmpty() && !password.isEmpty()) {
client.send(new Message("login", username, password, "SERVER"));
client.send(new Message("pubKey",username, pubKey, "SERVER")); //send Public key to Server
}
}
Error I Get on Server:
Database exception : userExists()
53846 ERROR reading: cannot assign instance of sun.security.rsa.RSAPublicKeyImpl to field com.socket.Message.content of type java.lang.String in instance of com.socket.Message
I have implemented steps 1-3 but I get this exception... If anyone has any idea how to deal with this issue, please help me.
(I will provide any additional code if required.)
Thank you.

msg.content is instance of String and you trying assign it to sun.security.rsa.RSAPublicKeyImpl here:
pubKey = msg.content;

Just on a note, your implementation looks susceptible to a man-in-the-middle attack. If we call your client and server Alice and Bob. I'm Mallory - a malicious eavesdropper.
Alice creates public-private key pair and sends public key to Bob.
Mallory intercepts this, keeps Alice's public key for later and sends his own public key on to Bob.
Bob receives Mallory's public key, thinking it belongs to Alice, generates a session key, encrypts it and sends back to Mallory.
Mallory decrypts the session key, reencrypts using Alice's public key and returns it to her.
Alice decrypts using her private key and happily goes about sending encrypted messages to Bob, not realising that Mallory has intercepted the session key.
Mallory now listens in on their conversation and sells Alice's mums recipe for chocolate cake to the highest bidder. Alice blames Bob, Bob blames Alice etc.
You will need to introduce signing to your protocol to help to ensure authenticity of your protocol: Alice and Bob need to be certain that that the communications are original and have not been tampered with. I'm on my phone but will see if I can find a decent link later for you.

Related

Java Diffie hellman initialize ECDHKeyAgreement

I have a Diffie–Hellman security class like this:
public class AESSecurityCap {
private PublicKey publicKey;
KeyAgreement keyAgreement;
byte[] sharedsecret;
AESSecurityCap() {
makeKeyExchangeParams();
}
private void makeKeyExchangeParams() {
KeyPairGenerator kpg = null;
try {
kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(128);
KeyPair kp = kpg.generateKeyPair();
publicKey = kp.getPublic();
keyAgreement = KeyAgreement.getInstance("ECDH");
keyAgreement.init(kp.getPrivate());
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
}
}
public void setReceiverPublicKey(PublicKey publickey) {
try {
keyAgreement.doPhase(publickey, false); // <--- Error on this line
sharedsecret = keyAgreement.generateSecret();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
}
and implemented this class:
public class Node extends AESSecurityCap {
}
Sometimes I need to reinitialize DH keyAgreement:
public class TestMainClass {
public static void main(String[] args) {
Node server = new Node();
Node client = new Node();
server.setReceiverPublicKey(client.getPublicKey());
client.setReceiverPublicKey(server.getPublicKey());
// My problem is this line ,
// Second time result exception
server.setReceiverPublicKey(client.getPublicKey());
}
}
but receive this exception:
Exception in thread "main" java.lang.IllegalStateException: Phase already executed
at jdk.crypto.ec/sun.security.ec.ECDHKeyAgreement.engineDoPhase(ECDHKeyAgreement.java:91)
at java.base/javax.crypto.KeyAgreement.doPhase(KeyAgreement.java:579)
at ir.moke.AESSecurityCap.setReceiverPublicKey(AESSecurityCap.java:37)
at ir.moke.TestMainClass.main(TestMainClass.java:13)
Is there any way to reinitialize ECDH KeyAgreement multiple time?
This is my test case:
Client initialize DH and generate public key.
Client sent public key to server.
Server initialize DH with client key and generate own public key and generate shared secret key.
Server send public key to client.
Client generate shared secret key with server public key.
In this step client and server has public keys and shared secret.
My problem is client disconnected() and KeyAgreement initialized by singleton object and don't reinitialized second time.
Sometimes I need to do this subject.
Please guide me to fix this problem.
The IllegalStateException (Phase already executed) seems to be especially caused by the ECDH-implementation of the SunEC-provider. The exception doesn't occur if an (additional) init is executed immediately before the doPhase. However, this init-call shouldn't be necessary, since after the doPhase-call generateSecret is executed, which should reset the KeyAgreement-instance to the state after the init-call, at least according to the generateSecret-documentation:
This method resets this KeyAgreement object to the state that it was in after the most recent call to one of the init methods...
Possibly it's a bug in the SunEC-provider. If DH is used instead of ECDH (and the SunJCE-provider instead of the SunEC-provider) the behavior is as expected, i.e. repeated doPhase-calls are possible (without additional init-calls). The same applies to ECDH using the BouncyCastle-provider. Therefore, you could take the BouncyCastle-provider instead of the SunEC-provider to run ECDH with your code.
Note: The second parameter (lastPhase) in doPhase should be set to true, otherwise an IllegalStateException (Only two party agreement supported, lastPhase must be true) is generated (at least for ECDH).
EDIT:
The bug is already known and fixed in JDK 12, see JDK-8205476: KeyAgreement#generateSecret is not reset for ECDH based algorithmm.

Decryption at client side using java

I encypted the session Id at server side but when I am trying to decrypt the session id at client side some error is coming. please can anyone help resolving the error.
public static String decrypt(String sessionId)
{
try
{
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
final SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
final String decryptedSessionId = new String(cipher.doFinal(Base64.decodeBase64(sessionId)));
return decryptedSessionId;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
The error that is coming is :
Class 'javax.crypto.Cipher' is not present in JRE Emulation Library so it cannot be used in client code of "some" GWT module.
This inspection reports usages in client code of JDK classes which is not present in JRE Emulation Library.
The method for encryption that I used is :
public static String encrypt(String sessionId)
{
try
{
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
final SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
final String encryptedSessionId = Base64.encodeBase64String(cipher.doFinal(sessionId.getBytes()));
return encryptedSessionId;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
I am new to this stuff please help me resolving the errors
Well, you cannot use java standard encryption library in GWT coding on client side. It's not supported.
Use gwt-crypto to encrypt/decrypt the necessary stuff.
AES is not be supported on client side for GWT in GWT-crypto, but you can use TripleDES. TripleDES is also very much secure implementation.

ECDH using Android KeyStore generated private key

I'm trying to implement ECDH in Android using a private generated by Android KeyStore Provider.
public byte[] ecdh(PublicKey otherPubKey) throws Exception {
try {
ECPublicKey ecPubKey = (ECPublicKey) otherPubKey;
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
PrivateKey pk = (PrivateKey) LoadPrivateKey("Backend");
keyAgreement.init(pk);
keyAgreement.doPhase(ecPubKey, true);
return (keyAgreement.generateSecret());
}
catch (Exception e)
{
Log.e("failure", e.toString());
return null;
}
}
However, this exception is catched in keyAgreement.init(pk) :
E/failure: java.security.InvalidKeyException: cannot identify EC private key: java.security.InvalidKeyException: no encoding for EC private key
I generated before successfully the "Backend" Public/Private key pair using:
public void GenerateNewKeyPair(String alias)
throws Exception {
if (!keyStore.containsAlias(alias)) {
// use the Android keystore
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, ANDROID_KEYSTORE);
keyGen.initialize(
new KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY | KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA384,
KeyProperties.DIGEST_SHA512)
.setRandomizedEncryptionRequired(true)
.build());
// generates the keypair
KeyPair keyPair = keyGen.generateKeyPair();
}
}
And I load the private key using:
public PrivateKey LoadPrivateKey(String alias) throws Exception {
PrivateKey key = (PrivateKey) keyStore.getKey(alias, null);
return key;
}
Anyone has an idea what is happening and can help me to understand how to fix it? Thanks!
As far as I know through research and trial and error, this is not currently supported.
I believe the best you can do is sign the public key of an EC key pair you generate outside of the AndroidKeyStore with an EC key pair that is stored in AndroidKeyStore. You can then send this signed public key over to the other party with your signing key certificate, generate a shared secret (outside of AndroidKeyStore), then store the SecretKey that is derived using a KDF on the generated secret. I recommend using this non-AndroidKeyStore generated key pair once (so only for the purpose of deriving the secret) and repeating this process to re-key when deemed necessary.
EDIT: When I said 'store the SecretKey', I meant in AndroidKeyStore. That key will initially be in what is called 'normal world' in this context, but its the best you can do for now.
ECDH is supported in API level 23. Please refer android documentation on Android Keystore System
Sample code is also available in this link..

sftp with public key is not working

Generated RSA public key using ssh-keygen.
Trying to use to connect remote server through sftp :
JSch jsch = new JSch();
try {
String publicKey = "/home/testuser/.ssh/id_rsa.pub";
jsch.addIdentity(publicKey);
session = jsch.getSession(sftpUsername, sftpHostname, sftpPort);
session.setConfig("PreferredAuthentications", "publickey,keyboard-interactive,password");
} catch (JSchException e) {
logger.error("Unable to obtain session", e);
}
getting below error :
com.jcraft.jsch.JSchException: invalid privatekey: /home/testuser/.ssh/id_rsa.pub
at com.jcraft.jsch.IdentityFile.<init>(IdentityFile.java:261)
at com.jcraft.jsch.IdentityFile.newInstance(IdentityFile.java:135)
at com.jcraft.jsch.IdentityFile.newInstance(IdentityFile.java:130)
at com.jcraft.jsch.JSch.addIdentity(JSch.java:206)
at com.jcraft.jsch.JSch.addIdentity(JSch.java:192)
Any suggestions ?
You have:
jsch.addIdentity(publicKey);
JSch javadoc says:
public void addIdentity(String prvkey)
throws JSchException;
Adds an identity to be used for public-key authentication. Before registering it into identityRepository, it will be deciphered with passphrase.
Parameters:
prvkey - the file name of the private key file. This is also used as the identifying name of the key. The corresponding public key is assumed to be in a file with the same name with suffix .pub.
You have supplied the public key, when JSch wants the private key.
If you think about it, this makes sense. There's nothing secret about a public key. JSch wants a secret, so it can prove who you are.
Your private key is probably in ~/.ssh/id_rsa (without the .pub extension).
You may need to use the two-parameter version of addIdentity, in order to supply a passphrase to decrypt the private key.

How to extract publicKey attribute in verifyMessageSignature method of SignatureChecker

I am writing and sns http end point. I have to verify the SNS message using verifyMessageSignature method of SignatureChecker. How can I get the publicKey attribute from message. Is there util method for it as well. Please help.
The SNS message from Amazon includes a field SigningCertURL. Fetch the bytes at this location into a string cert, then make a public key from it thusly:
/**
* Build a PublicKey object from a cert
*
* #param cert the cert body
* #return a public key
*/
private PublicKey makePublicKey(String cert) {
try {
CertificateFactory fact = CertificateFactory.getInstance("X.509");
InputStream stream = new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8));
X509Certificate cer = (X509Certificate) fact.generateCertificate(stream);
return cer.getPublicKey();
} catch (Exception e) {
LOGGER.error("Failed to make a public key from Amazon cert", e);
return null;
}
}
Then you can call signatureChecker.verifySignature with it as the second argument.

Categories