org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto Maven / Gradle / Ivy
package org.bouncycastle.tls.crypto.impl.jcajce;
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPublicKey;
import java.util.Hashtable;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.jcajce.util.JcaJceHelper;
import org.bouncycastle.tls.AlertDescription;
import org.bouncycastle.tls.EncryptionAlgorithm;
import org.bouncycastle.tls.HashAlgorithm;
import org.bouncycastle.tls.MACAlgorithm;
import org.bouncycastle.tls.NamedGroup;
import org.bouncycastle.tls.ProtocolVersion;
import org.bouncycastle.tls.SignatureAlgorithm;
import org.bouncycastle.tls.SignatureAndHashAlgorithm;
import org.bouncycastle.tls.TlsFatalAlert;
import org.bouncycastle.tls.TlsUtils;
import org.bouncycastle.tls.crypto.SRP6Group;
import org.bouncycastle.tls.crypto.TlsCertificate;
import org.bouncycastle.tls.crypto.TlsCipher;
import org.bouncycastle.tls.crypto.TlsCryptoException;
import org.bouncycastle.tls.crypto.TlsCryptoParameters;
import org.bouncycastle.tls.crypto.TlsDHConfig;
import org.bouncycastle.tls.crypto.TlsDHDomain;
import org.bouncycastle.tls.crypto.TlsECConfig;
import org.bouncycastle.tls.crypto.TlsECDomain;
import org.bouncycastle.tls.crypto.TlsHMAC;
import org.bouncycastle.tls.crypto.TlsHash;
import org.bouncycastle.tls.crypto.TlsNonceGenerator;
import org.bouncycastle.tls.crypto.TlsSRP6Client;
import org.bouncycastle.tls.crypto.TlsSRP6Server;
import org.bouncycastle.tls.crypto.TlsSRP6VerifierGenerator;
import org.bouncycastle.tls.crypto.TlsSRPConfig;
import org.bouncycastle.tls.crypto.TlsSecret;
import org.bouncycastle.tls.crypto.impl.AbstractTlsCrypto;
import org.bouncycastle.tls.crypto.impl.TlsAEADCipher;
import org.bouncycastle.tls.crypto.impl.TlsAEADCipherImpl;
import org.bouncycastle.tls.crypto.impl.TlsBlockCipher;
import org.bouncycastle.tls.crypto.impl.TlsBlockCipherImpl;
import org.bouncycastle.tls.crypto.impl.TlsEncryptor;
import org.bouncycastle.tls.crypto.impl.TlsImplUtils;
import org.bouncycastle.tls.crypto.impl.TlsNullCipher;
import org.bouncycastle.tls.crypto.impl.jcajce.srp.SRP6Client;
import org.bouncycastle.tls.crypto.impl.jcajce.srp.SRP6Server;
import org.bouncycastle.tls.crypto.impl.jcajce.srp.SRP6VerifierGenerator;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Integers;
/**
* Class for providing cryptographic services for TLS based on implementations in the JCA/JCE.
*
* This class provides default implementations for everything. If you need to customise it, extend the class
* and override the appropriate methods.
*
*/
public class JcaTlsCrypto
extends AbstractTlsCrypto
{
private final JcaJceHelper helper;
private final SecureRandom entropySource;
private final SecureRandom nonceEntropySource;
private final Hashtable supportedEncryptionAlgorithms = new Hashtable();
private final Hashtable supportedNamedGroups = new Hashtable();
private final Hashtable supportedOther = new Hashtable();
/**
* Base constructor.
*
* @param helper a JCA/JCE helper configured for the class's default provider.
* @param entropySource primary entropy source, used for key generation.
* @param nonceEntropySource secondary entropy source, used for nonce and IV generation.
*/
protected JcaTlsCrypto(JcaJceHelper helper, SecureRandom entropySource, SecureRandom nonceEntropySource)
{
this.helper = helper;
this.entropySource = entropySource;
this.nonceEntropySource = nonceEntropySource;
}
JceTlsSecret adoptLocalSecret(byte[] data)
{
return new JceTlsSecret(this, data);
}
Cipher createRSAEncryptionCipher() throws GeneralSecurityException
{
try
{
return getHelper().createCipher("RSA/NONE/PKCS1Padding");
}
catch (GeneralSecurityException e)
{
return getHelper().createCipher("RSA/ECB/PKCS1Padding"); // try old style
}
}
public TlsNonceGenerator createNonceGenerator(byte[] additionalSeedMaterial)
{
return new JcaNonceGenerator(nonceEntropySource, additionalSeedMaterial);
}
public SecureRandom getSecureRandom()
{
return entropySource;
}
public byte[] calculateKeyAgreement(String agreementAlgorithm, PrivateKey privateKey, PublicKey publicKey, String secretAlgorithm)
throws GeneralSecurityException
{
KeyAgreement agreement = helper.createKeyAgreement(agreementAlgorithm);
agreement.init(privateKey);
agreement.doPhase(publicKey, true);
try
{
return agreement.generateSecret(secretAlgorithm).getEncoded();
}
catch (NoSuchAlgorithmException e)
{
// Oracle provider currently does not support generateSecret(algorithmName) for these.
if ("X25519".equals(agreementAlgorithm) || "X448".equals(agreementAlgorithm))
{
return agreement.generateSecret();
}
throw e;
}
}
public TlsCertificate createCertificate(byte[] encoding)
throws IOException
{
return new JcaTlsCertificate(this, encoding);
}
protected TlsCipher createCipher(TlsCryptoParameters cryptoParams, int encryptionAlgorithm, int macAlgorithm)
throws IOException
{
try
{
switch (encryptionAlgorithm)
{
case EncryptionAlgorithm._3DES_EDE_CBC:
return createDESedeCipher(cryptoParams, macAlgorithm);
case EncryptionAlgorithm.AES_128_CBC:
return createAESCipher(cryptoParams, 16, macAlgorithm);
case EncryptionAlgorithm.AES_128_CCM:
// NOTE: Ignores macAlgorithm
return createCipher_AES_CCM(cryptoParams, 16, 16);
case EncryptionAlgorithm.AES_128_CCM_8:
// NOTE: Ignores macAlgorithm
return createCipher_AES_CCM(cryptoParams, 16, 8);
case EncryptionAlgorithm.AES_128_GCM:
// NOTE: Ignores macAlgorithm
return createCipher_AES_GCM(cryptoParams, 16, 16);
case EncryptionAlgorithm.AES_128_OCB_TAGLEN96:
// NOTE: Ignores macAlgorithm
return createCipher_AES_OCB(cryptoParams, 16, 12);
case EncryptionAlgorithm.AES_256_CBC:
return createAESCipher(cryptoParams, 32, macAlgorithm);
case EncryptionAlgorithm.AES_256_CCM:
// NOTE: Ignores macAlgorithm
return createCipher_AES_CCM(cryptoParams, 32, 16);
case EncryptionAlgorithm.AES_256_CCM_8:
// NOTE: Ignores macAlgorithm
return createCipher_AES_CCM(cryptoParams, 32, 8);
case EncryptionAlgorithm.AES_256_GCM:
// NOTE: Ignores macAlgorithm
return createCipher_AES_GCM(cryptoParams, 32, 16);
case EncryptionAlgorithm.AES_256_OCB_TAGLEN96:
// NOTE: Ignores macAlgorithm
return createCipher_AES_OCB(cryptoParams, 32, 12);
case EncryptionAlgorithm.ARIA_128_CBC:
return createARIACipher(cryptoParams, 16, macAlgorithm);
case EncryptionAlgorithm.ARIA_128_GCM:
// NOTE: Ignores macAlgorithm
return createCipher_ARIA_GCM(cryptoParams, 16, 16);
case EncryptionAlgorithm.ARIA_256_CBC:
return createARIACipher(cryptoParams, 32, macAlgorithm);
case EncryptionAlgorithm.ARIA_256_GCM:
// NOTE: Ignores macAlgorithm
return createCipher_ARIA_GCM(cryptoParams, 32, 16);
case EncryptionAlgorithm.CAMELLIA_128_CBC:
return createCamelliaCipher(cryptoParams, 16, macAlgorithm);
case EncryptionAlgorithm.CAMELLIA_128_GCM:
// NOTE: Ignores macAlgorithm
return createCipher_Camellia_GCM(cryptoParams, 16, 16);
case EncryptionAlgorithm.CAMELLIA_256_CBC:
return createCamelliaCipher(cryptoParams, 32, macAlgorithm);
case EncryptionAlgorithm.CAMELLIA_256_GCM:
// NOTE: Ignores macAlgorithm
return createCipher_Camellia_GCM(cryptoParams, 32, 16);
case EncryptionAlgorithm.CHACHA20_POLY1305:
// NOTE: Ignores macAlgorithm
return createChaCha20Poly1305(cryptoParams);
case EncryptionAlgorithm.NULL:
return createNullCipher(cryptoParams, macAlgorithm);
case EncryptionAlgorithm.SEED_CBC:
return createSEEDCipher(cryptoParams, macAlgorithm);
case EncryptionAlgorithm.DES40_CBC:
case EncryptionAlgorithm.DES_CBC:
case EncryptionAlgorithm.IDEA_CBC:
case EncryptionAlgorithm.RC2_CBC_40:
case EncryptionAlgorithm.RC4_128:
case EncryptionAlgorithm.RC4_40:
default:
throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
catch (GeneralSecurityException e)
{
throw new TlsCryptoException("cannot create cipher: " + e.getMessage(), e);
}
}
public TlsHMAC createHMAC(short hashAlgorithm)
{
String digestName = getDigestName(hashAlgorithm).replaceAll("-", "");
String hmacName = "Hmac" + digestName;
return createHMAC(hmacName);
}
public TlsHMAC createHMAC(int macAlgorithm)
{
switch (macAlgorithm)
{
case MACAlgorithm._null:
return null;
case MACAlgorithm.hmac_md5:
return createHMAC("HmacMD5");
case MACAlgorithm.hmac_sha1:
return createHMAC("HmacSHA1");
case MACAlgorithm.hmac_sha256:
return createHMAC("HmacSHA256");
case MACAlgorithm.hmac_sha384:
return createHMAC("HmacSHA384");
case MACAlgorithm.hmac_sha512:
return createHMAC("HmacSHA512");
default:
throw new IllegalArgumentException("unknown MACAlgorithm: " + MACAlgorithm.getText(macAlgorithm));
}
}
public TlsSRP6Client createSRP6Client(TlsSRPConfig srpConfig)
{
final SRP6Client srpClient = new SRP6Client();
BigInteger[] ng = srpConfig.getExplicitNG();
SRP6Group srpGroup= new SRP6Group(ng[0], ng[1]);
srpClient.init(srpGroup, createHash(HashAlgorithm.sha1), this.getSecureRandom());
return new TlsSRP6Client()
{
public BigInteger calculateSecret(BigInteger serverB)
throws TlsFatalAlert
{
try
{
return srpClient.calculateSecret(serverB);
}
catch (IllegalArgumentException e)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
}
public BigInteger generateClientCredentials(byte[] srpSalt, byte[] identity, byte[] password)
{
return srpClient.generateClientCredentials(srpSalt, identity, password);
}
};
}
public TlsSRP6Server createSRP6Server(TlsSRPConfig srpConfig, BigInteger srpVerifier)
{
final SRP6Server srpServer = new SRP6Server();
BigInteger[] ng = srpConfig.getExplicitNG();
SRP6Group srpGroup= new SRP6Group(ng[0], ng[1]);
srpServer.init(srpGroup, srpVerifier, createHash(HashAlgorithm.sha1), this.getSecureRandom());
return new TlsSRP6Server()
{
public BigInteger generateServerCredentials()
{
return srpServer.generateServerCredentials();
}
public BigInteger calculateSecret(BigInteger clientA)
throws IOException
{
try
{
return srpServer.calculateSecret(clientA);
}
catch (IllegalArgumentException e)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
}
};
}
public TlsSRP6VerifierGenerator createSRP6VerifierGenerator(TlsSRPConfig srpConfig)
{
BigInteger[] ng = srpConfig.getExplicitNG();
final SRP6VerifierGenerator verifierGenerator = new SRP6VerifierGenerator();
verifierGenerator.init(ng[0], ng[1], createHash(HashAlgorithm.sha1));
return new TlsSRP6VerifierGenerator()
{
public BigInteger generateVerifier(byte[] salt, byte[] identity, byte[] password)
{
return verifierGenerator.generateVerifier(salt, identity, password);
}
};
}
public boolean hasAllRawSignatureAlgorithms()
{
// TODO[RFC 8422] Revisit the need to buffer the handshake for "Intrinsic" hash signatures
return !JcaUtils.isSunMSCAPIProviderActive()
&& !hasSignatureAlgorithm(SignatureAlgorithm.ed25519)
&& !hasSignatureAlgorithm(SignatureAlgorithm.ed448);
}
public boolean hasDHAgreement()
{
return true;
}
public boolean hasECDHAgreement()
{
return true;
}
public boolean hasEncryptionAlgorithm(int encryptionAlgorithm)
{
final Integer key = Integers.valueOf(encryptionAlgorithm);
synchronized (supportedEncryptionAlgorithms)
{
Boolean cached = (Boolean)supportedEncryptionAlgorithms.get(key);
if (cached != null)
{
return cached.booleanValue();
}
}
boolean result = true;
try
{
switch (encryptionAlgorithm)
{
case EncryptionAlgorithm.CHACHA20_POLY1305:
{
helper.createCipher("ChaCha7539");
helper.createMac("Poly1305");
break;
}
case EncryptionAlgorithm._3DES_EDE_CBC:
{
helper.createCipher("DESede/CBC/NoPadding");
break;
}
case EncryptionAlgorithm.AES_128_CBC:
case EncryptionAlgorithm.AES_256_CBC:
{
helper.createCipher("AES/CBC/NoPadding");
break;
}
case EncryptionAlgorithm.AES_128_CCM:
case EncryptionAlgorithm.AES_128_CCM_8:
case EncryptionAlgorithm.AES_256_CCM:
case EncryptionAlgorithm.AES_256_CCM_8:
{
helper.createCipher("AES/CCM/NoPadding");
break;
}
case EncryptionAlgorithm.AES_128_GCM:
case EncryptionAlgorithm.AES_256_GCM:
{
helper.createCipher("AES/GCM/NoPadding");
break;
}
case EncryptionAlgorithm.AES_128_OCB_TAGLEN96:
case EncryptionAlgorithm.AES_256_OCB_TAGLEN96:
{
helper.createCipher("AES/OCB/NoPadding");
break;
}
case EncryptionAlgorithm.ARIA_128_CBC:
case EncryptionAlgorithm.ARIA_256_CBC:
{
helper.createCipher("ARIA/CBC/NoPadding");
break;
}
case EncryptionAlgorithm.ARIA_128_GCM:
case EncryptionAlgorithm.ARIA_256_GCM:
{
helper.createCipher("ARIA/GCM/NoPadding");
break;
}
case EncryptionAlgorithm.CAMELLIA_128_CBC:
case EncryptionAlgorithm.CAMELLIA_256_CBC:
{
helper.createCipher("Camellia/CBC/NoPadding");
break;
}
case EncryptionAlgorithm.CAMELLIA_128_GCM:
case EncryptionAlgorithm.CAMELLIA_256_GCM:
{
helper.createCipher("Camellia/GCM/NoPadding");
break;
}
case EncryptionAlgorithm.SEED_CBC:
{
helper.createCipher("SEED/CBC/NoPadding");
break;
}
case EncryptionAlgorithm.NULL:
{
break;
}
case EncryptionAlgorithm.DES40_CBC:
case EncryptionAlgorithm.DES_CBC:
case EncryptionAlgorithm.IDEA_CBC:
case EncryptionAlgorithm.RC2_CBC_40:
case EncryptionAlgorithm.RC4_128:
case EncryptionAlgorithm.RC4_40:
default:
{
result = false;
break;
}
}
}
catch (GeneralSecurityException e)
{
result = false;
}
synchronized (supportedEncryptionAlgorithms)
{
supportedEncryptionAlgorithms.put(key, Boolean.valueOf(result));
}
return result;
}
public boolean hasHashAlgorithm(short hashAlgorithm)
{
// TODO: expand
return true;
}
public boolean hasMacAlgorithm(int macAlgorithm)
{
// TODO: expand
return true;
}
public boolean hasNamedGroup(int namedGroup)
{
// TODO[tls] Actually check for DH support for the individual groups
if (NamedGroup.refersToASpecificFiniteField(namedGroup))
{
return true;
}
if (!NamedGroup.refersToASpecificCurve(namedGroup))
{
return false;
}
String groupName = NamedGroup.getName(namedGroup);
if (groupName == null)
{
return false;
}
final Integer key = Integers.valueOf(namedGroup);
synchronized (supportedNamedGroups)
{
Boolean cached = (Boolean)supportedNamedGroups.get(key);
if (cached != null)
{
return cached.booleanValue();
}
}
boolean result = true;
try
{
switch (namedGroup)
{
case NamedGroup.x25519:
{
// helper.createAlgorithmParameters("X25519");
helper.createKeyAgreement("X25519");
helper.createKeyFactory("X25519");
helper.createKeyPairGenerator("X25519");
break;
}
case NamedGroup.x448:
{
// helper.createAlgorithmParameters("X448");
helper.createKeyAgreement("X448");
helper.createKeyFactory("X448");
helper.createKeyPairGenerator("X448");
break;
}
default:
{
result &= isCurveSupported(groupName);
break;
}
}
}
catch (GeneralSecurityException e)
{
result = false;
}
synchronized (supportedNamedGroups)
{
supportedNamedGroups.put(key, Boolean.valueOf(result));
}
return result;
}
public boolean hasRSAEncryption()
{
final String key = "KE_RSA";
synchronized (supportedOther)
{
Boolean cached = (Boolean)supportedOther.get(key);
if (cached != null)
{
return cached.booleanValue();
}
}
boolean result = true;
try
{
createRSAEncryptionCipher();
}
catch (GeneralSecurityException e)
{
result = false;
}
synchronized (supportedOther)
{
supportedOther.put(key, Boolean.valueOf(result));
}
return result;
}
public boolean hasSignatureAlgorithm(short signatureAlgorithm)
{
switch (signatureAlgorithm)
{
case SignatureAlgorithm.rsa:
case SignatureAlgorithm.dsa:
case SignatureAlgorithm.ecdsa:
case SignatureAlgorithm.ed25519:
case SignatureAlgorithm.ed448:
case SignatureAlgorithm.rsa_pss_rsae_sha256:
case SignatureAlgorithm.rsa_pss_rsae_sha384:
case SignatureAlgorithm.rsa_pss_rsae_sha512:
case SignatureAlgorithm.rsa_pss_pss_sha256:
case SignatureAlgorithm.rsa_pss_pss_sha384:
case SignatureAlgorithm.rsa_pss_pss_sha512:
return true;
default:
return false;
}
}
public boolean hasSignatureAndHashAlgorithm(SignatureAndHashAlgorithm sigAndHashAlgorithm)
{
/*
* This is somewhat overkill, but much simpler for now. It's also consistent with SunJSSE behaviour.
*/
if (sigAndHashAlgorithm.getHash() == HashAlgorithm.sha224 && JcaUtils.isSunMSCAPIProviderActive())
{
return false;
}
return hasSignatureAlgorithm(sigAndHashAlgorithm.getSignature());
}
public boolean hasSRPAuthentication()
{
return true;
}
public TlsSecret createSecret(byte[] data)
{
try
{
return adoptLocalSecret(Arrays.clone(data));
}
finally
{
// TODO[tls-ops] Add this after checking all callers
// if (data != null)
// {
// Arrays.fill(data, (byte)0);
// }
}
}
public TlsSecret generateRSAPreMasterSecret(ProtocolVersion version)
{
byte[] data = new byte[48];
getSecureRandom().nextBytes(data);
TlsUtils.writeVersion(version, data, 0);
return adoptLocalSecret(data);
}
public TlsHash createHash(short algorithm)
{
try
{
return createHash(getDigestName(algorithm));
}
catch (GeneralSecurityException e)
{
throw Exceptions.illegalArgumentException("unable to create message digest:" + e.getMessage(), e);
}
}
public TlsDHDomain createDHDomain(TlsDHConfig dhConfig)
{
return new JceTlsDHDomain(this, dhConfig);
}
public TlsECDomain createECDomain(TlsECConfig ecConfig)
{
switch (ecConfig.getNamedGroup())
{
case NamedGroup.x25519:
return new JceX25519Domain(this);
case NamedGroup.x448:
return new JceX448Domain(this);
default:
return new JceTlsECDomain(this, ecConfig);
}
}
public TlsEncryptor createEncryptor(TlsCertificate certificate)
throws IOException
{
JcaTlsCertificate jcaCert = JcaTlsCertificate.convert(this, certificate);
jcaCert.validateKeyUsage(KeyUsage.keyEncipherment);
final RSAPublicKey pubKeyRSA = jcaCert.getPubKeyRSA();
return new TlsEncryptor()
{
public byte[] encrypt(byte[] input, int inOff, int length)
throws IOException
{
try
{
Cipher c = createRSAEncryptionCipher();
// try wrap mode first - strictly speaking this is the correct one to use.
try
{
c.init(Cipher.WRAP_MODE, pubKeyRSA, getSecureRandom());
return c.wrap(new SecretKeySpec(input, inOff, length, "TLS"));
}
catch (Exception e)
{
try
{
// okay, maybe the provider does not support wrap mode.
c.init(Cipher.ENCRYPT_MODE, pubKeyRSA, getSecureRandom());
return c.doFinal(input, inOff, length);
}
catch (Exception ex)
{
// okay, if we get here let's rethrow the original one.
throw new TlsFatalAlert(AlertDescription.internal_error, e);
}
}
}
catch (GeneralSecurityException e)
{
/*
* This should never happen, only during decryption.
*/
throw new TlsFatalAlert(AlertDescription.internal_error, e);
}
}
};
}
public TlsSecret hkdfInit(short hashAlgorithm)
{
return adoptLocalSecret(new byte[HashAlgorithm.getOutputSize(hashAlgorithm)]);
}
/**
* If you want to create your own versions of the AEAD ciphers required, override this method.
*
* @param cipherName the full name of the cipher (algorithm/mode/padding)
* @param algorithm the base algorithm name
* @param keySize keySize (in bytes) for the cipher key.
* @param isEncrypting true if the cipher is for encryption, false otherwise.
* @return an AEAD cipher.
* @throws GeneralSecurityException in case of failure.
*/
protected TlsAEADCipherImpl createAEADCipher(String cipherName, String algorithm, int keySize, boolean isEncrypting)
throws GeneralSecurityException
{
return new JceAEADCipherImpl(helper, cipherName, algorithm, keySize, isEncrypting);
}
/**
* If you want to create your own versions of the block ciphers required, override this method.
*
* @param cipherName the full name of the cipher (algorithm/mode/padding)
* @param algorithm the base algorithm name
* @param keySize keySize (in bytes) for the cipher key.
* @param isEncrypting true if the cipher is for encryption, false otherwise.
* @return a block cipher.
* @throws GeneralSecurityException in case of failure.
*/
protected TlsBlockCipherImpl createBlockCipher(String cipherName, String algorithm, int keySize, boolean isEncrypting)
throws GeneralSecurityException
{
return new JceBlockCipherImpl(helper.createCipher(cipherName), algorithm, keySize, isEncrypting);
}
/**
* If you want to create your own versions of the block ciphers for < TLS 1.1, override this method.
*
* @param cipherName the full name of the cipher (algorithm/mode/padding)
* @param algorithm the base algorithm name
* @param keySize keySize (in bytes) for the cipher key.
* @param isEncrypting true if the cipher is for encryption, false otherwise.
* @return a block cipher.
* @throws GeneralSecurityException in case of failure.
*/
protected TlsBlockCipherImpl createBlockCipherWithCBCImplicitIV(String cipherName, String algorithm, int keySize, boolean isEncrypting)
throws GeneralSecurityException
{
return new JceBlockCipherWithCBCImplicitIVImpl(helper.createCipher(cipherName), algorithm, isEncrypting);
}
/**
* If you want to create your own versions of HMACs, override this method.
*
* @param hmacName the name of the HMAC required.
* @return a HMAC calculator.
*/
protected TlsHMAC createHMAC(String hmacName)
{
try
{
return new JceTlsHMAC(helper.createMac(hmacName), hmacName);
}
catch (GeneralSecurityException e)
{
throw new RuntimeException("cannot create HMAC: " + hmacName, e);
}
}
/**
* If you want to create your own versions of Hash functions, override this method.
*
* @param digestName the name of the Hash function required.
* @return a hash calculator.
* @throws GeneralSecurityException in case of failure.
*/
protected TlsHash createHash(String digestName)
throws GeneralSecurityException
{
return new JcaTlsHash(helper.createDigest(digestName));
}
/**
* To disable the null cipher suite, override this method with one that throws an IOException.
*
* @param macAlgorithm the name of the algorithm supporting the MAC.
* @return a null cipher suite implementation.
* @throws IOException in case of failure.
* @throws GeneralSecurityException in case of a specific failure in the JCA/JCE layer.
*/
protected TlsNullCipher createNullCipher(TlsCryptoParameters cryptoParams, int macAlgorithm)
throws IOException, GeneralSecurityException
{
return new TlsNullCipher(cryptoParams, createMAC(macAlgorithm), createMAC(macAlgorithm));
}
protected boolean isCurveSupported(String curveName)
{
return ECUtil.isCurveSupported(curveName, this.getHelper());
}
public JcaJceHelper getHelper()
{
return helper;
}
private TlsBlockCipher createAESCipher(TlsCryptoParameters cryptoParams, int cipherKeySize, int macAlgorithm)
throws IOException, GeneralSecurityException
{
return new TlsBlockCipher(this, cryptoParams, createCBCBlockOperator(cryptoParams, "AES", true, cipherKeySize), createCBCBlockOperator(cryptoParams, "AES", false, cipherKeySize),
createMAC(macAlgorithm), createMAC(macAlgorithm), cipherKeySize);
}
private TlsBlockCipher createARIACipher(TlsCryptoParameters cryptoParams, int cipherKeySize, int macAlgorithm)
throws IOException, GeneralSecurityException
{
return new TlsBlockCipher(this, cryptoParams, createCBCBlockOperator(cryptoParams, "ARIA", true, cipherKeySize), createCBCBlockOperator(cryptoParams, "ARIA", false, cipherKeySize),
createMAC(macAlgorithm), createMAC(macAlgorithm), cipherKeySize);
}
private TlsBlockCipher createCamelliaCipher(TlsCryptoParameters cryptoParams, int cipherKeySize, int macAlgorithm)
throws IOException, GeneralSecurityException
{
return new TlsBlockCipher(this, cryptoParams, createCBCBlockOperator(cryptoParams, "Camellia", true, cipherKeySize), createCBCBlockOperator(cryptoParams, "Camellia", false, cipherKeySize),
createMAC(macAlgorithm), createMAC(macAlgorithm), cipherKeySize);
}
private TlsBlockCipher createDESedeCipher(TlsCryptoParameters cryptoParams, int macAlgorithm)
throws IOException, GeneralSecurityException
{
return new TlsBlockCipher(this, cryptoParams, createCBCBlockOperator(cryptoParams, "DESede", true, 24), createCBCBlockOperator(cryptoParams, "DESede", false, 24),
createMAC(macAlgorithm), createMAC(macAlgorithm), 24);
}
private TlsBlockCipher createSEEDCipher(TlsCryptoParameters cryptoParams, int macAlgorithm)
throws IOException, GeneralSecurityException
{
return new TlsBlockCipher(this, cryptoParams, createCBCBlockOperator(cryptoParams, "SEED", true, 16), createCBCBlockOperator(cryptoParams, "SEED", false, 16),
createMAC(macAlgorithm), createMAC(macAlgorithm), 16);
}
private TlsBlockCipherImpl createCBCBlockOperator(TlsCryptoParameters cryptoParams, String algorithm, boolean forEncryption, int keySize)
throws GeneralSecurityException
{
String cipherName = algorithm + "/CBC/NoPadding";
if (TlsImplUtils.isTLSv11(cryptoParams))
{
return createBlockCipher(cipherName, algorithm, keySize, forEncryption);
}
else
{
return createBlockCipherWithCBCImplicitIV(cipherName, algorithm, keySize, forEncryption);
}
}
private TlsHMAC createMAC(int macAlgorithm) throws IOException
{
return createHMAC(macAlgorithm);
}
private TlsCipher createChaCha20Poly1305(TlsCryptoParameters cryptoParams)
throws IOException, GeneralSecurityException
{
return new TlsAEADCipher(cryptoParams, new JceChaCha20Poly1305(helper, true),
new JceChaCha20Poly1305(helper, false), 32, 16, TlsAEADCipher.NONCE_RFC7905);
}
private TlsAEADCipher createCipher_AES_CCM(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize)
throws IOException, GeneralSecurityException
{
return new TlsAEADCipher(cryptoParams, createAEADCipher("AES/CCM/NoPadding", "AES", cipherKeySize, true), createAEADCipher("AES/CCM/NoPadding", "AES", cipherKeySize, false),
cipherKeySize, macSize);
}
private TlsAEADCipher createCipher_AES_GCM(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize)
throws IOException, GeneralSecurityException
{
return new TlsAEADCipher(cryptoParams, createAEADCipher("AES/GCM/NoPadding", "AES", cipherKeySize, true), createAEADCipher("AES/GCM/NoPadding", "AES", cipherKeySize, false),
cipherKeySize, macSize);
}
private TlsAEADCipher createCipher_AES_OCB(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize)
throws IOException, GeneralSecurityException
{
return new TlsAEADCipher(cryptoParams, createAEADCipher("AES/OCB/NoPadding", "AES", cipherKeySize, true), createAEADCipher("AES/OCB/NoPadding", "AES", cipherKeySize, false),
cipherKeySize, macSize, TlsAEADCipher.NONCE_RFC7905);
}
private TlsAEADCipher createCipher_ARIA_GCM(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize)
throws IOException, GeneralSecurityException
{
return new TlsAEADCipher(cryptoParams, createAEADCipher("ARIA/GCM/NoPadding", "ARIA", cipherKeySize, true), createAEADCipher("ARIA/GCM/NoPadding", "ARIA", cipherKeySize, false),
cipherKeySize, macSize);
}
private TlsAEADCipher createCipher_Camellia_GCM(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize)
throws IOException, GeneralSecurityException
{
return new TlsAEADCipher(cryptoParams, createAEADCipher("Camellia/GCM/NoPadding", "Camellia", cipherKeySize, true), createAEADCipher("Camellia/GCM/NoPadding", "Camellia", cipherKeySize, false),
cipherKeySize, macSize);
}
String getDigestName(short hashAlgorithm)
{
String digestName;
switch (hashAlgorithm)
{
case HashAlgorithm.md5:
digestName = "MD5";
break;
case HashAlgorithm.sha1:
digestName = "SHA-1";
break;
case HashAlgorithm.sha224:
digestName = "SHA-224";
break;
case HashAlgorithm.sha256:
digestName = "SHA-256";
break;
case HashAlgorithm.sha384:
digestName = "SHA-384";
break;
case HashAlgorithm.sha512:
digestName = "SHA-512";
break;
default:
throw new IllegalArgumentException("invalid HashAlgorithm: " + HashAlgorithm.getText(hashAlgorithm));
}
return digestName;
}
}