org.cryptacular.util.KeyPairUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.saml.opensaml.integration Show documentation
Show all versions of com.liferay.saml.opensaml.integration Show documentation
Liferay SAML OpenSAML Integration
/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.signers.DSASigner;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.crypto.signers.RSADigestSigner;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.cryptacular.EncodingException;
import org.cryptacular.StreamException;
import org.cryptacular.adapter.Converter;
import org.cryptacular.asn.OpenSSLPrivateKeyDecoder;
import org.cryptacular.asn.PKCS8PrivateKeyDecoder;
import org.cryptacular.asn.PublicKeyDecoder;
/**
* Utility methods for public/private key pairs used for asymmetric encryption.
*
* @author Middleware Services
*/
public final class KeyPairUtil
{
/** Data used to verify key pairs. */
private static final byte[] SIGN_BYTES = ByteUtil.toBytes("Mr. Watson--come here--I want to see you.");
/** Private constructor of utility class. */
private KeyPairUtil() {}
/**
* Gets the length in bits of a public key where key size is dependent on the particulars of the algorithm.
*
*
* - DSA - length of p
* - EC - length of p for prime fields, m for binary fields
* - RSA - length of modulus
*
*
* @param pubKey Public key.
*
* @return Size of the key in bits.
*/
public static int length(final PublicKey pubKey)
{
final int size;
if (pubKey instanceof DSAPublicKey) {
size = ((DSAPublicKey) pubKey).getParams().getP().bitLength();
} else if (pubKey instanceof RSAPublicKey) {
size = ((RSAPublicKey) pubKey).getModulus().bitLength();
} else if (pubKey instanceof ECPublicKey) {
size = ((ECPublicKey) pubKey).getParams().getCurve().getField().getFieldSize();
} else {
throw new IllegalArgumentException(pubKey + " not supported.");
}
return size;
}
/**
* Gets the length in bits of a private key where key size is dependent on the particulars of the algorithm.
*
*
* - DSA - length of q in bits
* - EC - length of p for prime fields, m for binary fields
* - RSA - modulus length in bits
*
*
* @param privKey Private key.
*
* @return Size of the key in bits.
*/
public static int length(final PrivateKey privKey)
{
final int size;
if (privKey instanceof DSAPrivateKey) {
size = ((DSAPrivateKey) privKey).getParams().getQ().bitLength();
} else if (privKey instanceof RSAPrivateKey) {
size = ((RSAPrivateKey) privKey).getModulus().bitLength();
} else if (privKey instanceof ECPrivateKey) {
size = ((ECPrivateKey) privKey).getParams().getCurve().getField().getFieldSize();
} else {
throw new IllegalArgumentException(privKey + " not supported.");
}
return size;
}
/**
* Determines whether the given public and private keys form a proper key pair by computing and verifying a digital
* signature with the keys.
*
* @param pubKey DSA, RSA or EC public key.
* @param privKey DSA, RSA, or EC private key.
*
* @return True if the keys form a functioning keypair, false otherwise. Errors during signature verification are
* treated as false.
*
* @throws org.cryptacular.CryptoException on key validation errors.
*/
public static boolean isKeyPair(final PublicKey pubKey, final PrivateKey privKey)
throws org.cryptacular.CryptoException
{
final String alg = pubKey.getAlgorithm();
if (!alg.equals(privKey.getAlgorithm())) {
return false;
}
// Dispatch onto the algorithm-specific method
final boolean result;
switch (alg) {
case "DSA":
result = isKeyPair((DSAPublicKey) pubKey, (DSAPrivateKey) privKey);
break;
case "RSA":
result = isKeyPair((RSAPublicKey) pubKey, (RSAPrivateKey) privKey);
break;
case "EC":
result = isKeyPair((ECPublicKey) pubKey, (ECPrivateKey) privKey);
break;
default:
throw new IllegalArgumentException(alg + " not supported.");
}
return result;
}
/**
* Determines whether the given DSA public and private keys form a proper key pair by computing and verifying a
* digital signature with the keys.
*
* @param pubKey DSA public key.
* @param privKey DSA private key.
*
* @return True if the keys form a functioning keypair, false otherwise. Errors during signature verification are
* treated as false.
*
* @throws org.cryptacular.CryptoException on key validation errors.
*/
public static boolean isKeyPair(final DSAPublicKey pubKey, final DSAPrivateKey privKey)
throws org.cryptacular.CryptoException
{
final DSASigner signer = new DSASigner();
final DSAParameters params = new DSAParameters(
privKey.getParams().getP(),
privKey.getParams().getQ(),
privKey.getParams().getG());
try {
signer.init(true, new DSAPrivateKeyParameters(privKey.getX(), params));
final BigInteger[] sig = signer.generateSignature(SIGN_BYTES);
signer.init(false, new DSAPublicKeyParameters(pubKey.getY(), params));
return signer.verifySignature(SIGN_BYTES, sig[0], sig[1]);
} catch (RuntimeException e) {
throw new org.cryptacular.CryptoException("Signature computation error", e);
}
}
/**
* Determines whether the given RSA public and private keys form a proper key pair by computing and verifying a
* digital signature with the keys.
*
* @param pubKey RSA public key.
* @param privKey RSA private key.
*
* @return True if the keys form a functioning keypair, false otherwise. Errors during signature verification are
* treated as false.
*
* @throws org.cryptacular.CryptoException on key validation errors.
*/
public static boolean isKeyPair(final RSAPublicKey pubKey, final RSAPrivateKey privKey)
throws org.cryptacular.CryptoException
{
final RSADigestSigner signer = new RSADigestSigner(new SHA256Digest());
try {
signer.init(true, new RSAKeyParameters(true, privKey.getModulus(), privKey.getPrivateExponent()));
signer.update(SIGN_BYTES, 0, SIGN_BYTES.length);
final byte[] sig = signer.generateSignature();
signer.init(false, new RSAKeyParameters(false, pubKey.getModulus(), pubKey.getPublicExponent()));
signer.update(SIGN_BYTES, 0, SIGN_BYTES.length);
return signer.verifySignature(sig);
} catch (Exception e) {
throw new org.cryptacular.CryptoException("Signature computation error", e);
}
}
/**
* Determines whether the given EC public and private keys form a proper key pair by computing and verifying a digital
* signature with the keys.
*
* @param pubKey EC public key.
* @param privKey EC private key.
*
* @return True if the keys form a functioning keypair, false otherwise. Errors during signature verification are
* treated as false.
*
* @throws org.cryptacular.CryptoException on key validation errors.
*/
public static boolean isKeyPair(final ECPublicKey pubKey, final ECPrivateKey privKey)
throws org.cryptacular.CryptoException
{
final ECDSASigner signer = new ECDSASigner();
try {
signer.init(true, ECUtil.generatePrivateKeyParameter(privKey));
final BigInteger[] sig = signer.generateSignature(SIGN_BYTES);
signer.init(false, ECUtil.generatePublicKeyParameter(pubKey));
return signer.verifySignature(SIGN_BYTES, sig[0], sig[1]);
} catch (Exception e) {
throw new org.cryptacular.CryptoException("Signature computation error", e);
}
}
/**
* Reads an encoded private key from a file at the given path. Both PKCS#8 and OpenSSL "traditional" formats are
* supported in DER or PEM encoding. See {@link #decodePrivateKey(byte[])} for supported asymmetric algorithms.
*
* @param path Path to private key file.
*
* @return Private key.
*
* @throws EncodingException on key encoding errors.
* @throws StreamException on IO errors reading data from file.
*/
public static PrivateKey readPrivateKey(final String path) throws EncodingException, StreamException
{
return readPrivateKey(new File(path));
}
/**
* Reads an encoded private key from a file. Both PKCS#8 and OpenSSL "traditional" formats are supported in DER or PEM
* encoding. See {@link #decodePrivateKey(byte[])} for supported asymmetric algorithms.
*
* @param file Private key file.
*
* @return Private key.
*
* @throws EncodingException on key encoding errors.
* @throws StreamException on IO errors reading data from file.
*/
public static PrivateKey readPrivateKey(final File file) throws EncodingException, StreamException
{
try {
return readPrivateKey(new FileInputStream(file));
} catch (FileNotFoundException e) {
throw new StreamException("File not found: " + file);
}
}
/**
* Reads an encoded private key from an input stream. Both PKCS#8 and OpenSSL "traditional" formats are supported in
* DER or PEM encoding. See {@link #decodePrivateKey(byte[])} for supported asymmetric algorithms. The {@link
* InputStream} parameter is closed by this method.
*
* @param in Input stream containing private key data.
*
* @return Private key.
*
* @throws EncodingException on key encoding errors.
* @throws StreamException on IO errors reading data from file.
*/
public static PrivateKey readPrivateKey(final InputStream in) throws EncodingException, StreamException
{
return decodePrivateKey(StreamUtil.readAll(in));
}
/**
* Reads an encrypted private key from a file at the given path. Both PKCS#8 and OpenSSL "traditional" formats are
* supported in DER or PEM encoding. See {@link #decodePrivateKey(byte[])} for supported asymmetric algorithms.
*
* @param path Path to private key file.
* @param password Password used to encrypt private key.
*
* @return Private key.
*
* @throws EncodingException on key encoding errors.
* @throws StreamException on IO errors.
*/
public static PrivateKey readPrivateKey(final String path, final char[] password)
throws EncodingException, StreamException
{
return readPrivateKey(new File(path), password);
}
/**
* Reads an encrypted private key from a file. Both PKCS#8 and OpenSSL "traditional" formats are supported in DER or
* PEM encoding. See {@link #decodePrivateKey(byte[])} for supported asymmetric algorithms.
*
* @param file Private key file.
* @param password Password used to encrypt private key.
*
* @return Private key.
*
* @throws EncodingException on key encoding errors.
* @throws StreamException on IO errors.
*/
public static PrivateKey readPrivateKey(final File file, final char[] password)
throws EncodingException, StreamException
{
try {
return readPrivateKey(new FileInputStream(file), password);
} catch (FileNotFoundException e) {
throw new StreamException("File not found: " + file);
}
}
/**
* Reads an encrypted private key from an input stream. Both PKCS#8 and OpenSSL "traditional" formats are supported in
* DER or PEM encoding. See {@link #decodePrivateKey(byte[])} for supported asymmetric algorithms. The {@link
* InputStream} parameter is closed by this method.
*
* @param in Input stream containing private key data.
* @param password Password used to encrypt private key.
*
* @return Private key.
*
* @throws EncodingException on key encoding errors.
* @throws StreamException on IO errors.
*/
public static PrivateKey readPrivateKey(final InputStream in, final char[] password)
throws EncodingException, StreamException
{
return decodePrivateKey(StreamUtil.readAll(in), password);
}
/**
* Decodes an encoded private key in either PKCS#8 or OpenSSL "traditional" format in either DER or PEM encoding. Keys
* from the following asymmetric algorithms are supported:
*
*
* - DSA
* - RSA
* - Elliptic curve
*
*
* @param encodedKey Encoded private key data.
*
* @return Private key.
*
* @throws EncodingException on key encoding errors.
*/
public static PrivateKey decodePrivateKey(final byte[] encodedKey) throws EncodingException
{
return decodePrivateKey(encodedKey, null);
}
/**
* Decodes an encrypted private key. The following formats are supported:
*
*
* - DER or PEM encoded PKCS#8 format
* - PEM encoded OpenSSL "traditional" format
*
*
* Keys from the following asymmetric algorithms are supported:
*
*
* - DSA
* - RSA
* - Elliptic curve
*
*
* @param encryptedKey Encrypted private key data.
* @param password Password used to encrypt private key.
*
* @return Private key.
*
* @throws EncodingException on key encoding errors.
*/
public static PrivateKey decodePrivateKey(final byte[] encryptedKey, final char[] password) throws EncodingException
{
AsymmetricKeyParameter key;
try {
final PKCS8PrivateKeyDecoder decoder = new PKCS8PrivateKeyDecoder();
key = decoder.decode(encryptedKey, password);
} catch (RuntimeException e) {
final OpenSSLPrivateKeyDecoder decoder = new OpenSSLPrivateKeyDecoder();
key = decoder.decode(encryptedKey, password);
}
return Converter.convertPrivateKey(key);
}
/**
* Reads a DER or PEM-encoded public key from a file.
*
* @param path Path to DER or PEM-encoded public key file.
*
* @return Public key.
*
* @throws EncodingException on key encoding errors.
* @throws StreamException on IO errors.
*/
public static PublicKey readPublicKey(final String path) throws EncodingException, StreamException
{
return readPublicKey(new File(path));
}
/**
* Reads a DER or PEM-encoded public key from a file.
*
* @param file DER or PEM-encoded public key file.
*
* @return Public key.
*
* @throws EncodingException on key encoding errors.
* @throws StreamException on IO errors.
*/
public static PublicKey readPublicKey(final File file) throws EncodingException, StreamException
{
try {
return readPublicKey(new FileInputStream(file));
} catch (FileNotFoundException e) {
throw new StreamException("File not found: " + file);
}
}
/**
* Reads a DER or PEM-encoded public key from data in the given stream. The {@link InputStream} parameter is closed by
* this method.
*
* @param in Input stream containing an encoded key.
*
* @return Public key.
*
* @throws EncodingException on key encoding errors.
* @throws StreamException on IO errors.
*/
public static PublicKey readPublicKey(final InputStream in) throws EncodingException, StreamException
{
return decodePublicKey(StreamUtil.readAll(in));
}
/**
* Decodes public keys formatted in an X.509 SubjectPublicKeyInfo structure in either PEM or DER encoding.
*
* @param encoded Encoded public key bytes.
*
* @return Public key.
*
* @throws EncodingException on key encoding errors.
*/
public static PublicKey decodePublicKey(final byte[] encoded) throws EncodingException
{
return Converter.convertPublicKey(new PublicKeyDecoder().decode(encoded));
}
}