How to validate a public and private key pair in Java - java

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.

Related

Java RSA-OAEP generate key pair

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

How can I generate a RSA key for use with com.auth0 java-jwt?

https://github.com/auth0/java-jwt
States that setting up the algorithm for JWT should be as simple as
//RSA
RSAPublicKey publicKey = //Get the key instance
RSAPrivateKey privateKey = //Get the key instance
Algorithm algorithmRS = Algorithm.RSA256(publicKey, privateKey);
The problem is I can't work out how to create an RSAPublicKey and RSAPrivateKey instance without touching the filesystem.
It should be secure.
It shouldn't create the key on the file system, as I plan on storing it via another method.
Normally this is the sort of thing I'd guess at until I get right, but considering it's cryptography I want to do the right thing.
keygen = KeyPairGenerator.getInstance("RSA");
RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4); //What does F4 mean vs F0?
keygen.initialize(spec);
KeyPair keypair = keygen.generateKeyPair();
PublicKey pub = keypair.getPublic(); //Wrong type, need RSAPublicKey
PrivateKey priv = keypair.getPrivate(); //Wrong type, need RSAPrivateKey
You can directly cast the public and private keys to RSAPublicKey and RSAPrivateKey because you are using a RSA KeyPairGenerator
RSAPublicKey rsaPublicKey = (RSAPublicKey) keypair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keypair.getPrivate();
You can get the key content using key.getEncoded(); (no cast needed) and store it as a byte array any way you like

Java DSA Signature: fixed key size and signature size

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!

Converting byte array to publickey ECDSA

I need to use ECDSA algorithm to sign a message and send to receiver in java. Then, receiver should verify sender's signature.
So, for this, receiver has sender's public key but in byte array format after converting java.security.PublicKey to byte array by the command bellow:
byte[] byteArrayPublicKey = publickey.getEncoded();
The format of public key in ECDSA algorithm (before converting it to byte array) is as follow:
Public Key:
X: 8a83c389e7bb817c17bf2db4ed71055f18342b630221b2a3a1ca752502dc2e21
Y: 3eaf48c9ab1700fe0966a0cde196b85af66bb8f0bacef711c9dca2368f9d8470
But, the problem is to convert this byte array to usable format to verify the signature that is java.security.PublicKey by receiver.
In general, is there any solution to verify the signature without converting it to byte array? In the other word, the problem is to verify the signature by sender's public key, using any method.
But, the problem is to convert this byte array to usable format to verify the signature that is java.security.PublicKey by receiver.
You can solve the problem like this way:
public static ECPublicKey genEcPubKey() throws Exception {
KeyFactory factory = KeyFactory.getInstance("ECDSA", "BC");
java.security.PublicKey ecPublicKey = (ECPublicKey) factory
.generatePublic(new X509EncodedKeySpec(Helper
.toByte(ecRemotePubKey))); // Helper.toByte(ecRemotePubKey)) is java.security.PublicKey#getEncoded()
return (ECPublicKey) ecPublicKey;
}
Note that, you need BouncyCastle provider to do that.
But question remains, how you generate the private key?
public KeyPair ecKeyPairGenerator(String curveName) throws Exception {
KeyPair keyPair;
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
"ECDSA", "BC");
ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec(
curveName);
keyPairGenerator.initialize(ecGenParameterSpec, new SecureRandom());
keyPair = keyPairGenerator.generateKeyPair();
java.security.PublicKey ecPublicKey = (ECPublicKey) keyPair.getPublic();
System.out.println("JAVA EC PublicKey: "
+ Helper.toHex(ecPublicKey.getEncoded()));
// write private key into a file. Just for testing purpose
FileOutputStream fileOutputStream = new FileOutputStream(
"ECPrivateKey.key");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(
fileOutputStream);
objectOutputStream.writeObject(keyPair.getPrivate());
objectOutputStream.close();
return keyPair;
}
I have the full running code for EC sign/verify in github. You can take a look for better understanding.

Android KeyPairGenerator always generates the same key pair

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!

Categories

Resources