When I generate a private key using the following code
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.genKeyPair();
PrivateKey privateKey = kp.getPrivate();
I get OpenSSLRSAPrivateCrtKey representation of PrivateKey. Then I turn it into byte array, save and then restore using this code:
byte[] encodedPrivateKey = null;
fileInputStream = new FileInputStream(file);
encodedPrivateKey = new byte[(int) file.length()];
fileInputStream.read(encodedPrivateKey);
fileInputStream.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
But this time I get OpenSSLRSAPrivateKey representation.
I want to know what is the difference between OpenSSLRSAPrivateCrtKey
and OpenSSLRSAPrivateKey
. Also which representation of PrivateKey should I use to decrypt information encrypted by public key from this keypair?
OpenSSLRSAPrivateCrtKey extends
OpenSSLRSAPrivateKey.
private BigInteger publicExponent;
private BigInteger primeP;
private BigInteger primeQ;
private BigInteger primeExponentP;
private BigInteger primeExponentQ;
private BigInteger crtCoefficient;
OpenSSLRSAPrivateCrtKey(RSAPrivateCrtKeySpec rsaKeySpec) throws InvalidKeySpecException {
super(init(rsaKeySpec));
}
Initially it was
private static OpenSSLKey init(RSAPrivateKeySpec rsaKeySpec) throws InvalidKeySpecException {
final BigInteger modulus = rsaKeySpec.getModulus();
final BigInteger privateExponent = rsaKeySpec.getPrivateExponent();
if (modulus == null) {
throw new InvalidKeySpecException("modulus == null");
} else if (privateExponent == null) {
throw new InvalidKeySpecException("privateExponent == null");
}
try {
return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
modulus.toByteArray(),
null,
privateExponent.toByteArray(),
null,
null,
null,
null,
null));
} catch (Exception e) {
throw new InvalidKeySpecException(e);
}
}
Now it is
private static OpenSSLKey init(RSAPrivateCrtKeySpec rsaKeySpec) throws InvalidKeySpecException {
BigInteger modulus = rsaKeySpec.getModulus();
BigInteger privateExponent = rsaKeySpec.getPrivateExponent();
if (modulus == null) {
throw new InvalidKeySpecException("modulus == null");
} else if (privateExponent == null) {
throw new InvalidKeySpecException("privateExponent == null");
}
try {
/*
* OpenSSL uses the public modulus to do RSA blinding. If
* the public modulus is not available, the call to
* EVP_PKEY_new_RSA will turn off blinding for this key
* instance.
*/
final BigInteger publicExponent = rsaKeySpec.getPublicExponent();
final BigInteger primeP = rsaKeySpec.getPrimeP();
final BigInteger primeQ = rsaKeySpec.getPrimeQ();
final BigInteger primeExponentP = rsaKeySpec.getPrimeExponentP();
final BigInteger primeExponentQ = rsaKeySpec.getPrimeExponentQ();
final BigInteger crtCoefficient = rsaKeySpec.getCrtCoefficient();
return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
modulus.toByteArray(),
publicExponent == null ? null : publicExponent.toByteArray(),
privateExponent.toByteArray(),
primeP == null ? null : primeP.toByteArray(),
primeQ == null ? null : primeQ.toByteArray(),
primeExponentP == null ? null : primeExponentP.toByteArray(),
primeExponentQ == null ? null : primeExponentQ.toByteArray(),
crtCoefficient == null ? null : crtCoefficient.toByteArray()));
} catch (Exception e) {
throw new InvalidKeySpecException(e);
}
}
As we can see, when OpenSSLKey is instantiated, we pass NativeCrypto.EVP_PKEY_new_RSA
in both cases. OpenSSLRSAPrivateCrtKey passes:
It changed from
static OpenSSLRSAPrivateKey getInstance(OpenSSLKey key) {
byte[][] params = NativeCrypto.get_RSA_private_params(key.getNativeRef());
if (params[1] != null) {
return new OpenSSLRSAPrivateCrtKey(key, params);
}
return new OpenSSLRSAPrivateKey(key, params);
}
to
static OpenSSLKey getInstance(RSAPrivateCrtKey rsaPrivateKey) throws InvalidKeyException {
/**
* If the key is not encodable (PKCS11-like key), then wrap it and use
* JNI upcalls to satisfy requests.
*/
if (rsaPrivateKey.getFormat() == null) {
return wrapPlatformKey(rsaPrivateKey);
}
BigInteger modulus = rsaPrivateKey.getModulus();
BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
if (modulus == null) {
throw new InvalidKeyException("modulus == null");
} else if (privateExponent == null) {
throw new InvalidKeyException("privateExponent == null");
}
try {
/*
* OpenSSL uses the public modulus to do RSA blinding. If
* the public modulus is not available, the call to
* EVP_PKEY_new_RSA will turn off blinding for this key
* instance.
*/
final BigInteger publicExponent = rsaPrivateKey.getPublicExponent();
final BigInteger primeP = rsaPrivateKey.getPrimeP();
final BigInteger primeQ = rsaPrivateKey.getPrimeQ();
final BigInteger primeExponentP = rsaPrivateKey.getPrimeExponentP();
final BigInteger primeExponentQ = rsaPrivateKey.getPrimeExponentQ();
final BigInteger crtCoefficient = rsaPrivateKey.getCrtCoefficient();
return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
modulus.toByteArray(),
publicExponent == null ? null : publicExponent.toByteArray(),
privateExponent.toByteArray(),
primeP == null ? null : primeP.toByteArray(),
primeQ == null ? null : primeQ.toByteArray(),
primeExponentP == null ? null : primeExponentP.toByteArray(),
primeExponentQ == null ? null : primeExponentQ.toByteArray(),
crtCoefficient == null ? null : crtCoefficient.toByteArray()));
} catch (Exception e) {
throw new InvalidKeyException(e);
}
}
The new version computes
If this failed, then some exception is thrown. Otherwise, it continues to compute stuff, namely:
and then generates an OpenSSLKey
similarly as in init
. I think this could be refactored, because it involves some code duplication, but my opinion is less important than the differences.
This changed from
void readParams(byte[][] params) {
if (params[0] == null) {
throw new NullPointerException("modulus == null");
} else if (params[2] == null && !key.isHardwareBacked()) {
throw new NullPointerException("privateExponent == null");
}
modulus = new BigInteger(params[0]);
// ENGINE-based keys are not guaranteed to have a private exponent.
if (params[2] != null) {
privateExponent = new BigInteger(params[2]);
}
}
to
@Override
synchronized void readParams(byte[][] params) {
super.readParams(params);
// params[0] read in super.readParams
if (params[1] != null) {
publicExponent = new BigInteger(params[1]);
}
// params[2] read in super.readParams
if (params[3] != null) {
primeP = new BigInteger(params[3]);
}
if (params[4] != null) {
primeQ = new BigInteger(params[4]);
}
if (params[5] != null) {
primeExponentP = new BigInteger(params[5]);
}
if (params[6] != null) {
primeExponentQ = new BigInteger(params[6]);
}
if (params[7] != null) {
crtCoefficient = new BigInteger(params[7]);
}
}
because of the new members
@Override
public BigInteger getPublicExponent() {
ensureReadParams();
return publicExponent;
}
@Override
public BigInteger getPrimeP() {
ensureReadParams();
return primeP;
}
@Override
public BigInteger getPrimeQ() {
ensureReadParams();
return primeQ;
}
@Override
public BigInteger getPrimeExponentP() {
ensureReadParams();
return primeExponentP;
}
@Override
public BigInteger getPrimeExponentQ() {
ensureReadParams();
return primeExponentQ;
}
@Override
public BigInteger getCrtCoefficient() {
ensureReadParams();
return crtCoefficient;
}
Has changed from
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof OpenSSLRSAPrivateKey) {
OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o;
return key.equals(other.getOpenSSLKey());
}
if (o instanceof RSAPrivateKey) {
ensureReadParams();
RSAPrivateKey other = (RSAPrivateKey) o;
return modulus.equals(other.getModulus())
&& privateExponent.equals(other.getPrivateExponent());
}
return false;
}
to
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof OpenSSLRSAPrivateKey) {
OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o;
return getOpenSSLKey().equals(other.getOpenSSLKey());
}
if (o instanceof RSAPrivateCrtKey) {
ensureReadParams();
RSAPrivateCrtKey other = (RSAPrivateCrtKey) o;
if (getOpenSSLKey().isHardwareBacked()) {
return getModulus().equals(other.getModulus())
&& publicExponent.equals(other.getPublicExponent());
} else {
return getModulus().equals(other.getModulus())
&& publicExponent.equals(other.getPublicExponent())
&& getPrivateExponent().equals(other.getPrivateExponent())
&& primeP.equals(other.getPrimeP()) && primeQ.equals(other.getPrimeQ())
&& primeExponentP.equals(other.getPrimeExponentP())
&& primeExponentQ.equals(other.getPrimeExponentQ())
&& crtCoefficient.equals(other.getCrtCoefficient());
}
} else if (o instanceof RSAPrivateKey) {
ensureReadParams();
RSAPrivateKey other = (RSAPrivateKey) o;
if (getOpenSSLKey().isHardwareBacked()) {
return getModulus().equals(other.getModulus());
} else {
return getModulus().equals(other.getModulus())
&& getPrivateExponent().equals(other.getPrivateExponent());
}
}
return false;
}
because of the new members.
@Override
public final int hashCode() {
int hashCode = super.hashCode();
if (publicExponent != null) {
hashCode ^= publicExponent.hashCode();
}
return hashCode;
}
If publicExponent is null, then it's the same as in the base class. If not, then hashCode is computed based on the base-classes' hash code and publicExponent's hash code, calculating the bitwise XOR between the two.
Changed from
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateKey{");
ensureReadParams();
sb.append("modulus=");
sb.append(modulus.toString(16));
return sb.toString();
}
to
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateCrtKey{");
ensureReadParams();
sb.append("modulus=");
sb.append(getModulus().toString(16));
if (publicExponent != null) {
sb.append(',');
sb.append("publicExponent=");
sb.append(publicExponent.toString(16));
}
sb.append('}');
return sb.toString();
}
Changed from
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
modulus.toByteArray(),
null,
privateExponent.toByteArray(),
null,
null,
null,
null,
null));
fetchedParams = true;
}
to
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
modulus.toByteArray(),
publicExponent == null ? null : publicExponent.toByteArray(),
privateExponent.toByteArray(),
primeP == null ? null : primeP.toByteArray(),
primeQ == null ? null : primeQ.toByteArray(),
primeExponentP == null ? null : primeExponentP.toByteArray(),
primeExponentQ == null ? null : primeExponentQ.toByteArray(),
crtCoefficient == null ? null : crtCoefficient.toByteArray()));
fetchedParams = true;
}
Changed from
private void writeObject(ObjectOutputStream stream) throws IOException {
if (key.isHardwareBacked()) {
throw new NotSerializableException("Hardware backed keys can not be serialized");
}
ensureReadParams();
stream.defaultWriteObject();
}
to
private void writeObject(ObjectOutputStream stream) throws IOException {
if (getOpenSSLKey().isHardwareBacked()) {
throw new NotSerializableException("Hardware backed keys cannot be serialized");
}
ensureReadParams();
stream.defaultWriteObject();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With