Classes utilitaire qui fournit des fonctionnalités cryptographiques pour le chiffrement et le déchiffrement symétriques (AES) et asymétriques (RSA).

This commit is contained in:
2025-10-24 16:51:37 -07:00
parent 350544524b
commit 49cbbf691d

View File

@@ -0,0 +1,267 @@
package common;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.util.Base64;
import java.util.logging.Level;
import java.util.logging.Logger;
import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
/**
* Static utility methods to support encryption and decryption. ePub uses only AES and RSA.
* RC4 may be needed in the future to support encrypted PDF files.
*/
@SuppressWarnings("UnnecessaryLocalVariable")
public class CryptoUtils
{
public enum CRYPTO_ALGO
{
ALGO_AES, ALGO_RC4, ALGO_RSA, ALGO_MD5, ALGO_SHA1, ALGO_SHA256
}
public enum CHAINING_MODE
{
CHAIN_ECB, CHAIN_CBC
}
enum RSAKeyType
{
RSA_KEY_PKCS12, RSA_KEY_X509, RSA_KEY_PKCS8, RSA_KEY_UNKNOWN
}
public static int SHA1_LEN = 20;
public static int RSA_KEY_SIZE = 128; // NOTE: this is less than the standard key size of 256 bytes
// static because the methods in this class are all static
private static final Logger LOGGER = Logger.getLogger( CryptoUtils.class.getName() );
private CryptoUtils() {} // prevents instantiation
/**
* Encrypts or Decrypts data using a symmetric key
*
* @param algo Algorithm to use
* @param chain Chaining mode
* @param key Key
* @param initialzationVector IV key - not used for rc4
* @param inData Data to encrypt
* @return The encrypted data
*/
public static byte[] crypt( CRYPTO_ALGO algo, CHAINING_MODE chain, int cryptMode,
byte[] key, byte[] initialzationVector, byte[] inData )
{
try
{
Cipher cipher;
/*
This class specifies a secret key in a provider-independent fashion.
It can be used to construct a SecretKey from a byte array, without having to go through a
(provider-based) SecretKeyFactory.
This class is only useful for raw secret keys that can be represented as a byte array and
have no key parameters associated with them, e.g., DES or Triple DES keys.
*/
SecretKeySpec secretKeySpec;
if (algo == CRYPTO_ALGO.ALGO_AES && chain == CHAINING_MODE.CHAIN_CBC)
{
cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" );
secretKeySpec = new SecretKeySpec( key, "AES" );
cipher.init( cryptMode, secretKeySpec,
new IvParameterSpec( initialzationVector ) );
}
else if (algo == CRYPTO_ALGO.ALGO_RC4 && chain == CHAINING_MODE.CHAIN_ECB)
{
cipher = Cipher.getInstance( "RC4" ); // No padding for RC4
secretKeySpec = new SecretKeySpec( key, "RC4" );
cipher.init( cryptMode, secretKeySpec );
}
else
{
throw new NoSuchAlgorithmException( "Unsupported algorithm or chain: " + algo + "/" + chain );
}
/*
Encrypts or decrypts the data depending on the value of cryptMode.
Acts in a single-part operation, or finishes a multiple-part operation.
*/
return cipher.doFinal( inData );
}
catch (Exception e)
{
LOGGER.log( Level.SEVERE, "Encryption failed: " + e.getMessage(), e );
throw new GourouException( "Encryption failed", e );
}
}
/**
* Decrypts data using a symmetric key and the AES algorithm
* @param key 16 byte aes encryption key
* @param initializationVector a 16 byte initialization vector, usually prepended to the actual encrypted data
* @param encryptedData The encrypted data to decrypt. Does not include the initialization vector.
* @return The decrypted data
* @throws GeneralSecurityException If any part of the decryption fails
*/
public static byte[] aesDecrypt( byte[] key, byte[] initializationVector, byte[] encryptedData )
throws GeneralSecurityException
{
Cipher cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" );
SecretKeySpec secretKey = new SecretKeySpec( key, "AES" );
cipher.init( DECRYPT_MODE, secretKey, new IvParameterSpec( initializationVector ) );
return cipher.doFinal( encryptedData );
}
/**
* Encrypts data using the AES algorithm. It uses the random initialization vector
* created by default in cipher.
*
* @param key 16 byte aes encryption key
* @param unencryptedData The data to encrypt
* @return The encrypted data preceded by the 16 byte initialization vector
* @throws GeneralSecurityException If any part of the encryption fails
*/
public static byte[] aesEncrypt( byte[] key, byte[] unencryptedData )
throws GeneralSecurityException
{
Cipher cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" );
SecretKeySpec secretKey = new SecretKeySpec( key, "AES" );
cipher.init( ENCRYPT_MODE, secretKey );
byte[] encryptedData = cipher.doFinal( unencryptedData );
byte[] iv = cipher.getIV();
byte[] finalEncrypteData = new byte[16 + encryptedData.length];
System.arraycopy( iv, 0, finalEncrypteData, 0, 16 );
System.arraycopy( encryptedData, 0, finalEncrypteData, 16, encryptedData.length );
return finalEncrypteData;
}
/**
* Encrypts data using an RSA key.
*
* @param rsaKey The RSA key bytes. May be public or private
* @param data The data to encrypt.
* @return The encrypted data.
*
* @throws GeneralSecurityException If any part of the encryption fails
*/
public static byte[] rsaEncrypt( Key rsaKey, byte[] data )
throws GeneralSecurityException
{
Cipher cipher = Cipher.getInstance( "RSA/ECB/PKCS1Padding" );
cipher.init( ENCRYPT_MODE, rsaKey );
byte[] enc = cipher.doFinal( data );
return enc;
}
/* *
* Decrypts data using an RSA key.
*
* @param rsaKey The RSA Key bytes.
* @param data The data to decrypt.
*
* @return The decrypted data.
*/
// public static byte[] rsaDecrypt( Key rsaKey, byte[] data )
// throws GeneralSecurityException
// {
// Cipher cipher = Cipher.getInstance( "RSA/ECB/PKCS1Padding" );
// cipher.init( Cipher.DECRYPT_MODE, rsaKey );
// byte[] decrytped = cipher.doFinal( data );
// return decrytped;
// }
/**
* Encrypts or decrypts data using the RSA algorithm and the public key embedded
* in an X509 Certificate. Useful for creating or verifying digital signatures.
*
* @param x509Cert a base64 representation of an X509 certificate
* @param cryptMode flag for encryption or decryption
* @param data The data to treat
*
* @return The converted data
*
* @throws GeneralSecurityException if any part of the encryption or decryption fails.
*/
public static byte[] x509Crypt( String x509Cert, int cryptMode, byte[] data )
throws GeneralSecurityException
{
Cipher cipher = Cipher.getInstance( "RSA/ECB/PKCS1Padding" );
PublicKey pk = extractPKFromCert( x509Cert );
cipher.init( cryptMode, pk );
return cipher.doFinal( data );
}
/**
* Create a digital signature for arbitrary data, using a PKCS#12 private key.
*
* @param data data to hash using the SHA1 algorithm, and then RSA encrypt
* @param pkcs12 a base 64 representation of the PKCS#12 certificate
* @param alias the subject name associated with the key. It is assumed
* that it is stored without a password
* @param key a 16 byte key to unlock the private key.
* @return a hash of the data encrypted with the private key from the PKCS#12 entry
*
* @throws GeneralSecurityException if any part of the signing process fails.
* @throws IOException if the PKCS#12 data cannot be loaded
*/
public static byte[] signData( byte[] data, String pkcs12, String alias, byte[] key )
throws GeneralSecurityException, IOException
{
MessageDigest shaMd = MessageDigest.getInstance( "SHA1" );
byte[] shaOut = shaMd.digest( data );
Base64.Decoder decoder = Base64.getDecoder();
byte[] pkcs12Data = decoder.decode( pkcs12 );
KeyStore keyStore = // KeyStore.getInstance( "PKCS12" );
extractPkcsKeystore( pkcs12Data, null );
String dk = Base64.getEncoder().encodeToString( key );
char[] keyPassword = dk.toCharArray();
// this keystore contains a private key entry, but may not have a public key certificate!
RSAPrivateKey rsaKey = (RSAPrivateKey) keyStore.getKey( alias, keyPassword );
byte[] encryptedHash = CryptoUtils.rsaEncrypt( rsaKey, shaOut );
return encryptedHash;
}
static public PublicKey extractPKFromCert( String authenticationCertificate )
throws CertificateException
{
byte[] certBytes = Base64.getDecoder().decode( authenticationCertificate );
CertificateFactory cf = CertificateFactory.getInstance( "X.509" );
X509Certificate x509AuthCertificate = (X509Certificate) cf.generateCertificate(
new ByteArrayInputStream( certBytes ) );
PublicKey pk = x509AuthCertificate.getPublicKey();
return pk;
}
/**
*
* @param pkcs12Data The pkcs12 data as a byte array, usually decoded from base64
* @param password The password protecting the pkcs12 data; may be empty
* @return A java KeyStore object initialized with a PKCS12 key pair.
*/
public static KeyStore extractPkcsKeystore( byte[] pkcs12Data, char[] password )
throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException
{
KeyStore keyStore;
ByteArrayInputStream inputStream = new ByteArrayInputStream( pkcs12Data) ;
keyStore = KeyStore.getInstance( "PKCS12" );
// Load the keystore from the input stream and password
keyStore.load( inputStream, password );
return keyStore;
}
}