Edo
Edo

Reputation: 1841

How to generate BinarySecurityToken (X509PKIPathv1) from .p12 file

I am trying to consume a Java web service from a C# client. The service requires BinarySecurityToken element with value type X509PKIPathv1.

<wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" 
      ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1">
      MIIH......
</wsse:BinarySecurityToken>

Since WCF does not support X509PKIPathv1 value type, I am generating the SOAP message by hand, signing it using the SignedXml class, encrypting it using the EncryptedXml and sending it using the WebClient class. As for the value of BinarySecurityToken element, I used the value generated in SoapUI for the same certificate, and it works.

But, I would like to be able to generate this value programmatically from .p12 file, and not having to paste it from SoapUI again every time when the certificate expires.

The WS-Security documentation is a bit vague, so I am not sure how to go about it. This is all the information it gives about this token type:

#X509PKIPathv1: An ordered list of X.509 certificates packaged in a PKIPath

How to generate this value from .p12 file in C#? SoapUI does it somehow.

Upvotes: 2

Views: 2410

Answers (2)

csh-comms
csh-comms

Reputation: 11

Based on asn1, you need to start SEQUENCE tag (0x30), then encoded length of sequence. C# code (assuming, your cert chain is not crazy-long:

 byte[] X509toPKIPath(X509Certificate2 source)
 {
     X509Chain chain = new X509Chain();
     chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
     chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
     chain.Build(source);

     MemoryStream ms = new MemoryStream();
     ms.Write(new byte[] { 0x30, 0x82, 0x00, 0x00 }, 0, 4);

     // should be == ; order seem to be CA -> leaf, not other way around
     if (chain.ChainElements.Count > 0 && chain.ChainElements[0].Certificate.IssuerName.Name == chain.ChainElements[0].Certificate.Subject)
     {
         foreach (X509ChainElement element in chain.ChainElements)
         {
             byte[] leaf = element.Certificate.Export(X509ContentType.Cert);
             ms.Write(leaf, 0, leaf.Length);
         }
     }
     else
     {
         for (int i = chain.ChainElements.Count - 1; i >= 0; i--)
         {
             byte[] leaf = chain.ChainElements[i].Certificate.Export(X509ContentType.Cert);
             ms.Write(leaf, 0, leaf.Length);
         }
     }
     ms.Seek(2, SeekOrigin.Begin);
     ms.WriteByte((byte)((ms.Length - 4) / 256));
     ms.WriteByte((byte)((ms.Length - 4) % 256));

     return ms.ToArray();
 }

I couldn't find any clear spec on how chain need to be ordered, so going from CA root to cert may be overkill

Upvotes: 1

tete17
tete17

Reputation: 11

So this may not be a fully solution to your problem but it may help you out somewhat.

This:

#X509PKIPathv1: An ordered list of X.509 certificates packaged in a PKIPath

means is a asn1 sequence or chain of certificates that you have used to sign your message. You can even see it here.

To give some context asn1 is way of representing data in way independent of the machine you are using. This data is binary and not human readable so you transform it to bade 64 and that is what you see in that field.

I am not entirely sure what the exact content of your .p12 file is but at the very least I assume it has the certificate of the private key you used to sign your message and maybe the chain until the publicly trusted certificate or CA.

I am mostly a C++ developer and I know openssl provides a C like api to read a certificate manipulate the underlying asn1 structure and the output it as a string.

Sorry for not providing with a greater level of detail or a code example

Upvotes: 1

Related Questions