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!
Related
I am trying to interface with external system which requires to send an RSA-OAEP SHA-256 public key with key size of 2048.
I am getting an error that the key has wrong bit length: '2350' .
I tried online encryption/decryption services using the generated KP and they seems to accept the key. What I am doing wrong?
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
SecureRandom random = new SecureRandom();
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
generator.initialize(2048, random);
KeyPair pair = generator.generateKeyPair();
Key pubKey = pair.getPublic();
Key privKey = pair.getPrivate();
// I am sending pubKey.getEncoded()
Example result of the public key:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuUKRnE07rwQ8Juegbhabv1kaHOrfou8+FNHYyhjn8jUPgwumALonsvztt4goivc1Bwm6sFwCIGnxf+2y1BI1saG3w5S1R24EUAN7efi7CS4LMEhtgJtavtWYEkEZj7OaU/BLnkB1rFAmBDU3vudSd3Gupgnbqtw7VjOH6Qrfnsh3phVr6DdHruq7SftOvCyhBucOax0hwt6enRs5UjBfbgDbbSMFaFdF4jZpE4Jnfl9gwRF51QP934Il1djPT6cezuEYlD8VAklLFPR+rOL73nvBCxLdZwdBlQHO8J8XGjWaNmAMHvyisFxkD8Ud9nC7m9MPb9J6+n3cWG7OL+C97QIDAQAB
Thank you
Is there a way to validate in java if the given private key, say certain *.key file matches with the certain public key, to a certain .pub file using RSA algorithm?
You can verify if a key pair matches by
creating a challenge (random byte sequence of sufficient length)
signing the challenge with the private key
verifying the signature using the public key
This gives you a sufficiently high confidence (almost certainity) that a key pair matches if the signature verification is ok, and an absolute certainity that a key pair does not match otherwise.
Example code:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// create a challenge
byte[] challenge = new byte[10000];
ThreadLocalRandom.current().nextBytes(challenge);
// sign using the private key
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(privateKey);
sig.update(challenge);
byte[] signature = sig.sign();
// verify signature using the public key
sig.initVerify(publicKey);
sig.update(challenge);
boolean keyPairMatches = sig.verify(signature);
This also works with Elliptic Curve (EC) key pairs, but you need to use a different signature algorithm (SHA256withECDSA):
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
keyGen.initialize(new ECGenParameterSpec("sect571k1"));
...
Signature sig = Signature.getInstance("SHA256withECDSA");
The answer that was marked as being correct wastes a lot of CPU cycles. This answer is waaaay more CPU efficient:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyPair.getPrivate();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// comment this out to verify the behavior when the keys are different
//keyPair = keyGen.generateKeyPair();
//publicKey = (RSAPublicKey) keyPair.getPublic();
boolean keyPairMatches = privateKey.getModulus().equals(publicKey.getModulus()) &&
privateKey.getPublicExponent().equals(publicKey.getPublicExponent());
(the other answer signs a message with the private key and then verifies it with the public key whereas my answer checks to see if the modulus and public exponent are the same)
boolean keyPairMatches = privateKey.getModulus().equals(publicKey.getModulus()) && privateKey.getPublicExponent().equals(publicKey.getPublicExponent());
java.security.interfaces.RSAPrivateKey doesn't have getPublicExponent() method.
org.bouncycastle.asn1.pkcs.RSAPrivateKey has getPublicExponent() method.
So,if you don't want to use bouncycastle, you have to use the sign&verify answer.
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!
In my Android application working with SpongyCastle, I want to perform ECDH Key Exchange using a specific elliptic curve (prime192v1) and I am using this code to generate byte[] representation of private and public params:
try{
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDH", "SC");
org.spongycastle.jce.spec.ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("prime192v1");
g.initialize(ecSpec, new SecureRandom());
KeyPair kp1 = g.generateKeyPair();
KeyPair kp2 = g.generateKeyPair();
// Alice generated this
byte[] privK1 = kp1.getPrivate().getEncoded();
byte[] pubK1= kp1.getPrivate().getEncoded();
// Bob generated this
byte[] privK2 = kp2.getPrivate().getEncoded();
byte[] pubK2= kp2.getPrivate().getEncoded();
}catch(Exception e)
{
Log.e(LOGTAG, "Exception caught in ECDHInit function");
}
now lets assume Alice and Bob save their private keys locally, exchange pubK1 and pubK2 and want to proceed with setting a shared secret (they have both keys now in byte[] format). I will describe only case of Alice, as for Bob this is identical:
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "SC");
PrivateKey privateKey = KeyFactory.getInstance("ECDH", "SC").generatePrivate(new PKCS8EncodedKeySpec(privK1));
PublicKey publicKey = KeyFactory.getInstance("ECDH", "SC").generatePublic(new X509EncodedKeySpec(pubK2));
Here comes my question -
using the following code, I manage to get same shared secret for both Alice and Bob
keyAgreement.init(privateKey);
keyAgreement.doPhase(publicKey, true);
byte[] sharedSecret = SHA256(keyAgreement.generateSecret());
but I specify nowhere my special curve prime192v1
If I try to supply this in this way:
org.spongycastle.jce.spec.ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("prime192v1");
keyAgreement.init(privateKey, ecSpec);
keyAgreement.doPhase(publicKey, true);
byte[] sharedSecret = SHA256(keyAgreement.generateSecret());
an Exception is thrown saying
java.security.InvalidAlgorithmParameterException: No algorithm parameters supported
Why can't I supply my desired parameter? Does the KeyAgreement somehow deduce this from the private key?
Thanks!
So it turns out I had the answer right there in front of me in the Android Studio debugger. Upon inspecting PrivateKey object I found internal member privateKey.ecSpec which contains all the details of selected curve. So passing arguments is unnecessary and throws an error, use the code without providing additional ecSpec
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).