org.bouncycastle.cms.KeyAgreeIntRecipientInfoGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcmail Show documentation
Show all versions of bcmail Show documentation
A patched bouncycastle-mail
The newest version!
package org.bouncycastle.cms;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.cms.KeyAgreeRecipientIdentifier;
import org.bouncycastle.asn1.cms.KeyAgreeRecipientInfo;
import org.bouncycastle.asn1.cms.OriginatorIdentifierOrKey;
import org.bouncycastle.asn1.cms.OriginatorPublicKey;
import org.bouncycastle.asn1.cms.RecipientEncryptedKey;
import org.bouncycastle.asn1.cms.RecipientInfo;
import org.bouncycastle.asn1.cms.ecc.MQVuserKeyingMaterial;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.spec.MQVPrivateKeySpec;
import org.bouncycastle.jce.spec.MQVPublicKeySpec;
class KeyAgreeIntRecipientInfoGenerator
implements IntRecipientInfoGenerator
{
private DERObjectIdentifier keyAgreementOID;
private DERObjectIdentifier keyEncryptionOID;
private ArrayList recipientCerts;
private KeyPair senderKeyPair;
KeyAgreeIntRecipientInfoGenerator()
{
}
void setKeyAgreementOID(DERObjectIdentifier keyAgreementOID)
{
this.keyAgreementOID = keyAgreementOID;
}
void setKeyEncryptionOID(DERObjectIdentifier keyEncryptionOID)
{
this.keyEncryptionOID = keyEncryptionOID;
}
void setRecipientCerts(Collection recipientCerts)
{
this.recipientCerts = new ArrayList(recipientCerts);
}
void setSenderKeyPair(KeyPair senderKeyPair)
{
this.senderKeyPair = senderKeyPair;
}
public RecipientInfo generate(SecretKey contentEncryptionKey, SecureRandom random,
Provider prov) throws GeneralSecurityException
{
PublicKey senderPublicKey = senderKeyPair.getPublic();
PrivateKey senderPrivateKey = senderKeyPair.getPrivate();
OriginatorIdentifierOrKey originator;
try
{
originator = new OriginatorIdentifierOrKey(
createOriginatorPublicKey(senderPublicKey));
}
catch (IOException e)
{
throw new InvalidKeyException("cannot extract originator public key: " + e);
}
ASN1OctetString ukm = null;
if (keyAgreementOID.getId().equals(CMSEnvelopedGenerator.ECMQV_SHA1KDF))
{
try
{
ECParameterSpec ecParamSpec = ((ECPublicKey)senderPublicKey).getParams();
KeyPairGenerator ephemKPG = KeyPairGenerator.getInstance(
keyAgreementOID.getId(), prov);
ephemKPG.initialize(ecParamSpec, random);
KeyPair ephemKP = ephemKPG.generateKeyPair();
ukm = new DEROctetString(
new MQVuserKeyingMaterial(
createOriginatorPublicKey(ephemKP.getPublic()), null));
senderPrivateKey = new MQVPrivateKeySpec(
senderPrivateKey, ephemKP.getPrivate(), ephemKP.getPublic());
}
catch (InvalidAlgorithmParameterException e)
{
throw new InvalidKeyException(
"cannot determine MQV ephemeral key pair parameters from public key: " + e);
}
catch (IOException e)
{
throw new InvalidKeyException("cannot extract MQV ephemeral public key: " + e);
}
}
ASN1EncodableVector params = new ASN1EncodableVector();
params.add(keyEncryptionOID);
params.add(DERNull.INSTANCE);
AlgorithmIdentifier keyEncAlg = new AlgorithmIdentifier(keyAgreementOID,
new DERSequence(params));
ASN1EncodableVector recipientEncryptedKeys = new ASN1EncodableVector();
Iterator it = recipientCerts.iterator();
while (it.hasNext())
{
X509Certificate recipientCert = (X509Certificate)it.next();
// TODO Should there be a SubjectKeyIdentifier-based alternative?
KeyAgreeRecipientIdentifier karid = new KeyAgreeRecipientIdentifier(
CMSUtils.getIssuerAndSerialNumber(recipientCert));
PublicKey recipientPublicKey = recipientCert.getPublicKey();
if (keyAgreementOID.getId().equals(CMSEnvelopedGenerator.ECMQV_SHA1KDF))
{
recipientPublicKey = new MQVPublicKeySpec(recipientPublicKey, recipientPublicKey);
}
// Use key agreement to choose a wrap key for this recipient
KeyAgreement keyAgreement = KeyAgreement.getInstance(keyAgreementOID.getId(), prov);
keyAgreement.init(senderPrivateKey, random);
keyAgreement.doPhase(recipientPublicKey, true);
SecretKey keyEncryptionKey = keyAgreement.generateSecret(keyEncryptionOID.getId());
// Wrap the content encryption key with the agreement key
Cipher keyEncryptionCipher = CMSEnvelopedHelper.INSTANCE.createSymmetricCipher(
keyEncryptionOID.getId(), prov);
keyEncryptionCipher.init(Cipher.WRAP_MODE, keyEncryptionKey, random);
byte[] encryptedKeyBytes = keyEncryptionCipher.wrap(contentEncryptionKey);
ASN1OctetString encryptedKey = new DEROctetString(encryptedKeyBytes);
recipientEncryptedKeys.add(new RecipientEncryptedKey(karid, encryptedKey));
}
return new RecipientInfo(new KeyAgreeRecipientInfo(originator, ukm,
keyEncAlg, new DERSequence(recipientEncryptedKeys)));
}
// TODO Make this a public helper?
private static OriginatorPublicKey createOriginatorPublicKey(PublicKey publicKey)
throws IOException
{
SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(
ASN1Object.fromByteArray(publicKey.getEncoded()));
return new OriginatorPublicKey(
new AlgorithmIdentifier(spki.getAlgorithmId().getObjectId(), DERNull.INSTANCE),
spki.getPublicKeyData().getBytes());
}
}