You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
8.9 KiB
230 lines
8.9 KiB
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) |
|
#pragma warning disable |
|
using System; |
|
using System.Collections; |
|
using System.IO; |
|
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Cms; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Cms.Ecc; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Pkcs; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Utilities; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.X509; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.X9; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Math; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Pkcs; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Security; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.X509; |
|
|
|
namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Cms |
|
{ |
|
/** |
|
* the RecipientInfo class for a recipient who has been sent a message |
|
* encrypted using key agreement. |
|
*/ |
|
public class KeyAgreeRecipientInformation |
|
: RecipientInformation |
|
{ |
|
private KeyAgreeRecipientInfo info; |
|
private Asn1OctetString encryptedKey; |
|
|
|
internal static void ReadRecipientInfo(IList infos, KeyAgreeRecipientInfo info, |
|
CmsSecureReadable secureReadable) |
|
{ |
|
try |
|
{ |
|
foreach (Asn1Encodable rek in info.RecipientEncryptedKeys) |
|
{ |
|
RecipientEncryptedKey id = RecipientEncryptedKey.GetInstance(rek.ToAsn1Object()); |
|
|
|
RecipientID rid = new RecipientID(); |
|
|
|
Asn1.Cms.KeyAgreeRecipientIdentifier karid = id.Identifier; |
|
|
|
Asn1.Cms.IssuerAndSerialNumber iAndSN = karid.IssuerAndSerialNumber; |
|
if (iAndSN != null) |
|
{ |
|
rid.Issuer = iAndSN.Name; |
|
rid.SerialNumber = iAndSN.SerialNumber.Value; |
|
} |
|
else |
|
{ |
|
Asn1.Cms.RecipientKeyIdentifier rKeyID = karid.RKeyID; |
|
|
|
// Note: 'date' and 'other' fields of RecipientKeyIdentifier appear to be only informational |
|
|
|
rid.SubjectKeyIdentifier = rKeyID.SubjectKeyIdentifier.GetOctets(); |
|
} |
|
|
|
infos.Add(new KeyAgreeRecipientInformation(info, rid, id.EncryptedKey, |
|
secureReadable)); |
|
} |
|
} |
|
catch (IOException e) |
|
{ |
|
throw new ArgumentException("invalid rid in KeyAgreeRecipientInformation", e); |
|
} |
|
} |
|
|
|
internal KeyAgreeRecipientInformation( |
|
KeyAgreeRecipientInfo info, |
|
RecipientID rid, |
|
Asn1OctetString encryptedKey, |
|
CmsSecureReadable secureReadable) |
|
: base(info.KeyEncryptionAlgorithm, secureReadable) |
|
{ |
|
this.info = info; |
|
this.rid = rid; |
|
this.encryptedKey = encryptedKey; |
|
} |
|
|
|
private AsymmetricKeyParameter GetSenderPublicKey( |
|
AsymmetricKeyParameter receiverPrivateKey, |
|
OriginatorIdentifierOrKey originator) |
|
{ |
|
OriginatorPublicKey opk = originator.OriginatorPublicKey; |
|
if (opk != null) |
|
{ |
|
return GetPublicKeyFromOriginatorPublicKey(receiverPrivateKey, opk); |
|
} |
|
|
|
OriginatorID origID = new OriginatorID(); |
|
|
|
Asn1.Cms.IssuerAndSerialNumber iAndSN = originator.IssuerAndSerialNumber; |
|
if (iAndSN != null) |
|
{ |
|
origID.Issuer = iAndSN.Name; |
|
origID.SerialNumber = iAndSN.SerialNumber.Value; |
|
} |
|
else |
|
{ |
|
SubjectKeyIdentifier ski = originator.SubjectKeyIdentifier; |
|
|
|
origID.SubjectKeyIdentifier = ski.GetKeyIdentifier(); |
|
} |
|
|
|
return GetPublicKeyFromOriginatorID(origID); |
|
} |
|
|
|
private AsymmetricKeyParameter GetPublicKeyFromOriginatorPublicKey( |
|
AsymmetricKeyParameter receiverPrivateKey, |
|
OriginatorPublicKey originatorPublicKey) |
|
{ |
|
PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(receiverPrivateKey); |
|
SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo( |
|
privInfo.PrivateKeyAlgorithm, |
|
originatorPublicKey.PublicKey.GetBytes()); |
|
return PublicKeyFactory.CreateKey(pubInfo); |
|
} |
|
|
|
private AsymmetricKeyParameter GetPublicKeyFromOriginatorID( |
|
OriginatorID origID) |
|
{ |
|
// TODO Support all alternatives for OriginatorIdentifierOrKey |
|
// see RFC 3852 6.2.2 |
|
throw new CmsException("No support for 'originator' as IssuerAndSerialNumber or SubjectKeyIdentifier"); |
|
} |
|
|
|
private KeyParameter CalculateAgreedWrapKey( |
|
string wrapAlg, |
|
AsymmetricKeyParameter senderPublicKey, |
|
AsymmetricKeyParameter receiverPrivateKey) |
|
{ |
|
DerObjectIdentifier agreeAlgID = keyEncAlg.Algorithm; |
|
|
|
ICipherParameters senderPublicParams = senderPublicKey; |
|
ICipherParameters receiverPrivateParams = receiverPrivateKey; |
|
|
|
if (agreeAlgID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf)) |
|
{ |
|
byte[] ukmEncoding = info.UserKeyingMaterial.GetOctets(); |
|
MQVuserKeyingMaterial ukm = MQVuserKeyingMaterial.GetInstance( |
|
Asn1Object.FromByteArray(ukmEncoding)); |
|
|
|
AsymmetricKeyParameter ephemeralKey = GetPublicKeyFromOriginatorPublicKey( |
|
receiverPrivateKey, ukm.EphemeralPublicKey); |
|
|
|
senderPublicParams = new MqvPublicParameters( |
|
(ECPublicKeyParameters)senderPublicParams, |
|
(ECPublicKeyParameters)ephemeralKey); |
|
receiverPrivateParams = new MqvPrivateParameters( |
|
(ECPrivateKeyParameters)receiverPrivateParams, |
|
(ECPrivateKeyParameters)receiverPrivateParams); |
|
} |
|
|
|
IBasicAgreement agreement = AgreementUtilities.GetBasicAgreementWithKdf( |
|
agreeAlgID, wrapAlg); |
|
agreement.Init(receiverPrivateParams); |
|
BigInteger agreedValue = agreement.CalculateAgreement(senderPublicParams); |
|
|
|
int wrapKeySize = GeneratorUtilities.GetDefaultKeySize(wrapAlg) / 8; |
|
byte[] wrapKeyBytes = X9IntegerConverter.IntegerToBytes(agreedValue, wrapKeySize); |
|
return ParameterUtilities.CreateKeyParameter(wrapAlg, wrapKeyBytes); |
|
} |
|
|
|
private KeyParameter UnwrapSessionKey( |
|
string wrapAlg, |
|
KeyParameter agreedKey) |
|
{ |
|
byte[] encKeyOctets = encryptedKey.GetOctets(); |
|
|
|
IWrapper keyCipher = WrapperUtilities.GetWrapper(wrapAlg); |
|
keyCipher.Init(false, agreedKey); |
|
byte[] sKeyBytes = keyCipher.Unwrap(encKeyOctets, 0, encKeyOctets.Length); |
|
return ParameterUtilities.CreateKeyParameter(GetContentAlgorithmName(), sKeyBytes); |
|
} |
|
|
|
internal KeyParameter GetSessionKey( |
|
AsymmetricKeyParameter receiverPrivateKey) |
|
{ |
|
try |
|
{ |
|
string wrapAlg = DerObjectIdentifier.GetInstance( |
|
Asn1Sequence.GetInstance(keyEncAlg.Parameters)[0]).Id; |
|
|
|
AsymmetricKeyParameter senderPublicKey = GetSenderPublicKey( |
|
receiverPrivateKey, info.Originator); |
|
|
|
KeyParameter agreedWrapKey = CalculateAgreedWrapKey(wrapAlg, |
|
senderPublicKey, receiverPrivateKey); |
|
|
|
return UnwrapSessionKey(wrapAlg, agreedWrapKey); |
|
} |
|
catch (SecurityUtilityException e) |
|
{ |
|
throw new CmsException("couldn't create cipher.", e); |
|
} |
|
catch (InvalidKeyException e) |
|
{ |
|
throw new CmsException("key invalid in message.", e); |
|
} |
|
catch (Exception e) |
|
{ |
|
throw new CmsException("originator key invalid.", e); |
|
} |
|
} |
|
|
|
/** |
|
* decrypt the content and return an input stream. |
|
*/ |
|
public override CmsTypedStream GetContentStream( |
|
ICipherParameters key) |
|
{ |
|
if (!(key is AsymmetricKeyParameter)) |
|
throw new ArgumentException("KeyAgreement requires asymmetric key", "key"); |
|
|
|
AsymmetricKeyParameter receiverPrivateKey = (AsymmetricKeyParameter) key; |
|
|
|
if (!receiverPrivateKey.IsPrivate) |
|
throw new ArgumentException("Expected private key", "key"); |
|
|
|
KeyParameter sKey = GetSessionKey(receiverPrivateKey); |
|
|
|
return GetContentFromSessionKey(sKey); |
|
} |
|
} |
|
} |
|
#pragma warning restore |
|
#endif
|
|
|