InvalidBasicConstraints: A certificate's basic constraint extension has not been observed - c#

I am getting the error "InvalidBasicConstraints: A certificate's basic constraint extension has not been observed." This certificate was issued with OpenSSL and will be used as a server-side test certificate for a WCF service (which gives the same error when validating the certificate). I can replicate the error with this code.
X509Certificate2 cert = new X509Certificate2(#"c:\test.cer");
X509Chain chain = X509Chain.Create();
X509ChainPolicy policy = new X509ChainPolicy();
policy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy = policy;
bool valid = chain.Build(cert);
Console.WriteLine(string.Join(" -- ", chain.ChainStatus.Select(o => o.Status + ": " + o.StatusInformation)));
Console.WriteLine(valid ? "VALID" : "NOT VALID");
Viewing the certificate I can see these basic constraints,
Subject Type=End Entity
Path Length Constraint=None
What does this error mean and how can I fix it?
The most relevent article I found on basic constraints was this one, http://unitstep.net/blog/2009/03/16/using-the-basic-constraints-extension-in-x509-v3-certificates-for-intermediate-cas/ however it only talks about prohibiting a child certificate from signing / creating more child certificates which in my case I'm not trying to do.
Also any references that explain what the above basic constraints mean would be helpful.

http://forums.juniper.net/t5/SSL-VPN/quot-Failed-to-authenticate-client-certificate-quot-after/td-p/89232/page/4
After reading the above thread I see there are two different subject types,
Basic Constraints: Subject Type=CA, Length Constraint=None
Basic Constraints: Subject Type=End Entity, Path Length Constraint=None
The server-side certificate I'm using is actually the 3rd link in a hiearchy.
A -> B -> C....A issued B which then issued signed C.
Certificate B also has Subject Type=End Entity in the basic constraints. I cannot find any documentation which states the different types of basic contraints or their meanings, but based on the difference of the above 2 types, I would say that End Entity means it CANNOT issue certificates. It is the end of the chain, as opposed to Subject Type=CA.
When .NET is validating the chain it sees that B does not have permission from it's father certificate to issue certificates and throws the error, A certificate's basic constraint extension has not been observed.
Edit: Further testing with a new certificate that was issued by a self-signed certificate verifies the above theory.

Related

How to create certificate object from public key in PEM format?

I want to use iText to embed signed hash and public in PDF.
As per arguments to sign method in iText 7 I need to pass certificate chain,
How can I create this certificate object directly from public key string?
Update 1
Below is small c# code. You can see I am trying to get x509 certificate from public key. This certificate will be used to verify the signed data from corresponding private key. Also it will be used to embed this public certificate and signed hash into PDF for digital signature.
In below code I am getting error as below
Error:
'DigiSignDemo.exe' (CLR v4.0.30319: DigiSignDemo.exe): Loaded 'C:\Users\xposs\source\repos\DigiSignDemo\bin\Debug\itext.forms.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Exception thrown: 'System.Security.Cryptography.CryptographicException' in mscorlib.dll
An unhandled exception of type 'System.Security.Cryptography.CryptographicException' occurred in mscorlib.dll
Cannot find the requested object.
public static readonly string publickey = #"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGhYfAvWxqIwsZsO1zUN
NyFT/3US7HGLXiW48NvYn2qNyn/9hm/BFWG901YoJAjlPTcNtMo1t8lUr2dRkc3l
8YyP8SetWKbznerQuXYBZZy31kp8u3Wj+zQSroZsFn69FoMAMWXqhkw9woFumINe
gw4sMtQ1S8CucX0uXJ4a2ElzoaUKp1M+MOCATDnmsXSyf/2/ERO71SpD+alDV2rE
m5DqvEnE0t27fm7PpNeCX0XEHRvx620LooGv1Co+0w5Au37sfSjOZp1B9V0n8KFR
6gLFY7mAZ1krZJscYgkNAPIz2QE6voBR8OVSHMnNcPH+0KLfGuNVHhaTyI4naPH+
0QIDAQAB
-----END PUBLIC KEY-----
";
public static System.Security.Cryptography.X509Certificates.X509Certificate getPublicCertificate()
{
//Here below I am getting error
X509Certificate2 clientCertificate =
new X509Certificate2(Encoding.UTF8.GetBytes(publickey));
return clientCertificate;
}
You can't create a certificate from a public key. It's analogous to asking how to create a car from a steering wheel... you're missing a lot of other stuff before it'd be a car.
Given that you have an RSA public key in the SubjectPublicKeyInfo format, you can import it as an RSA key, starting with .NET Core 3.0, via
RSA rsa = RSA.Create();
rsa.ImportSubjectPublicKeyInfo(
Convert.FromBase64String(#"
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGhYfAvWxqIwsZsO1zUN
NyFT/3US7HGLXiW48NvYn2qNyn/9hm/BFWG901YoJAjlPTcNtMo1t8lUr2dRkc3l
8YyP8SetWKbznerQuXYBZZy31kp8u3Wj+zQSroZsFn69FoMAMWXqhkw9woFumINe
gw4sMtQ1S8CucX0uXJ4a2ElzoaUKp1M+MOCATDnmsXSyf/2/ERO71SpD+alDV2rE
m5DqvEnE0t27fm7PpNeCX0XEHRvx620LooGv1Co+0w5Au37sfSjOZp1B9V0n8KFR
6gLFY7mAZ1krZJscYgkNAPIz2QE6voBR8OVSHMnNcPH+0KLfGuNVHhaTyI4naPH+
0QIDAQAB"),
out _);
// the key is loaded now.
If you're not on .NET Core, this is a lot harder. See How to load the RSA public key from file in C# or How to get RSACryptoServiceProvider public and private key only in c# for more information.
#bartonjs in his answer already has mentioned that a X509 certificate is much more than a public key. Here comes an overwiew to show how much more.
For this let's look at a specification of X509 certificates, RFC 5280. The certificate structure is specified in ASN.1 which is quite easy to follow even if one does not know it yet.
Here you'll see that first of all a X509 certificate is something signed:
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
The private key used for this signature may again be associated with the public key of another certificate (issuer certificate) which again may be signed using a private key associated with the public key of yet another certificate. This gives rise to a chain of certificates which usually ends in a certificate which is signed using the private key of this certificate itself, a self-signed certificate.
To verify whether one trusts the information in a certificate, one usually follows its issuer chain and checks whether one eventually gets to some certificate which is from a small collection of certificates one trusts explicitly.
The TBSCertificate in it contains the information to-be-signed. It is specified as
TBSCertificate ::= SEQUENCE {
version [0] EXPLICIT Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
extensions [3] EXPLICIT Extensions OPTIONAL
}
Here you in particular a subject Name, an issue Name and a serialNumber. The subject name describes the holder of this certificate, the issue name is the subject name of the issuer certificate, and the serial number is a unique number determined by the issuer when signing the certificate.
The validity interval indicates the intended time span of validity of the certificate.
Then there eventually is the SubjectPublicKeyInfo which contains the public key of the certificate.
Finally the extensions are an optional collection containing extra information like the allowed usages of the certificate (and its private key) and URLs where revocation information for the certificate can be retrieved.
As you can see you cannot simply the certificate based on the public key alone. You can create a certificate based thereon alone but don't expect anyone to trust it.

C# | jstedfast/MimeKit | Office 365 connector with DKIM setup

DKIM is set up for a domain in Office365. A .Net application (currently MVC 4) sends Email through an O365 connector to external parties.
We'd like to sign these using DKIM as well.
I'm not quite clear about the entire process.
MimeKit's Documentation is reasonably clear. I suppose I can use any pub/priv key generator such as Putty to generate a keypair? I would then store the private key in a way that the C# application can read it into
var signer = new DkimSigner ("privatekey.pem") {
SignatureAlgorithm = DkimSignatureAlgorithm.RsaSha1,
AgentOrUserIdentifier = "#eng.example.com",
QueryMethod = "dns/txt",
};
The public key will be published as a DNS record for my domain. Unfortunately, the Office 365 documentation isn't all too clear on the exact how.
Summary Questions
What exactly goes into AgentOrUserIdentifier, if my system sends with the address application#example.org?
How exactly would I publish my generated public key to Office 365?
Any enlightening summary would be greatly appreciated, thanks.
I'll accept #jstedfast's answer (although without really understanding it).
Just in case anyone else is struggling with this, here's the complete walk-through:
Get a public/private key pair. You can use Puttygen or openssl directly, but it's easier to use (oh had I only known beforehand) sth like https://port25.com/dkim-wizard/
Specify your domain name (example.org here) and a "selector" - this could be your application name ("greatapp"). This selector will be the TXT record for the public key in DNS.
Create an additional DNS (TXT) record; leave the Office365 ones intact. Since they rotate keys regularly you want an additional record that you can control.
greatapp._domainkey.example.org IN TXT
"k=rsa\; p=here goes the stuff between -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY-----", so e.g.
"k=rsa\; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhvIwVBomj+dx2CEBbY/ZpSdnQK2Omx6ZNyHsuvC3MMJYNLQ069ajuJo5FP......."
Copy the private key to a file, or use it in your code directly. MimeKit either expects a file or a stream, so for the quick & dirty example here I'm using a string:
var mail = new MimeMessage();
mail.From.Add(new MailboxAddress("Justin Case", "justin#example.org"));
mail.To.Add(new MailboxAddress("Candy Barr", "candy#example.org"));
... subject etc
var privateKey = #"-----BEGIN RSA PRIVATE KEY-----......";
var privateKeyStream = new MemoryStream(Encoding.Default.GetBytes(privateKey));
mail.Sign(new DkimSigner(privateKeyStream, "example.org", "greatapp", DkimSignatureAlgorithm.RsaSha256), new HeaderId[] { HeaderId.From, HeaderId.Subject }, DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm.Simple);
... Connect client and send.
Thanks to jstedfast something as awesome as MailKit/MimeKit exists, don't forget to donate.
From rfc6376, section 2.6:
2.6. Agent or User Identifier (AUID)
A single identifier that refers to the agent or user on behalf of
whom the Signing Domain Identifier (SDID) has taken responsibility.
The AUID comprises a domain name and an optional <local-part>. The
domain name is the same as that used for the SDID or is a subdomain
of it. For DKIM processing, the domain name portion of the AUID has
only basic domain name semantics; any possible owner-specific
semantics are outside the scope of DKIM. It is specified in
Section 3.5.
Note that acceptable values for the AUID may be constrained via a
flag in the public-key record. (See Section 3.6.1.)

X509Chain.Build() succeeding for revoked certificate. C#

I create a certificate request like this:
certreq -new req.inf req-Revoked.req
certreq -submit -attrib "SAN:email=ttesting#Test.Domain&upn=1234567890#Test.Domain" -config Win2K8-64\Test-Win2K8-64-CA req-Revoked.req testerCert-Revoked.cer
certreq -accept testerCert-Revoked.cer
CertUtil -f -p testerCert -exportPFX -user My Testing.Tester.T.1234567890 testerCert-Revoked.pfx
CertUtil -delstore -user My Testing.Tester.T.1234567890
Then I revoke it, via:
CertUtil -revoke <SerialNumber_from_above_Cert>
I then execute this code:
X509Certificate2 certificate = GetCertificate("testerCert-Revoked.pfx", "password"); // helper method loads the pfx from a file
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
chain.ChainPolicy.VerificationTime = DateTime.Now;
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 0);
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
if (!chain.Build(certificate))
{
errorBuffer.Append("Could not build X.509 certificate chain:\r\n");
foreach (X509ChainStatus status in chain.ChainStatus)
{
errorBuffer.AppendFormat(" - {0}\r\n", status.StatusInformation);
}
throw new CryptographicException(errorBuffer.ToString());
}
chain.Build() always returns true. But since the certificate is revoked, it should be False! I've double-checked that the certificate is revoked, and that the serial number is listed in the Server Manager under revoked certificates. I've double-checked that the CURL distribution point urls match on the server and the certificate requests. (they're LDAP urls). CertUtil sees the intermediate .cer file as revoked. But the C# code above doesn't.
This all worked before the original CA expired, and IT rebuilt my test machine with a new cert. I've regenerated the certs with the new CA, and every single unit tests works again, except the ones that deal with revocation. Expired certs work as expected, but not revoked certs.
I'm at a loss for what to do next to get this code working again, and would love some help. Thanks!
Turns out the problem was an application called Tumbleweed. It was installed on the server, and was intercepting all revocation requests. Disabling the Tumbleweed service fixed my problem entirely.
Charles.
The chain is simply a list of the certificates, starting with the one you providing, all the way up to the root certificate. It's built out of whatever certificates can be found -- even if they are out of date, invalid, or revoked.
What you're trying to do is validate the chain. Use the chain.ChainStatus for that.
From the MSDN docs:
The X509Chain object has a global error status called ChainStatus that should be used for certificate validation. The rules governing certificate validation are complex, and it is easy to oversimplify the validation logic by ignoring the error status of one or more of the elements involved. The global error status takes into consideration the status of each element in the chain.

How can I check signature of a SignedCms envelope?

I don't really understand how to work with PKCS#7 messages.
I sign some byte array with a X509Certificate2 I have and get also a byte array.
byte[] data = new byte[5] { 110, 111, 112, 113, 114 }, signedData;
X509Certificate2 cert = new X509Certificate2(certPath, password);
ContentInfo content = new ContentInfo(data);
SignedCms envelope = new SignedCms(content);
CmsSigner cmsSigner = new CmsSigner(cert);
envelope.ComputeSignature(cmsSigner);
signedData = envelope.Encode();
The signedData is transmitted to some remote recipient and he gets the SignedCms envelope.
SignedCms envelope = new SignedCms();
envelope.Decode(signedData);
How can he decode the envelope? He doesn't pass my public key as a parameter. There's my public key in the envelope, in SignerInfo property, but is there any reason for that, cause anyone can replace it with the whole signature?
He can the recipient make sure, using my public key that he has, that the actual sender of the envelope is me?
There's method envelope.CheckSignature(new X509Certificate2Collection(certificate), true); but I tried to use wrong certificate and there was no exception thrown.
A PKCS#7 / CMS / S/MIME signed message is a data container which has (in addition to some other metadata):
EncapsulatedContentInfo
ContentInfoType
EncapsulatedContent (the message bytes)
Certificates (Optional)
CRLs (Optional)
SignerInfos
DigestAlgorithm (e.g. SHA-1)
SignedAttributes (Optional, allows other context information to be signed)
SignatureAlgorithm (e.g. RSA, DSA, ECDSA)
SignatureValue (the signature bytes)
UnsignedAttributes (Optional, allows for after-signing information, like counter-signatures)
(This is a summary of RFC 2630 (Cryptographic Message Syntax) Section 5)
SignedCms.Decode reads the encoded message and populates members. Every direct signatory to the message can be read from the SignedCms::SignerInfos property (counter-signers, or entities which have signed that they witnessed the original signature, can be read from SignerInfo::CounterSignerInfos).
When you call SignedCms.CheckSignature, it checks every SigerInfo and verifies that the signature can be successfully verified (or throws an exception), as well as that every counter-signer signature can be worked out.
What it doesn't know is that any of the signers "made sense". For that check you would need to loop over each SignerInfo and look at (for example) the Certificate property; then perform a suitability check:
Perhaps it is a pre-registered public key
Perhaps it chains up to a well-known root or intermediate CA
Perhaps it has some sort of Extension which shows it to be suitable
This part SignedCms cannot realistically do for you, since there's no default notion of "suitable" for messages, unlike the hostname verification of TLS.
If you want to assess the signature of a single signer, you can call SignedInfo::CheckSignature, but that's redundant if you also called SignedCms::CheckSignature.
There's method envelope.CheckSignature(new X509Certificate2Collection(certificate), true); but I tried to use wrong certificate and there was no exception thrown.
The extraCerts overloads provide extra certificates. It's valid to have a SignedCms message which does not embed the signer certificates, leaving it up to the recipient to have known the valid certs ahead of time (e.g. using a per-user database of pre-registered certificates). You didn't get an exception because the correct certificates were found within the provided certificates collection.
You can see what was in the provided certificates collection via the X509Certificate2Collection.Import methods; they can read a PKCS#7 signed-data message and populate the collection with the optional embedded certificates.
A PKCS#7 by itself is just a signature, could it be replaced? sure. envelope.CheckSiganture just validates that pkcs#7 has the right format and length, in other words checks if a pkcs#7 is well constructed.
Broadly putted, you need to implement a PKI (Private Key Infrastructure). Where in one end you construct your pkcs#7 using a public key, and on the other end you must validate that the pkcs#7 you have actually has a valid certificate that you recognize as your own. You must implement an OCSP to validate those certificates and if everything checks out all right you should and must request a timestamp to a third party to vouch for your pkcs#7. Also you will need a vault (database) to keep track of everything: pkcs#7's, data hashes, timestamps, original data, ocsp responses...
But if you are only interested in knowing how to identify a pkcs#7, there are various tools you could use to decode a PKCS#7, this action gives back all the information contained in it. Or you could create your own using c#.

SignedCms.CheckSignature checks the signature against which certificates?

I have a signed message and I want to know against what certificate is this code checking the signature.
Does the SignedCms always have the signing certificate in it (and it is used to verify the signature) or sometimes the certificate isn't inside the message and it is taken from the verifying machine Certificate Store's?
Basically I'm interested in identifying who is the User that signed that message.
Here is the example of code that makes that verification (from msdn: https://msdn.microsoft.com/en-us/library/aedbc064(v=vs.110).aspx )
// Create a ContentInfo object from the inner content obtained independently from encodedMessage.
ContentInfo contentInfo = new ContentInfo(innerContent);
// Create a new, detached SignedCms message.
SignedCms signedCms = new SignedCms(contentInfo, true);
// encodedMessage is the encoded message received from the sender.
signedCms.Decode(encodedMessage);
// Verify the signature without validating the certificate.
signedCms.CheckSignature(true); //<-- Here is the verification
Thank you, and sorry for my poor english.
SignedCms is represented by an ASN.1 structure SignedData defined in RFC 2315
SignedData ::= SEQUENCE {
version Version,
digestAlgorithms DigestAlgorithmIdentifiers,
contentInfo ContentInfo,
certificates
[0] IMPLICIT ExtendedCertificatesAndCertificates
OPTIONAL,
crls
[1] IMPLICIT CertificateRevocationLists OPTIONAL,
signerInfos SignerInfos }
Property certificates as described by RFC 2315
is a set of PKCS #6 extended certificates and X.509 certificates. It
is intended that the set be sufficient to contain chains from a
recognized "root" or "top-level certification authority" to all of the
signers in the signerInfos field. There may be more certificates than
necessary, and there may be certificates sufficient to contain chains
from two or more independent top-level certification authorities.
There may also be fewer certificates than necessary, if it is expected
that those verifying the signatures have an alternate means of
obtaining necessary certificates (e.g., from a previous set of
certificates).
But it is optional.
signerInfos is described as
signerInfos is a collection of per-signer information. There may be any number of elements in the collection, including zero.
SignerInfo contains IssuerAndSerialNumber element that describes what certificate was used to sign the content.
More info in RFC 2315
In c# you can get the certificate with this code:
signedCms.SignerInfos[0].Certificate

Categories

Resources