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

Problem reading nested Octet Strings #532

Closed
ggrote opened this issue Apr 18, 2024 · 22 comments
Closed

Problem reading nested Octet Strings #532

ggrote opened this issue Apr 18, 2024 · 22 comments
Assignees

Comments

@ggrote
Copy link

ggrote commented Apr 18, 2024

It seems like the OctetString Reader is not capable of reading nested Octet Strings.
In our public cert file the SubjectIdentifier is stored as:
0416 0414 06a926722d485a3cf7ac997e47376d478fb61273

The Value of the SubjectIdentifier Extension is '041406a926722d485a3cf7ac997e47376d478fb61273' after reading the file and not '06a926722d485a3cf7ac997e47376d478fb61273' how it should be.

It seems like the Java Version correctly reads the private key, but not the public key.
If needed I can provide the public key.

@cipherboy
Copy link
Collaborator

@ggrote Do you have an example? When I tried to reproduce this with:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.X509.Extension;


var sr = File.OpenText("cert.pem");
var pr = new PemReader(sr);

var o = pr.ReadPemObject();
var cert = new X509Certificate(o.Content);

var e = cert.GetExtensionValue(X509Extensions.SubjectKeyIdentifier);
Console.WriteLine("e=" + Hex.ToHexString(e.GetOctets()));

I got:

$ dotnet run
e=0414b18deae423a77e098eb5ee31e06add9e343765ac

Where e here seems to be the correct encoding of this parsed octet value.

@cipherboy cipherboy self-assigned this Apr 18, 2024
@ggrote
Copy link
Author

ggrote commented Apr 18, 2024

I think you got the same problem with your example.
The subject identifier should be:
b18deae423a77e098eb5ee31e06add9e343765ac
The 0414 at the beginning marks another octet string (0x04) with the length 0x14 inside the octet string for the extension. Just have a look in the pen with openssl or any other tool. Or just create the sha1 hash of the public key which should be the subject identifier.

@peterdettman
Copy link
Collaborator

@ggrote I don't see any bug, you just aren't parsing the extension value.

An Extension has a value which is an OCTET STRING. In your example, the OCTET STRING is "041406a9...", encoded as "0416041406a9..." in the ASN.1. In the case of SubjectKeyIdentifier, that value is interpreted as an encoding of an ("inner") OCTET STRING. So to process this extension correctly, you need to actually parse the contents octets of the value OCTET STRING, which would give you back the OCTET STRING "06a9...".

Anyway, we have a convenience method for this type of thing, so just try:

cert.GetX509Extensions()?.GetExtensionParsedValue(X509Extensions.SubjectKeyIdentifier)

and you should get back the Asn1OctetString that you expect.

@ggrote
Copy link
Author

ggrote commented Apr 19, 2024

Okay got it.
We faced the problem using MimeKit for decrypting S/Mime Mails which uses BouncyCastle.
The Implementation is as follows:

protected override AsymmetricKeyParameter GetPrivateKey (ISelector<X509Certificate> selector)
{
	foreach (var certificate in certificates) {
		var fingerprint = certificate.GetFingerprint ();

		if (!keys.TryGetValue (fingerprint, out var key))
			continue;

		if (selector != null && !selector.Match (certificate))
			continue;

		return key;
	}

	return null;
}

Which uses the X509CertStoreSelectors Match function in which the SubjectKeyIdentifier is compared:

if (!MatchExtension(subjectKeyIdentifier, c, X509Extensions.SubjectKeyIdentifier))
				return false;
private static bool MatchExtension(byte[] b, X509Certificate c, DerObjectIdentifier oid)
		{
			if (b == null)
				return true;
			Asn1OctetString extVal = c.GetExtensionValue(oid);
			if (extVal == null)
				return false;
			return Arrays.AreEqual(b, extVal.GetOctets());
		}

Shouldn't this be cert.GetX509Extensions()?.GetExtensionParsedValue(X509Extensions.SubjectKeyIdentifier) like you said?
Because now the parsed value from the incoming Mail is compared to the unparsed value from the certificate.

I think the Java Version gives two values. One Raw Value and one parsed Value.

I'm sorry but the code formatting doesn't like me.


@cipherboy edited for formatting.

@ggrote
Copy link
Author

ggrote commented May 7, 2024

Any news about it?

@peterdettman
Copy link
Collaborator

I agree that there seems to be a problem in the selector. This code was all ported from bc-java so I think the confusion arises partly because of the behaviour of the JDK X509CertSelector class.

@ggrote
Copy link
Author

ggrote commented May 9, 2024

Okay maybe there is a problem too. In my opinion the selector should compare the inner most octet string with the given value, as this could be the only option where it could match. Otherwise this whole selector can not work.

@peterdettman
Copy link
Collaborator

So the problem turned out to be that the selector's SubjectKeyIdentifier is not being set appropriately by the CMS recipients (KeyAgreeRecipientInformation and KeyTransRecipientInformation).

It might be a good idea to change the way AuthorityKeyIdentifier and SubjectKeyIdentifier are set on X509CertStoreSelector, but for the moment I've just fixed the recipients.

@ggrote
Copy link
Author

ggrote commented May 10, 2024

Thank you!

@peterdettman
Copy link
Collaborator

@jstedfast This issue affects selection of CMS recipients (KeyAgree/KeyTrans) that are specifying SubjectKeyIdentifier (instead of the more common IssuerAndSerialNumber). I am a bit surprised that we have no test coverage of that option between BouncyCastle (even bc-java) and MimeKit, so we probably should add some.

@peterdettman
Copy link
Collaborator

@jstedfast I've published 2.4.0-beta.61 to allow testing of the fix.

@peterdettman
Copy link
Collaborator

@jstedfast With the "fix" I now get errors from MimeKit tests, and I have to take back the claim that you don't have test coverage for this case. I'm a bit confused how things used to work and why they break now though; needs more investigation.

@jstedfast
Copy link
Contributor

@peterdettman I'll try to look into the test failures this weekend. Is the published fix on nuget.org?

@jstedfast
Copy link
Contributor

Hmmm, odd, I installed the 2.4.0-beta.61 version and MimeKit UnitTests all passed?

jstedfast added a commit to jstedfast/MimeKit that referenced this issue May 10, 2024
@jstedfast
Copy link
Contributor

Added more tests for this and now I'm getting failures for SubjectKeyIdentifier but not IssuerAndSerialNumber (using 2.4.0-beta.61). I'll check v2.3.1 as soon as the meeting I'm in is over.

@jstedfast
Copy link
Contributor

all tests pass on v2.3.1

@peterdettman
Copy link
Collaborator

peterdettman commented May 10, 2024

MimeKit sets the SubjectKeyIdentifier incorrectly in RsaOaepAwareRecipientInfoGenerator.Generate (also in CmsEnvelopeAddEllipticCurve method just below there). That was "cancelling out" this issue in BouncyCastle.

After updating MimeKit to use 2.4.0-beta.61, then fixing those two places in MimeKit, things go back to working (but now would be correct for third-party messages - which our test suites ought to incorporate examples of).

Edit: @ggrote presumably saw the error because he was decrypting something with MimeKit that wasn't generated by MimeKit.

@jstedfast
Copy link
Contributor

Ah, got it.

@ggrote
Copy link
Author

ggrote commented May 10, 2024

Edit: @ggrote presumably saw the error because he was decrypting something with MimeKit that wasn't generated by MimeKit.
I can't 100% confirm, but I'm pretty sure the service provider we got the mail from does not use MimeKit.

@jstedfast
Copy link
Contributor

The commit above depends on BouncyCastle 2.4.0-beta.61

@peterdettman What I ended up doing is writing unit tests for MimeKit that would encrypt using the System.Security backend and then verifying that the BouncyCastle backend could decrypt it. Likewise, I also added a unit test that would encrypt using the BouncyCastle backend and verifying that the System.Security backend could decrypt it.

Both tests try IssuerAndSerialNumber and SubjectKeyIdentifier so that we make sure that both work in both directions.

@jstedfast
Copy link
Contributor

@peterdettman what are your plans for releasing 2.4.0? I didn't realize that 2.3.1 fixed some security issues and so I'm getting requests to make a release with >= 2.3.1.

@peterdettman
Copy link
Collaborator

@peterdettman what are your plans for releasing 2.4.0? I didn't realize that 2.3.1 fixed some security issues and so I'm getting requests to make a release with >= 2.3.1.

@jstedfast I'm planning to release 2.4.0 this coming weekend.

hubot pushed a commit that referenced this issue May 22, 2024
jstedfast added a commit to jstedfast/MimeKit that referenced this issue May 28, 2024
…pt for SubjectKeyIdentifier

Added Windows -> BouncyCastle and BouncyCastle -> Windows unit tests in order
to validate that each backend can decrypt the other backend's output.

Fixes bcgit/bc-csharp#532
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

4 participants