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:
267
src/main/java/common/CryptoUtils.java
Normal file
267
src/main/java/common/CryptoUtils.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user