How to validate a public and private key pair in Java
Asked Answered
O

3

9

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?

Olsson answered 22/3, 2018 at 10:55 Comment(2)
You did not specify the algorithm used, but maybe this is helpful: #4428736Duaneduarchy
Its RSA algorithm, specified in the tags and updated the question. And thanks for the link.Olsson
P
18

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");
Pinball answered 22/3, 2018 at 11:15 Comment(5)
Thanks a lot, it worked! And I must research on this code too.Olsson
And can you provide any link related to this, so I can understand it's working?Olsson
Java tutorial: generating and verifying signatures -> docs.oracle.com/javase/tutorial/security/apisign/index.htmlPinball
Usually signatures are used to verify that a message was not altered. It can also be used to identify the parties (one owning the private key, the other owning the public key) by using a challenge that is signed and then verified. We use this for authentication: server poses a challenge, client signs it (with the private key) and the server validates the identity by verifying the signed challenge (with the client's public key).Pinball
Thanks for the explanation, will look into the link too.Olsson
I
9

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)

Ingrained answered 3/2, 2021 at 5:30 Comment(3)
This answer does not work with general key pairs (EC, DSA, ...) but only with RSA key pairs.Pinball
@PeterWalser - that's true but the OP's question was "using RSA algorithm". But even then, you still don't need to sign / verify, even with EC or DSA. With EC you'd check to make sure that the curve is the same along with the QA parameter. Technically, I suppose a EC private key might not have QA available but I feel like most real world EC keys would just as most real world RSA private keys have the public exponent embedded within them. I mean, like with RSA, technically, all you need is the modulus and the private exponent but most real world RSA private keys include CRT components as wellIngrained
In theory an RSA privatekey can be n,d only but in practice everyone uses the CRT-form of PKCS1 A.1.2 which requires e. For X9-type EC SEC1 makes the public point OPTIONAL and I've seen plenty both ways -- but in Java the interface ECPrivateKey can't access the public even if it is in the provider object. DSA, DH, EdDSA and XDH private keys standardly don't contain the public, although some things (not Java) sometimes use rfc5958 to add it.Pilgrim
S
0

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.

Sadism answered 3/11, 2022 at 8:1 Comment(2)
interfaces.RSAPrivateKey doesn't, but in practice more than 99.9999% of RSA private key objects in Java actually implement (and can be cast to) the subinterface interfaces.RSAPrivateCrtKey which does, as neubert's code correctly shows.Pilgrim
And for that matter, Bouncy asn1.pkcs.RSAPrivateKey only works if you have a CRT-from encoding, and in that case you can write an ad-hoc DER parser good enough to get n and e in about 10 lines of code.Pilgrim

© 2022 - 2024 — McMap. All rights reserved.