I am trying to achieve encryption using PGP and my encrypt method successfully encrypts the input string but when I try to decrypt it to verify if the encryption is properly done, the string doesn't get decrypted.
I tried 2 approaches:
1st approach uses FileOutputStream to write encrypted string & 2nd approach uses ByteArrayOutputStream.
FileOutputStream creates a file and I am able to decrypt it using Kleopatra. However my requirement is to just get an encrypted string (not written in a file). So when I try to decrypt the encrypted string (received after using ByteArrayOutputStream) its not working. I tried copying the string and decrypting it through tools>>clipboard in Kleopatra, but the decrypt/verify option is disabled. I tried writing the string on a file manually & through FileWriter class, but decryption fails with the error that File contains certificate & cannot be decrypted or verified.
I assume only files created directly by OutputStream gets decrypted successfully.
But I have to really check the encrypted string.
Any help would be highly appreciated.
The following full example is taken from the source code of the book "Java Cryptography: Tools and Techniques by David Hook & Jon Eaves".
The complete source code with all examples is available here: https://www.bouncycastle.org/java-crypto-tools-src.zip
The examples are showing a private-/public key creation with El Gamal or Elliptic Curves and encryption with AES-256.
In the ecExample-method I added two lines to save the encrypted string to the file "pgp-encrypted-string.dat" and then
reload the data to decypt the file and show the decrypted string.
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.io.Streams;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.ECGenParameterSpec;
import java.util.Date;
public class PGPEncryptionExampleForSO
{
/**
* Create an encrypted data blob using an AES-256 session key and the
* passed in public key.
*
* #param encryptionKey the public key to use.
* #param data the data to be encrypted.
* #return a PGP binary encoded version of the encrypted data.
*/
public static byte[] createEncryptedData(
PGPPublicKey encryptionKey,
byte[] data)
throws PGPException, IOException
{
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256)
.setWithIntegrityPacket(true)
.setSecureRandom(new SecureRandom()).setProvider("BC"));
encGen.addMethod(
new JcePublicKeyKeyEncryptionMethodGenerator(encryptionKey)
.setProvider("BC"));
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
// create an indefinite length encrypted stream
OutputStream cOut = encGen.open(encOut, new byte[4096]);
// write out the literal data
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
OutputStream pOut = lData.open(
cOut, PGPLiteralData.BINARY,
PGPLiteralData.CONSOLE, data.length, new Date());
pOut.write(data);
pOut.close();
// finish the encryption
cOut.close();
return encOut.toByteArray();
}
/**
* Extract the plain text data from the passed in encoding of PGP
* encrypted data. The routine assumes the passed in private key
* is the one that matches the first encrypted data object in the
* encoding.
*
* #param privateKey the private key to decrypt the session key with.
* #param pgpEncryptedData the encoding of the PGP encrypted data.
* #return a byte array containing the decrypted data.
*/
public static byte[] extractPlainTextData(
PGPPrivateKey privateKey,
byte[] pgpEncryptedData)
throws PGPException, IOException
{
PGPObjectFactory pgpFact = new JcaPGPObjectFactory(pgpEncryptedData);
PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpFact.nextObject();
// find the matching public key encrypted data packet.
PGPPublicKeyEncryptedData encData = null;
for (PGPEncryptedData pgpEnc: encList)
{
PGPPublicKeyEncryptedData pkEnc
= (PGPPublicKeyEncryptedData)pgpEnc;
if (pkEnc.getKeyID() == privateKey.getKeyID())
{
encData = pkEnc;
break;
}
}
if (encData == null)
{
throw new IllegalStateException("matching encrypted data not found");
}
// build decryptor factory
PublicKeyDataDecryptorFactory dataDecryptorFactory =
new JcePublicKeyDataDecryptorFactoryBuilder()
.setProvider("BC")
.build(privateKey);
InputStream clear = encData.getDataStream(dataDecryptorFactory);
byte[] literalData = Streams.readAll(clear);
clear.close();
// check data decrypts okay
if (encData.verify())
{
// parse out literal data
PGPObjectFactory litFact = new JcaPGPObjectFactory(literalData);
PGPLiteralData litData = (PGPLiteralData)litFact.nextObject();
byte[] data = Streams.readAll(litData.getInputStream());
return data;
}
throw new IllegalStateException("modification check failed");
}
private static void elgamalExample()
throws Exception
{
byte[] msg = Strings.toByteArray("Hello, world!");
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("DH", "BC");
kpGen.initialize(2048);
KeyPair kp = kpGen.generateKeyPair();
PGPKeyPair elgKp = new JcaPGPKeyPair(
PGPPublicKey.ELGAMAL_ENCRYPT, kp, new Date());
byte[] encData = createEncryptedData(elgKp.getPublicKey(), msg);
byte[] decData = extractPlainTextData(elgKp.getPrivateKey(), encData);
System.out.println("elgamal encryption msg length: " + msg.length + " enc.length: " + encData.length + " dec.length: " + decData.length);
System.out.println(Strings.fromByteArray(decData));
}
private static void ecExample()
throws Exception
{
byte[] msg = Strings.toByteArray("Hello, world!");
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", "BC");
kpGen.initialize(new ECGenParameterSpec("P-256"));
KeyPair kp = kpGen.generateKeyPair();
PGPKeyPair ecdhKp = new JcaPGPKeyPair(PGPPublicKey.ECDH, kp, new Date());
byte[] encData = createEncryptedData(ecdhKp.getPublicKey(), msg);
// save encrypted string
Files.write(Paths.get("pgp-encrypted-string.dat"), encData);
// load encrypted string
byte[] encDataLoad = Files.readAllBytes(Paths.get("pgp-encrypted-string.dat"));
byte[] decData = extractPlainTextData(ecdhKp.getPrivateKey(), encDataLoad);
System.out.println("ec encryption msg length: " + msg.length + " enc.length: " + encData.length + " dec.length: " + decData.length);
System.out.println(Strings.fromByteArray(decData));
}
public static void main(String[] args)
throws Exception
{
Security.addProvider(new BouncyCastleProvider());
// you need the two files bcpg-jdk15on-165.jar and bcprov-jdk15to18-165.jar to run the example
System.out.println("Example from Java Cryptography: Tools and Techniques by David Hook & Jon Eaves");
System.out.println("get source files: https://www.bouncycastle.org/java-crypto-tools-src.zip");
elgamalExample();
ecExample();
}
}
This is the short output:
Example from Java Cryptography: Tools and Techniques by David Hook & Jon Eaves
get source files: https://www.bouncycastle.org/java-crypto-tools-src.zip
elgamal encryption msg length: 13 enc.length: 601 dec.length: 13
Hello, world!
ec encryption msg length: 13 enc.length: 200 dec.length: 13
Hello, world!
Added: As I now understand you're having a String like
This string needs an encryption
and you want to encrypt it with a rsa pgp public key:
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG v1.65
mI0EXr/nDgEEAKhB6ufAB954aBIlNjPCsryzUVLu0qkC/1RtnFHf+J6IVegV8Wi7
28V074inQcw6o6FTLtFTaLRP4+3eXNATdjGSjrvcP7k+nu50vydugHv43fPuCiZ7
6gbbMTE9gPiLPA2pS+SmQJnr9hOrD5rzwYP1yNNIsRJ9qmU5NeZyu+szABEBAAG0
DHRlc3RpZGVudGl0eYicBBABAgAGBQJev+cOAAoJEPBDuyqTbz/gY0YD/R+gDkfe
qPgNuk6iI2wLSGEeZRXr6Ru1cyG73CRvz7BjCpwWx039AdQzP9gkeo6MEj8Z0c73
obqEP8NtvvOcwC7+/QiGLTR2mgCsNhk54+iCGsvNbkpkr/rRoYZGyvb+rxui0A61
DCB1w5hdnyMg2OglFNrkaPfpNjMsTebfF5eS
=h1+m
-----END PGP PUBLIC KEY BLOCK-----
and get the encrypted string
-----BEGIN PGP MESSAGE-----
Version: BCPG v1.65
hIwD8EO7KpNvP+ABA/9JkOE9PDyS/kr/lZ1Uz+NCSe1JiNcKCXjbsUbvP8CT7Tf1
cKlgzIz1mQjdpkBtVpVhEnEjmUzFy2UCRKr4b4Wx7/1UL+370CICW5HgMoi5TgTg
MYRy5I9Uba/+JxcusjWB1JJHP4ofULziXRKLWAoSPLlglZDzSmV88hNo19rl39JZ
AbMhIS2edM9hHICefL/Yaiq90hGjKMRReVopu2tPUjNLGYP7QABAvWb3WQJMZoYT
HEsyjHxeyYQylAdYB7pWQA0++Z803iclvM3skN8FBt64ebDkqfxgbhs=
=je0r
-----END PGP MESSAGE-----
Now you'd like to decrypt this message with Kleoptatra, online (e.g. https://sela.io/pgp-en/) or in Java with the RSA pgp private key and the password 123456:
-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: BCPG v1.65
lQH+BF6/5w4BBACoQernwAfeeGgSJTYzwrK8s1FS7tKpAv9UbZxR3/ieiFXoFfFo
u9vFdO+Ip0HMOqOhUy7RU2i0T+Pt3lzQE3Yxko673D+5Pp7udL8nboB7+N3z7gom
e+oG2zExPYD4izwNqUvkpkCZ6/YTqw+a88GD9cjTSLESfaplOTXmcrvrMwARAQAB
/gMDAhhcE1oF/u8YYExKGLgriK5JpUUSsMFU0AOHP9/zZQr09437V0f/F4J87+9s
G30lDRikGwynEGRnAvIVwqq2F+iarKGGHCZCRgbyufXS7VK6wE/43lR0kSwA2VIM
ll/KbQKP1cSZv0rqtJ1tGL7cDHFEwq10gM4Bn75HOKyBzE9oERRKz37noAECsAZn
xuXGlEB5noqTT00RxsHjBA5Os04CtEz9N+OMrg47IR7AzSQUe90lG2F6W71dhJ6V
jQaf7D6JFU3dOWPW1eBb5FQhgYF92CFRizJ42lDCiTfl2FQU49MlwLd2ofNneuPo
aVuPoYUNKwbasyx4fo2vh6rrMyxmncCizMExvh6GIVgYd7EK9s6Gxq/duuOvly4O
ZAyIY2MOon0bDXxAYR2q/wdQLamnP7rAR4uMu24m/iOuBj6wwTR8v8hhsFFTp/4u
tebwWzLnPyyBYStnTF5IZ9ZJeVl5S3zdzNcrP9g8yXtItAx0ZXN0aWRlbnRpdHmI
nAQQAQIABgUCXr/nDgAKCRDwQ7sqk28/4GNGA/0foA5H3qj4DbpOoiNsC0hhHmUV
6+kbtXMhu9wkb8+wYwqcFsdN/QHUMz/YJHqOjBI/GdHO96G6hD/Dbb7znMAu/v0I
hi00dpoArDYZOePoghrLzW5KZK/60aGGRsr2/q8botAOtQwgdcOYXZ8jINjoJRTa
5Gj36TYzLE3m3xeXkg==
=y/tQ
-----END PGP PRIVATE KEY BLOCK-----
and get the decrypted string:
This string needs an encryption
To encrypt/decrypt in Java fortunately there are sample files available in the BouncyCastle Github-Repo: https://github.com/bcgit/bc-java/blob/master/pg/src/main/java/org/bouncycastle/openpgp/examples/. You may need to create a new PGP-keypair using RSA (RSAKeyPairGenerator.java) or ElGamal
(DSAElGamalKeyRingGenerator.java). With the generated keys you can encrypt or decrypt using KeyBasedFileProcessor.java and neccessary PGPExampleUtil.java.
I created the RSA key files with "-a testidentity 123456" as arguments, the encryption is done with "-e -ai plaintext.txt rsa_pub.asc" and the decryption goes with "-d plaintext.txt.asc rsa_secret.asc 123456".
Related
I have a situation where I have to store the encrypted from of a text in an excel sheet and my java code should read that encrypted text from a excel sheet and convert it back to original text and hit the server with original plain text.
I'm trying to achieve this with AES encryption/decryption logic but I'm not able to achieve it as every time I convert the plain text into encrypted format for it to be stored in the excel sheet it results in a different encrypted string each time so I'm not able to decrypt it back to original, what I want is for a same static string/text my AES encrypted text should also be static.
I know even if my encrypted string is dynamic I can convert it back to decrypted format but for my case what I want is my encrypted text should also remain static.
How can I achieve this?
Ex. If my plain text is "This is a question" my encrypted string is different each time like below
Enter
This is a question
Encrypted Data : mFsue8JGwLcJQTiBzM0HLVvdDXKPNGsG/O7N60joH+Ozgg==
Decrypted Data : This is a question
Enter
This is a question
Encrypted Data : FdBz3cGS4NphK14Fw8Me4daM4lVzdrK47WUMSRiUVe+juQ==
Decrypted Data : This is a question
See the encrypted data's are different each time. I want that to be static
The classes which I'm using are as below (please note these are not my exact classes but they implement the same logic which I have used. These example classes may have some non used variables and methods, I have mentioned just for reference)
Method.java
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class Method {
private static SecretKey key;
private final int KEY_SIZE = 128;
private final int DATA_LENGTH = 128;
private Cipher encryptionCipher;
/*
* public void init() throws Exception { KeyGenerator keyGenerator =
* KeyGenerator.getInstance("AES"); keyGenerator.init(KEY_SIZE); key =
* keyGenerator.generateKey();
*
*
* }
*/
// String to Key
public static void getKeyFromPassword(String toEnc, String salt) throws
NoSuchAlgorithmException, InvalidKeySpecException { SecretKeyFactory factory
= SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); KeySpec spec = new
PBEKeySpec(toEnc.toCharArray(), salt.getBytes(), 65536, 128); SecretKey
originalKey = new SecretKeySpec(factory.generateSecret(spec).getEncoded(),"AES");
key= originalKey; }
/*
* public static void convertStringToSecretKeyto(String string) {
*
*
* byte[] bytesEncoded = Base64.getEncoder().encode(string.getBytes()); byte[]
* decodedKey = Base64.getDecoder().decode(string); key = new
* SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
* System.out.println(bytesEncoded); System.out.println(decodedKey);
*
* }
*/
public String encrypt(String data) throws Exception {
byte[] dataInBytes = data.getBytes();
encryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
encryptionCipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = encryptionCipher.doFinal(dataInBytes);
return encode(encryptedBytes);
}
public String decrypt(String encryptedData) throws Exception {
byte[] dataInBytes = decode(encryptedData);
Cipher decryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(DATA_LENGTH,
encryptionCipher.getIV());
decryptionCipher.init(Cipher.DECRYPT_MODE, key, spec);
byte[] decryptedBytes = decryptionCipher.doFinal(dataInBytes);
return new String(decryptedBytes);
}
private String encode(byte[] data) {
return Base64.getEncoder().encodeToString(data);
}
private byte[] decode(String data) {
return Base64.getDecoder().decode(data);
}
}
Main.class
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.util.Base64;
import java.util.Scanner;
public class Cypher {
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
Method aes_encryption = new Method();
// aes_encryption.init();
Method.getKeyFromPassword("Texty text","Salty salt");
System.out.println("Enter");
Scanner sc= new Scanner(System.in);
String s= sc.nextLine();
String encryptedData = aes_encryption.encrypt(s);
String decryptedData = aes_encryption.decrypt(encryptedData);
System.out.println("Encrypted Data : " + encryptedData);
System.out.println("Decrypted Data : " + decryptedData);
} catch (Exception ignored) {
}
}
}
This is not really an implementation issue; your issue is with the used encryption scheme.
The nonce or IV are used to randomize your ciphertext. This gives you an important property: identical plaintext will encrypt to randomized ciphertext. This is required for a cipher to be secure, as you could otherwise identify identical plaintext messages.
Now you could just fix the IV or nonce, but that means that you will leak information early: if the first blocks of plaintext are identical then those blocks will result in identical ciphertext. Instead you can use an encryption scheme where each of the ciphertext bits depend on all of the plaintext bits.
There are several schemes that offer this kind of property:
biIGE mode: the bi-directional infinite garble extension mode is an encryption mode that runs both forward and then backwards. It is an odd mode that isn't used or implemented much;
FPE modes: format preserving encryption can be used to encrypt X plaintext bits into exactly X ciphertext bits, it is mainly used for smaller plaintext messages such as credit cards (so that there aren't any additional storage requirements for encrypted credit card numbers);
AES-SIV mode: synthetic IV mode is probably the best option, it can encrypt large(r) messages, and offers authenticity as well as the MAC / authentication tag doubles as IV. It has the drawback that it does require additional room for the synthetic IV / authentication tag. It is also a two-pass mode (all plaintext bytes are visited twice), which means that it won't be as performant as other generic modes.
There is AES-GCM-SIV for a performant option of the latter.
Of course, as indicated by others, you really want to make sure that you need this kind of functionality: not having messages encrypt to the same ciphertext is a rather fundamental security property of a cipher.
Hi all i am usig AES for encryption, what i have done is i encrypted a data in a text file and and stored the a given location, decryption works fine if given in the same class file, i have created a different java class to decrypt the file, I am using the Javakeystore with username and password to store the keys and retrieve it and use the stored key to decrypt but i am getting the above error. Help me out guys. Here is the code for decryption.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import de.flexiprovider.core.FlexiCoreProvider;
public class Decrypto {
public static void main(String[] args) throws Exception {
Security.addProvider(new FlexiCoreProvider());
/*
* Cipher cipher1 = Cipher.getInstance("AES128_CBC", "FlexiCore");
* KeyGenerator keyGen = KeyGenerator.getInstance("AES", "FlexiCore");
* SecretKey secKey = keyGen.generateKey();
* System.out.println(secKey);
*/
Cipher cipher1 = Cipher.getInstance("AES128_CBC", "FlexiCore");
KeyStore keyStore = KeyStore.getInstance("JCEKS");
FileInputStream fis = new FileInputStream("C:\\mykey.keystore"); // here
// i am
// uploading
keyStore.load(fis, "javaci123".toCharArray());
fis.close();
Key secKey = (Key) keyStore.getKey("mySecretKey",
"javaci123".toCharArray()); // line 35
System.out.println("Found Key: " + (secKey));
String cleartextFile = "C:\\cleartext.txt";
String ciphertextFile = "C:\\ciphertextSymm.txt";
// FileInputStream fis = new FileInputStream(cleartextFile);
FileOutputStream fos = new FileOutputStream(ciphertextFile);
String cleartextAgainFile = "C:\\cleartextAgainSymm.txt";
cipher1.init(Cipher.DECRYPT_MODE, secKey);
fis = new FileInputStream(ciphertextFile);
// fis = new FileInputStream(ciphertextFile);
CipherInputStream cis = new CipherInputStream(fis, cipher1);
fos = new FileOutputStream(cleartextAgainFile);
byte[] block = new byte[8];
int i;
while ((i = fis.read(block)) != -1) {
cis.read(block, 0, i);
}
cis.close();
}
}
error
Exception in thread "main" java.security.UnrecoverableKeyException: Given final block not properly padded
at com.sun.crypto.provider.KeyProtector.unseal(KeyProtector.java:360)
at com.sun.crypto.provider.JceKeyStore.engineGetKey(JceKeyStore.java:133)
at java.security.KeyStore.getKey(Unknown Source)
at darm.code.com.Decrypto.main(Decrypto.java:35)
UnrecoverableKeyException kind of identifies the problem, especially if the root cause is "Given final block not properly padded". This basically means that your password is incorrect. The KeyStore will first generate a key from the given password and they uses that key to decrypt the stored key. If the decryption fails you hope for a MAC authentication error, but in this instance you get a padding error (which basically means that somebody forgot to add integrity protection to the container that contains the wrapped private key).
I found the answer for the issue in line 35
Key secKey = (Key) keyStore.getKey("mySecretKey",
"javaci123".toCharArray()); // line 35
For encryption I had also set a password for the password i.e padding for the password is pw-password
Key secKey = (Key) keyStore.getKey("mySecretKey",
"pw-password".toCharArray()); // line 35
after running I get the saved key to decrypt a simple logic I missed.
Found Key: ***********6fd******5**********
Following this discussion it's a simple tutorial how to sign a string by using ECDSA algorithm in java without using any third-party libraries. But the question is:
How can i convert the public and the private key into a string ? (Because i want to send them into a database).
Can somebody help me create a simple tutorial of how to verify the message by using ECDSA algorithm in java ? at this point i need to include the signature and public key as the verification method.
Here's my scenario in my java code, assume that there's a sender side and the recipient side:
Sender side
package sender;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
public class Sign {
public static void main(String[] args) throws Exception {
/*
* Generate a key pair
*/
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
keyGen.initialize(256, random);
KeyPair pair = keyGen.generateKeyPair();
/*
Generate the private and the public key
*/
PrivateKey priv = pair.getPrivate();
/*
*and then Convert the priv key into a String;
*HOW can i do that ? this what i'm asking
*/
PublicKey pub = pair.getPublic();
/*
Convert the pub key into a String;
HOW can i do that ? this what i'm asking
*/
/*
-------Encrypt the pub and the priv key, i do with my own code
-------Store the enrypted pub & priv key into the database
-------I'm doing this with my own code
*/
/*
* Create a Signature object and initialize it with the private key
*/
Signature dsa = Signature.getInstance("SHA1withECDSA");
dsa.initSign(priv);
String str = "This is string to sign";
byte[] strByte = str.getBytes("UTF-8");
dsa.update(strByte);
/*
* Now that all the data to be signed has been read in, generate a
* signature for it
*/
byte[] realSig = dsa.sign();
System.out.println("Signature: " +
new BigInteger(1, realSig).toString(16));
/*
and Then i'm storing this signature into my database.
i have done with this
*/
}
}
Recipient side
package recipient;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
public class Verify {
public static void main(String[] args) throws Exception {
/*
Step one, taking public key from the database.
Step two, receive the message + signature.
Step three, split the message and signature into an "array[0]" for message,
and "array[1] for the signature"
Verify the signature <--- Here's what im asking to anybody,
how can i do, i mean the sample code ?
*/
}
}
Sorry for my bad English :D
You're asking a lot of different questions about dealing with ECDSA. I will address your first question about database storage here. I recommend you do some additional research on the mechanics of ECDSA if you want to learn about how to properly use it. Examples given here would be hard to follow out of context anyway.
To store keys as a string, you must first retrieve the byte array representing the key in its encoded format (note: encoded not encrypted). This can be done by using the getEncoded() method from class Key which is the superinterface of both PublicKey and PrivateKey.
Example:
PrivateKey key = // ...
byte[] enc_key = key.getEncoded();
// Byte array to string
StringBuilder key_builder = new StringBuilder();
for(byte b : enc_key){
key_builder.append(String.format("%02x", b));
}
String serialized_key = key_builder.toString();
To load the key again from a database you parse the string to a byte array, pass it into the appropriate key specification and then retrieve it by using a key factory.
Example:
String serialzed_key = // ...
byte[] encoded_key = // serialzed_key -> byte array conversion
// If key is private, use PKCS #8
PKCS8EncodedKeySpec formatted_private = new PKCS8EncodedKeySpec(encoded_key);
// or, if key is public, use X.509
X509EncodedKeySpec formatted_public = new X509EncodedKeySpec(encoded_key);
// Retrieve key using KeyFactory
KeyFactory kf = KeyFactory.getInstance("EC");
PublicKey pub = kf.generatePublic(formatted_public);
PrivateKey priv = kf.generatePrivate(formatted_private);
If all you mean to do is to use ECDSA as a signature algorithm, verification is identical to signing using using the verify methods instead of the sign methods, as follows:
byte[] message_hash = // ...
byte[] candidate_message = // ...
PublicKey pub = // ...
Signature dsa = Signature.getInstance("SHA1withECDSA");
dsa.initVerify(pub);
dsa.update(candidate_message);
boolean success = dsa.verify(message_hash);
I'm trying to demonstrate the use of RSA Public-key system to exchange messages that achieve confidentiality and integrity/authentication. I am trying to encrypt a message on the client side and send this information to the server side to decrypt. The issue I am having is that my code is not decrypting. It is giving me the following error:
javax.crypto.BadPaddingException: Data must start with zero
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:308)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:255)
at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at PKServer.decryptMessage(PKServer.java:36)
at PKServer.main(PKServer.java:69)
Public-Key Client code:
import java.io.*;
import java.net.*;
import java.security.*;
import javax.crypto.*;
public class PKClient
{
public static final int kBufferSize = 8192;
public static void main(String[] args) throws Exception
{
try {
// Generate new key
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
String message = "The quick brown fox jumps over the lazy dog.";
// Compute signature
Signature instance = Signature.getInstance("SHA1withRSA");
instance.initSign(privateKey);
instance.update((message).getBytes());
byte[] signature = instance.sign();
// Compute digest
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
byte[] digest = sha1.digest((message).getBytes());
// Encrypt digest
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encryptedMsg = cipher.doFinal(digest);
//Store the key in a file
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("KeyFile.xx"));
out.writeObject(privateKey);
out.close();
System.out.println("Client - Message: " + message);
System.out.println("Client - Encrypted: " + PKServer.asHex(encryptedMsg));
String host = "localhost";
int port = 7999;
Socket s = new Socket(host, port);
//Open stream to cipher server
DataOutputStream os = new DataOutputStream(s.getOutputStream());
os.writeInt(encryptedMsg.length);
os.write(encryptedMsg);
os.writeInt(digest.length);
os.write(digest);
os.writeInt(signature.length);
os.write(signature);
os.flush();
os.close();
//Close socket
s.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
Public-key Server code:
import java.io.*;
import java.net.*;
import java.security.*;
import javax.crypto.*;
public class PKServer
{
public void decryptMessage(InputStream inStream) throws IOException, NoSuchAlgorithmException
{
try {
//Create the Data input stream from the socket
DataInputStream dis = new DataInputStream(inStream);
//Get the key
ObjectInputStream in = new ObjectInputStream(new FileInputStream("KeyFile.xx"));
//ObjectOutputStream outSocket = new ObjectOutputStream(s.getOutputStream());
PrivateKey privatekey = (PrivateKey) in.readObject();
System.out.println("Key Used: " + in.toString());
in.close();
//Initiate the cipher
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE,privatekey);
int len = dis.readInt();
byte[] encryptedMsg = new byte[len];
dis.readFully(encryptedMsg);
System.out.println("Server - Msg Length: " + len);
System.out.println("Server - Encrypted: " + asHex(encryptedMsg));
// -Print out the decrypt String to see if it matches the original message.
byte[] plainText = cipher.doFinal(encryptedMsg);
System.out.println("Decrypted Message: " + new String(plainText, "SHA"));
} catch (Exception e) {
e.printStackTrace();
}
}
//Function to make the bytes printable (hex format)
public static String asHex(byte buf[]) {
StringBuilder strbuf = new StringBuilder(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10) {
strbuf.append("0");
}
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
public static void main(String[] args) throws Exception
{
int port = 7999;
ServerSocket server = new ServerSocket(port);
Socket s = server.accept();
PKServer cs = new PKServer();
cs.decryptMessage(s.getInputStream());
server.close();
}
}
Here is the output I receive on the server side:
Key Used: java.io.ObjectInputStream#fee4648
Server - Msg Length: 128
Server - Encrypted: 8c23b2cd96c07950f4901a670b025531b5f52be0730e4548c9a76090d7ae65a8ce82901c66acfb6bd79520cf8d86bf74bf3105fd638892a681a6905556cbadf394915fbdc09babb5b78b9dd06382e92604e9ca88901613520ccb45fcc376e813df059ebc649c52f233dc2632733d99212b42ce54e59ebd6d9dca98af36a20fc6
javax.crypto.BadPaddingException: Data must start with zero
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:308)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:255)
at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at PKServer.decryptMessage(PKServer.java:36)
at PKServer.main(PKServer.java:69)
I have noticed that if I adjust the line:
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
to
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
The program does decrypt but it does not decrypt to the intended string. It looks like this:
Decrypted Message: E???.1G?:*?$??|o\?"?
V????????O)Z?j??a?!p)6??????_??T*?c?6O????????:?(??C?,??'??`??????(?2D?mC?OLc<7?'?S?R?
Please let me know if you can see where I am going wrong with this code.
You need to encrypt your digest with public key in PKClient:
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
Key pubKey = keyPair.getPublic(); // add this
PrivateKey privateKey = keyPair.getPrivate();
// several lines later...
// Initiate the cipher
cipher.init(Cipher.ENCRYPT_MODE, pubKey); // change privateKey to pubKey
There are a number of problems with this code and with your understanding of Public Key Cryptography.
First, let's correct the Public Key Cryptography part.
The encryption part gives you confidentiality. The signature part gives you integrity and authentication. It is NOT recommended to use the same sets of keys for both signing and encrypting.
You sign with a PRIVATE key and verify the signature with a corresponding PUBLIC key.
With a DIFFERENT keypair, you encrypt with a PUBLIC key and decrypt with a PRIVATE key. That way, no one has to pass a private key to someone else.
The client just sends the client public key to the server so the server can verify the signature.
The server just sends the server public key to the client so the client can encrypt with the public key and the server can then decrypt with its own private key.
Now the code.
As I mentioned above, you should really have 2 keypairs generated, not just 1.
Your code is actually encrypting the SHA1 digest of the message, NOT the message itself.
Digests are one-way algorithms. There is no way to reverse a digest to get the original message. You are encrypting the digest and then trying to decrypt it and get the original message back. That won't work. If you decrypt it, all you can get back is the digest.
For your purposes, you do not need to compute the digest. You only need to sign and encrypt the message.
Send the signature, the encrypted message, and the client public key to the server.
A side note: RSA keys can SIGN a message of any length. However, RSA keys can only ENCRYPT a message less than the length of the key. For messages longer than the keylength, Symmetric encryption (like AES) is usually used. You can encrypt the message with an AES key, and then use the RSA key to encrypt the AES key. Send the encrypted AES key and message to the recipient. The recipient uses their private key to decrypt the AES key, and then uses that AES key to decrypt the message.
On the server, you are trying to use the same key to decrypt as you used to encrypt. RSA is an asymmetric algorithm, which means one key is used to encrypt and the other is used to decrypt. As I said above, what you really want to do is encrypt with the server's public key, and then the server will use its private key to decrypt.
Also, new String(plainText, "SHA") is not doing what you think it is. It will not be able to reverse the digest. It will probably give you a UnsupportedEncodingException because SHA is not a charset. See Java String for more details.
On the server side, you'll want to add a signature verification. Use the Java Signature class (specifically the verify method) to check the signature.
That should get you going in the right direction and get you much closer to what you want to achieve.
Good luck!
I am trying to encrypt data in java and decrypt data in ruby.
I found almost same asks, but my case is little bit different.
Encrypt in Ruby and Decrypt in Java - Why is it not working?
AES/CBC encrypt in Java, decrypt in Ruby
My code is ...
Encrypt in java
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import net.sf.json.JSONObject;
import org.apache.commons.codec.binary.Hex;
class Crypt {
public static void main(String[] args) throws Exception {
Map<String, String> node = new HashMap<String, String>();
node.put("timestamp", "1377499097199");
JSONObject jsonObject = JSONObject.fromObject(node);
String json = jsonObject.toString();
System.out.println(json);
//key
String skeyString = "97128424897797a166913557a6f4cc8e";
byte[] skey = Hex.decodeHex(skeyString.toCharArray());
System.out.println("key : " + skeyString);
//iv
String ivString = "84e8c3ea8859a0e293941d1cb00a39c3";
byte[] iv = Hex.decodeHex(ivString.toCharArray());
System.out.println("iv : " + ivString);
//encrypt
SecretKeySpec skeySpec1 = new SecretKeySpec(skey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec1, new IvParameterSpec(iv));
byte[] encrypted = cipher.doFinal(json.getBytes());
String encryptedString = Hex.encodeHexString(encrypted);
System.out.println("=============>");
System.out.println("encrypted string: " + encryptedString);
}
}
Result is
{"timestamp":"1377499097199"}
key : 97128424897797a166913557a6f4cc8e
iv : 84e8c3ea8859a0e293941d1cb00a39c3
=============>
encrypted string: 395f6c0e8ad27f57c4a5a8975aa633e5b26f288d37ce18c6971779951f3b3527
and I hope to decrypt (encrypted string) in Ruby
Ruby code is ...(got error)
require 'openssl'
key = "97128424897797a166913557a6f4cc8e"
iv = "84e8c3ea8859a0e293941d1cb00a39c3"
encrypted_string = "395f6c0e8ad27f57c4a5a8975aa633e5b26f288d37ce18c6971779951f3b3527"
de_cipher = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
de_cipher.decrypt
de_cipher.key = key
de_cipher.iv = iv
de_cipher.update(encrypted_string) << de_cipher.final
I expected to get
{"timestamp":"1377499097199"}
But it returns error
`final': bad decrypt (OpenSSL::Cipher::CipherError)
I think the problem is cipher.padding and type of key/iv.
But I don't know exactly how to complete ruby code.
Please let me know How to complete this code.
Thank you.
There's two problems with your Ruby code.
First, you're using AES 256 when you should be using AES 128. Java uses AES 128 or 256 based on the size of the key you use, and you're using a 128 bit key.
Second, you need to Hex decode your key, iv, and encrypted_string values in Ruby. OpenSSL Cipher is expecting binary, not hex strings.
require 'openssl';
key = "97128424897797a166913557a6f4cc8e";
iv = "84e8c3ea8859a0e293941d1cb00a39c3";
encrypted_string = "395f6c0e8ad27f57c4a5a8975aa633e5b26f288d37ce18c6971779951f3b3527";
de_cipher = OpenSSL::Cipher::Cipher.new("AES-128-CBC");
de_cipher.decrypt;
de_cipher.key = [key].pack('H*');
de_cipher.iv = [iv].pack('H*');
puts de_cipher.update([encrypted_string].pack('H*')) << de_cipher.final;
Output:
{"timestamp":"1377499097199"}