I want to encrypt the DSA secret key with the RSA public key using java. However, when I do so, I get this error:
javax.crypto.IllegalBlockSizeException: Data must not be longer than 245 bytes
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:337)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:382)
DSA and RSA key size is set to 1024 and 2048 respectively. I know that using RSA we cannot encrypt messages having size more than the RSA key size. However, in this case, DSA key size is less than RSA key size.
I guess the problem is related to the getEncode() function because when I checked the return value of this function, I understood that the size of result is 335 byte.
I want to know how I can fix this problem? (I do not want to increase RSA the key size). I set DSA key size to 1024. Why DSA key size has the size of 335 byte after encoding?
DSA and RSA keygen functions as well as RSA encryption functions are as follow:
public static KeyPair generateDSAKey() {
KeyPair pair = null;
try {
KeyPairGenerator keyGen = KeyPairGenerator
.getInstance("DSA", "SUN");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(1024, random);
pair = keyGen.generateKeyPair();
} catch (Exception e) {
e.printStackTrace();
}
return pair;
}
public static KeyPair generateRSAKey() {
KeyPairGenerator kpg;
KeyPair kp = null;
try {
kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
kp = kpg.genKeyPair();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return kp;
}
public static byte[] encryptRSA(byte[] msg, PublicKey pubKey) {
byte[] cipherData = null;
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
cipherData = cipher.doFinal(msg);
} catch (Exception e) {
e.printStackTrace();
}
return cipherData;
}
and I call this function for encrypting DSA key with RSA public key:
PrivateKey WSK = Crypto.generateDSAKey().getPrivate();
encWSK = encryptRSA(WSK.getEncoded(), RSAPublicKey);
A DSA private key contains the algorithm parameters as well as the x value. Below is an example of a private key printed to stdout:
Sun DSA Private Key
parameters:
p:
fd7f5381 1d751229 52df4a9c 2eece4e7 f611b752 3cef4400 c31e3f80 b6512669
455d4022 51fb593d 8d58fabf c5f5ba30 f6cb9b55 6cd7813b 801d346f f26660b7
6b9950a5 a49f9fe8 047b1022 c24fbba9 d7feb7c6 1bf83b57 e7c6a8a6 150f04fb
83f6d3c5 1ec30235 54135a16 9132f675 f3ae2b61 d72aeff2 2203199d d14801c7
q:
9760508f 15230bcc b292b982 a2eb840b f0581cf5
g:
f7e1a085 d69b3dde cbbcab5c 36b857b9 7994afbb fa3aea82 f9574c0b 3d078267
5159578e bad4594f e6710710 8180b449 167123e8 4c281613 b7cf0932 8cc8a6e1
3c167a8b 547c8d28 e0a3ae1e 2bb3a675 916ea37f 0bfa2135 62f1fb62 7a01243b
cca4f1be a8519089 a883dfe1 5ae59f06 928b665e 807b5525 64014c3b fecf492a
x: 1f853beb d6e30242 cd12bd28 e7055830 22ac43a8
You could simply encrypt the x value, however that assumes your recipient already knows the algorithm parameters p, q and g.
Or you can send the x value encrypted and the parameters unencrypted (thanks GregS).
Related
I am encrypting a string using RSA algorithm and encryption and decryption logic is
public class RsaEncrypt {
private static final String ALGORITHM = "RSA";
public static void main(String[] args) {
String filePath = "/home/Desktop/abc.jks";
char[] password = "changeit".toCharArray();
String alias = "123";
KeyStore ks = null;
try {
//loading the keystore
ks = KeyStore.getInstance("JKS");
InputStream readStream = new FileInputStream(filePath);
ks.load(readStream, password);
Certificate cert = ks.getCertificate(alias);
PublicKey publicKey = cert.getPublicKey();
PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password);
RsaEncrypt e = new RsaEncrypt();
String result = e.encrypt("abvhdh", publicKey);
String decryptResult = e.decrypt(result.getBytes(), privateKey);
} catch (Exception e) {
e.printStackTrace();
}
}
//Encryption of a string
public String encrypt(String text,PublicKey publicKey) {
String retVal = null;
byte[] cipherText = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipherText = cipher.doFinal(text.getBytes());
cipherText = Base64.getEncoder().encode(cipherText);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return new String(cipherText) ;
}
// Decryption of a string
private String decrypt(byte[] text, PrivateKey privatekey) {
byte[] dectyptedText = null;
try {
final Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privatekey);
dectyptedText = cipher.doFinal(Base64.getDecoder().decode(text));
} catch (Exception ex) {
ex.printStackTrace();
} catch (Throwable e) {
throw new RuntimeException(e);
}
return new String(dectyptedText);
}
The Result is fine.But if i encrypt same string one more time i am getting different encryption value.
For that i am using "RSA/ECB/NoPadding" cipher instead of "RSA",then If i encrypt one string many times i am getting same encryption value.
But when i decrypt,The reuslt string contains some null characters
Example
input : abcd output : abcd \u0000 \u0000 \u0000 \u0000....
How can i resolve this problem and what is the best way to get same encryption value if we encrypt multiple times?????
The Result is fine.But if i encrypt same string one more time i am getting different encryption value.
That is correct and it is even required property of the RSA encryption. By default PKCS1.5 padding is used (RSA/ECB/PKCS1Padding) which contains some random bytes. Safer option is RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING mode which is even more random.
For that i am using "RSA/ECB/NoPadding" cipher instead of "RSA",then If i encrypt one string many times i am getting same encryption value. But when i decrypt,The reuslt string contains some null characters
Using RSA without padding (NoPadding) is very unsafe (it is called textbook RSA).
Padding extends the original encrypted value to full space length (e.g. 2048 bits) and then the RSA magic (exponentiation) will be executed. Using the NoPadding parameter you are telling the crypto library that you will do the padding yourself. In that case you are expected to remove the padding after decryption (in your case zero padding)
I hope you are doing that for learning / academic purposes, not some real security project. You may have a look at my blog about encryption to get some examples.
btw: you should not use RSA to encrypt the plaintext itself. Rather use symmetric encryption to encrypt the plaintext and then RSA to encrypt the symmetric encryption key.
I am doing a AES encryption , in which i will use a secret key from cert file as below to initialise the cipher.
encryptModeCipher = Cipher.getInstance("AES");
encryptModeCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
But the problem i see here is that, my secretKey () remains the same for all the certificates that i use. Any suuggestion why? and suggest a good idea to do so.
byte[] encryptionKey = Arrays.copyOf(encoded, 32);
secretKey = new SecretKeySpec(encryptionKey, algorithm);
public class AESEncryptionServiceHelper {
private String algorithm = "AES";
private String certPass;
private SecretKey secretKey;
public SecretKey setKey() {
try {
certPass="****";
char[] pass = certPass.toCharArray();
KeyStore keyStore = KeyStore.getInstance("jceks");
File file = new File("D:/aws-kms-dps/***.jks");
InputStream inputStream = new FileInputStream(file);
keyStore.load(inputStream, pass);
Certificate cert = keyStore.getCertificate("****");
Key key = cert.getPublicKey();
secretKey = new SecretKeySpec(key.getEncoded(), algorithm);
byte[] encoded = secretKey.getEncoded();
byte[] encryptionKey = Arrays.copyOf(encoded, 32);
secretKey = new SecretKeySpec(encryptionKey, algorithm);
} catch (IOException e) {
System.out.println(e);
} catch (Exception e) {
System.out.println(e);
}
return secretKey;
}
public static void main(String args[]){
AESEncryptionServiceHelper aesEncryptionServiceHelper=new AESEncryptionServiceHelper();
aesEncryptionServiceHelper.setKey();
}
}
You seems you are usging (part of) the public key as an AES key. That is VERY BAD idea as
the public key is .. well .. public and static
it has relatively low entropy (as multiple bytes are defined in the ASN.1 format)
Did you do any research how to properly do encryption using PKI or you are just guessing / plaing with the crypto API?
Let's assume you want to do encryption using the public key and AES (it is called hybrid encryption), you could take example from my blog
Please read it and understand (or any other good blogs about cryptography), seems you are missing using IV (salt) and MAC
// generate random AES key
KeyGenerator keyGenerator = KeyGenerator.getInstance(SYMMETRIC_KEY_ALG);
SecretKey symmetricKey = keyGenerator.generateKey();
// this assumes there's whole keypair (including private key)
// normally only a certificate with PubKey is available
PublicKey pubKey = keystoreEntry.getCertificate().getPublicKey();
params.setKey(symmetricKey.getEncoded());
// execute symmetric encryption
this.symmetricEncryption(params);
// encrypt the key with the public key
Cipher cipher = Cipher.getInstance(PKI_CIPHER_ALG);
cipher.init(Cipher.WRAP_MODE, pubKey);
byte[] wrappedKey = cipher.wrap(symmetricKey);
LOGGER.log(Level.INFO, "Wrapped key: {0}", Base64.getEncoder().encodeToString(wrappedKey));
params.setKey(wrappedKey);
where the symetric encryption itself can be implemented as follows
// initialization vector
SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[SYMMETRIC_BLOCK_SIZE / 8];
rnd.nextBytes(iv);
encryptionParams.setIv(iv);
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
SecretKey symmetricKey = new SecretKeySpec(encryptionParams.getKey(), SYMMETRIC_KEY_ALG);
Cipher cipher = Cipher.getInstance(SYMMETRIC_CIPHER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, symmetricKey, ivParamSpec);
// for HMAC we should be able to use the same key as for encryption
// for CBC-MAC it may not be the case
// https://en.wikipedia.org/wiki/CBC-MAC#Using_the_same_key_for_encryption_and_authentication
Mac mac = Mac.getInstance(EncryptionTest.HASH_ALGORITHM_NAME);
mac.init(symmetricKey);
byte[] encrypted = cipher.doFinal(encryptionParams.getPlaintext());
encryptionParams.setCiphertext(encrypted);
byte[] authTag = mac.doFinal(encrypted);
encryptionParams.setMac(authTag);
I have the following code that signs some String data:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
keyGen.initialize(1024, random);
KeyPair pair = keyGen.generateKeyPair();
PrivateKey privateK = pair.getPrivate();
PublicKey publicK = pair.getPublic();
Signature dsa = Signature.getInstance("SHA1withDSA");
dsa.initSign(privateK);
dsa.update(data.getBytes());
byte[] signature = dsa.sign();
String hexSignature = DatatypeConverter.printHexBinary(signature);
String hexPublicK = DatatypeConverter.printHexBinary(publicK.getEncoded());
However, it's giving me a varying public key size and signature size.
For example,
Why is that? I want both the public key and the signature produced for some data to have fixed sizes.
Thank you for your help!
I am making an application which generates a key pair for a user. But in every device the keys are identical. Here is my code:
public KeyPair generateKeys() {
KeyPair keyPair = null;
try {
// get instance of rsa cipher
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024); // initialize key generator
keyPair = keyGen.generateKeyPair(); // generate pair of keys
} catch(GeneralSecurityException e) {
System.out.println(e);
}
return keyPair;
}
And to show the generated keys code is:
KeyPair keyPair = rsa.generateKeys();
byte[] publicKey = keyPair.getPublic().getEncoded();
byte[] privateKey = keyPair.getPrivate().getEncoded();
privateText.setText( Base64.encodeToString(privateKey, Base64.NO_WRAP) );
publicText.setText( Base64.encodeToString(publicKey, Base64.NO_WRAP) );
The key generation is called only one time for each android device, and for that reason the keys in each device should be different.. Can anyone tell me what i am missing here?
I believe you are only looking at the first few or last few bits. I thought I had the same problem too but when I looked at the bits in the middle, they were indeed different!
private static String decrypt(String cipherString, PrivateKey key) {
byte[] dectyptedText = null;
byte[] stringText = null;
try {
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance(ALGORITHM);
//chiper init in encrypt mode
cipher.init(Cipher.ENCRYPT_MODE, key);
//tried to get bytes out of encrypted string
stringText = cipher.doFinal(cipherString.getBytes());
// decrypt the text using the private key
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(stringText);
} catch (Exception ex) {
ex.printStackTrace();
}
return new String(dectyptedText);
}
I want to convert the cipher text into bytes generated by the encryptor to a string and store in a database. Then get the string and decrypt it whenever its needed. Is there anyone that could help me solving the issue I'm having?
I does not make sense to convert the byte-Array to String.
You have to either save the bytes directly (which would require an appropriate column in the database, for example BLOB), or you could encode the byte-Array, for example using Base64 (I would recommend the latter).
(If your problems are with the "public-crypto thingy", you may want to use the public key to encrypt, but the private key to decrypt. If you dont know what that means, check out some literature about public-key crypto, please.)
Since your problem seems to be with your key, you possibly need a public key and a private key, not only a private key.
Have a look at this simple RSA demo:
encryptionAlgorithm = "RSA/ECB/PKCS1Padding";
algorithm = "RSA";
try {
SecureRandom random = SecRandom.getDefault();
// Since you are working with asymmetric crypto, you need a keypair:
KeyPairGenerator kpg = KeyPairGenerator.getInstance(algorithm);
kpg.initialize(2048, random);
KeyPair kp = kpg.generateKeyPair();
// encrypting something with asymmetric crypto needs a public key:
Cipher cipher1 = Cipher.getInstance(encryptionAlgorithm);
cipher1.init(Cipher.ENCRYPT_MODE, kp.getPublic());
byte[] text = "This is a test".getBytes("ASCII");
System.out.println("text = " +(new String(text)));
byte[] ciphertext = cipher1.doFinal(text);
// here you could store & load your sipertext
System.out.println("ciphertext = " + ciphertext);
// decrypting something with asymmetric crypto needs a private key:
Cipher cipher2 = Cipher.getInstance(encryptionAlgorithm);
cipher2.init(Cipher.DECRYPT_MODE, kp.getPrivate());
byte[] cleartext = cipher2.doFinal(ciphertext);
System.out.println("cleartext = " +(new String(cleartext)));
} catch (Exception e) {
e.printStackTrace();
}