Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SECG Key Agreement / Key Derivation functions not supported? #523

Open
azure-hu opened this issue Feb 28, 2024 · 3 comments
Open

SECG Key Agreement / Key Derivation functions not supported? #523

azure-hu opened this issue Feb 28, 2024 · 3 comments

Comments

@azure-hu
Copy link

One of my workmates made an XML encryption / decryption process with certificates and CMS in Java using latest Bouncy Castle version.
Our customers want to implement those methods in .NET, so I'm trying to re-create it in C# using BC-CSharp. First, I have an encrypted XML in a stream and trying to decrypt it. The certificates / private keys used for encryption has 256-bit EC keys. The code what I've made so far:

private static Stream InitDecryptInputStream(Stream inputStream, KeystoreManager ksManager)
{
    Pkcs12Store ks = KeyStoreUtils.OpenPkcs12Store(ksManager.KeystoreFile, ksManager.KeystorePassword);
    CmsEnvelopedDataParser ep = new CmsEnvelopedDataParser(inputStream);
    RecipientInformationStore recipients = ep.GetRecipientInfos();

    IList<RecipientInformation> rec = recipients.GetRecipients();
    IEnumerator it = rec.GetEnumerator();
    while (it.MoveNext())
    {
        // no privateKey given, find the key from the keyStore using
        // the encryption certificate
        RecipientInformation recipient = (RecipientInformation)it.Current;
        RecipientID recId = recipient.RecipientID;
        if (recipient is KeyTransRecipientInformation || recipient is KeyAgreeRecipientInformation)
        {
            String info = recId.ToString();
            X509Name issuer = null;
            BigInteger serialNumber = null;
            issuer = recId.Issuer;
            serialNumber = recId.SerialNumber;
            X509CertificateEntry[] certChain;
            AsymmetricCipherKeyPair keyPair = KeyStoreUtils.GetKeyPairByIssuerAndSn(ks, issuer, serialNumber, out certChain);
            X509Certificate cert = CertificateUtils.GetCertificate(certChain, issuer, serialNumber, false);

            // we have found the crypter key in the keystore
            if (cert != null)
            {
                logger.Info("Recipient cert: " + cert.ToString());
                AsymmetricKeyParameter privateKey = null;
                // only the given key acceptable
                if (ksManager.Certificate != null)
                {
                    if (ksManager.Certificate.Equals(cert))
                    {
                        privateKey = ksManager.KeyPair.Private;
                    }
                }
                else
                {
                    String certAlias = ks.GetCertificateAlias(cert);
                    privateKey = ks.GetKey(certAlias).Key;
                }

                if (privateKey != null)
                {
                    logger.Debug("Decrypting with certificate: " + cert);
                    CmsTypedStream recData = recipient.GetContentStream(privateKey);
                    return recData.ContentStream;
                }
            }
            else
            {
                logger.Debug("No matching certificate found in our keystore for recipientId  " + info +
                      ".  Serial number in hexadecimal format " + serialNumber.ToString(16));
            }
        }
        else
        {
            logger.Warn("Unexpected type of recipient: " + recId.GetType());
        }
    }
        throw new KeyException("No acceptable private Key found for decrypting the message");
}

The line CmsTypedStream recData = recipient.GetContentStream(privateKey); throws an exception:

Org.BouncyCastle.Cms.CmsException: couldn't create cipher. 
---> Org.BouncyCastle.Security.SecurityUtilityException: Basic Agreement (with KDF) OID not recognised.
   at Org.BouncyCastle.Security.AgreementUtilities.GetBasicAgreementWithKdf(DerObjectIdentifier oid, String wrapAlgorithm)
   at Org.BouncyCastle.Security.AgreementUtilities.GetBasicAgreementWithKdf(DerObjectIdentifier agreeAlgOid, DerObjectIdentifier wrapAlgOid)
   at Org.BouncyCastle.Cms.KeyAgreeRecipientInformation.CalculateAgreedWrapKey(DerObjectIdentifier wrapAlgOid, AsymmetricKeyParameter senderPublicKey, AsymmetricKeyParameter receiverPrivateKey)
   at Org.BouncyCastle.Cms.KeyAgreeRecipientInformation.GetSessionKey(AsymmetricKeyParameter receiverPrivateKey)

Debugging through BC-CSharp source code, I've checked the algorithms.
The wrapAlgOid is aes128-wrap (2.16.840.1.101.3.4.1.5), which may not be a problem, but the agreeAlgOid is dhSinglePass-stdDH-sha256kdf-scheme (1.3.132.1.11.1), which exists in SecObjectIdentifiers class, but not mapped in AgreementUtilities class.

Is it possible, that dhSinglePass-stdDH-sha256kdf-scheme key agreement algorithm is not supported in C# yet and was implemented only in BC-Java? Or I should use other way to decrypt that kind of data?

@peterdettman
Copy link
Collaborator

I see that this OID and other similar ones are not mapped (bc-csharp usually lags bc-java on what features are supported). However I think we already have the underlying implementation so it may be a reasonably simple fix.

@peterdettman
Copy link
Collaborator

I've added dhSinglePass-stdDH-sha256kdf-scheme (along with other similar ones) to the registries. If you are able to build from the latest source code, you could check whether that fixes things for you, or else let me know what else is blocking this for you.

@azure-hu
Copy link
Author

Sorry for the late response. Built the BC-Sharp code from the latest commit.
Now the cipher was created, but when the code reaches this part:
CmsTypedStream recData = recipient.GetContentStream(privateKey);
an exception occured.

2024-03-12 10:54:26.8281 | ERROR | Program | Org.BouncyCastle.Cms.CmsException: originator key invalid. ---> Org.BouncyCastle.Crypto.InvalidCipherTextException: checksum failed
   at Org.BouncyCastle.Crypto.Engines.Rfc3394WrapEngine.Unwrap(Byte[] input, Int32 inOff, Int32 inLen)
   at Org.BouncyCastle.Cms.KeyAgreeRecipientInformation.UnwrapSessionKey(DerObjectIdentifier wrapAlgOid, KeyParameter agreedKey)
   at Org.BouncyCastle.Cms.KeyAgreeRecipientInformation.GetSessionKey(AsymmetricKeyParameter receiverPrivateKey)

I still cannot figure out why it fails, because the code finds the corresponding private key from the supplied keystore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants