openssl icon indicating copy to clipboard operation
openssl copied to clipboard

PKCS12: allow adding arbitrary bag attributes

Open kuzuk opened this issue 7 years ago • 69 comments

PKCS12 allows adding arbitrary attributes to its 'bags'. As far as I can tell OpenSSL's pkcs12 tool only supports the 'friendlyName' attribute (with the -name and -caname options).

Feature request: To better support the PKCS12 format add one or several command line options to the pkcs12 tool to allow adding arbitrary attributes to bags.

kuzuk avatar Jul 10 '18 08:07 kuzuk

@t8m @mattcaswell What is the status of this issue?

The minimum necessary would be the ability to add OID 2.16.840.1.113894.746875.1.1 (ORACLE_TrustedKeyUsage attribute) to the CA certificate imported into PKCS12 file.

Currently, OpenSSL barfs on it (i.e. doesn't even know what it is):

Certificate bag
Bag Attributes
    friendlyName: root-ca
    2.16.840.1.113894.746875.1.1: <Unsupported tag 6>

And it cannot be added to a new PKCS12 file, which makes it impossible to generate PKCS12 file to be used as a Java keystore.

levicki avatar Jun 06 '22 12:06 levicki

is this issue still open?

grahamwoodward avatar Jul 28 '22 15:07 grahamwoodward

For this specific reason, we can't just use openssl binary for creating a PKCS12 keystore with trusted CA chain which will work with Java application. It doesn't recognise CAs as trusted. But it works if importing CA chain with keytool which is part of JRE/JDK which is too fat for a sidecar container. We had to split the PKCS12 keystore created using openssl and truststore created using keytool which is not optimal as have to use 2 different tools.

Constantin07 avatar Jul 28 '22 15:07 Constantin07

@t8m @mattcaswell is this something I could help fix/implement?

grahamwoodward avatar Jul 30 '22 05:07 grahamwoodward

@levicki wondering if you had any ideas on how I could implement/investigate a solution for this? I'm wondering whether we add a new cli arg to set the OID or whether under the hood we set that OID if that's what's missing anyway?

grahamwoodward avatar Aug 01 '22 19:08 grahamwoodward

@grahamwoodward I am not very familiar with OpenSSL source code tree, but I would imagine that first step would be to add the OID to the file(s) where other known OIDs reside (assuming that the OpenSSL team thinks it is OK to add it).

From there, CLI should have at least the following modifications:

  1. It should be able to corectly parse the "unsupported tag 6" when decoding PKCS12 certificate bag attributes.
  2. It should be able to not discard said tag when saving the modifed PKCS12 file (i..e. when another certificate / key pair is imported into it or when it is re-encrypted).
  3. It should get command line options for both adding and removing the tag to / from certificates in a PKCS12 file.

As I said I am not an expert on the OpenSSL codebase, so it would be much better if we got some official pointers on how to proceed from someone on the team.

levicki avatar Aug 02 '22 01:08 levicki

Are those bag attributes applied to the PKCS#12 file or the certificate(s) in it?

grahamwoodward avatar Aug 05 '22 15:08 grahamwoodward

when I look at a PKCS#12 file with no private key in I see

openssl-certs$ openssl pkcs12 -info -in cert.pfx -nodes
Enter Import Password:
MAC: sha256, Iteration 2048
MAC length: 32, salt length: 8
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
Certificate bag
Bag Attributes: <No Attributes>
subject=CN = localhost
issuer=CN = localhost
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

grahamwoodward avatar Aug 05 '22 15:08 grahamwoodward

If I create the PKCS#12 with the private key as well, then I see

MAC: sha256, Iteration 2048
MAC length: 32, salt length: 8
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
Certificate bag
Bag Attributes
    friendlyName: alias
    localKeyID: 9E 53 61 FD 10 18 56 F9 83 E8 51 86 6C EA 4F BB 23 A1 D9 3E
subject=CN = localhost
issuer=CN = localhost
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
PKCS7 Data
Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
Bag Attributes
    friendlyName: alias
    localKeyID: 9E 53 61 FD 10 18 56 F9 83 E8 51 86 6C EA 4F BB 23 A1 D9 3E
Key Attributes: <No Attributes>
-----BEGIN PRIVATE KEY-----

grahamwoodward avatar Aug 05 '22 15:08 grahamwoodward

And using keytool to view it I see

~/openssl-1.1.1/openssl-1.1.1q/apps$ keytool -list -v -keystore cert.pfx -storetype PKCS12
Enter keystore password:
Keystore type: PKCS12
Keystore provider: SunJSSE

Your keystore contains 1 entry

Alias name: alias
Creation date: 05-Aug-2022
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:

when it has the private key. When it doesn't have the private key the same command gives me

~/openssl-1.1.1/openssl-1.1.1q/apps$ keytool -list -v -keystore /cert.pfx -storetype PKCS12
Enter keystore password:
Keystore type: PKCS12
Keystore provider: SunJSSE

Your keystore contains 0 entries

grahamwoodward avatar Aug 05 '22 15:08 grahamwoodward

From my output, I'm not sure why we think this OID value is missing/wrong?

grahamwoodward avatar Aug 05 '22 16:08 grahamwoodward

@levicki I appreciate your help with this, you know more than me lol

grahamwoodward avatar Aug 05 '22 16:08 grahamwoodward

how do you end up seeing that OID value? Guessing it's how you created the certificate/PKCS#12 file?

grahamwoodward avatar Aug 05 '22 16:08 grahamwoodward

I'm doing the following ```

Create key+cert

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=localhost'

Create PKCS#12 with no private key

openssl pkcs12 -export -name alias -passout pass:123456 -out cert.pfx -nokeys -in cert.pem

To view PKCS#12 using openssl

openssl pkcs12 -info -in cert.pfx -nodes

grahamwoodward avatar Aug 05 '22 16:08 grahamwoodward

spent some time looking through the code...looks like in pkcs12_main we parse the -infile argument into a local certs objects (a stack of x509 objects)..but because there's no keys, we never set another local ucert object. The ucert and certs objects are passed to the PKCS12_create() function and because the ucerts is NULL, we never set any Bag Attributes.

Not sure if that itself is an "issue" ?

I've tried a few hacks in the code, for instance if I set ucerts to be what's in my -infile argument, then I do see some Bag Attributes displayed when I run openssl pkcs12 -info -in <cert.pfx>...however the keytool still thinks there are 0 entries...guessing as mentioned previously I need this special OID attribute.

Looking around various files (obj_mac.h and obj_dat.c, x509_att.c) I can see anywhere where we're setting anything other than friendly name and local key id...

Do I need to create a new NID? Similar to

#define LN_friendlyName         "friendlyName"
#define NID_friendlyName                156
#define OBJ_friendlyName                OBJ_pkcs9,20L

#define LN_localKeyID           "localKeyID"
#define NID_localKeyID          157
#define OBJ_localKeyID          OBJ_pkcs9,21L

grahamwoodward avatar Aug 06 '22 13:08 grahamwoodward

@grahamwoodward You need to create a PKCS12 file using either Java keytool or KeyStore Explorer GUI tool.

Furthermore, you need to import a trusted certificate, not a keypair into said PKCS12 file:

image

If you now check the properties for this PKCS12 file, you will see that the imported trusted certificate is shown in a separate section titled Trusted Certificates:

image

If you check the file using OpenSSL CLI:

MAC: sha1, Iteration 100000
MAC length: 20, salt length: 20
PKCS7 Data
Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 10000, PRF hmacWithSHA256
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 10000, PRF hmacWithSHA256
Certificate bag
Bag Attributes
    friendlyName: wile e coyote
    localKeyID: 54 69 6D 65 20 31 36 35 39 38 30 32 39 34 31 36 33 32 
Key Attributes: <No Attributes>
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
Bag Attributes
    friendlyName: ssl.com ev root certification authority rsa r2
    2.16.840.1.113894.746875.1.1: <Unsupported tag 6>
subject=C = US, ST = Texas, L = Houston, O = SSL Corporation, CN = SSL.com EV Root Certification Authority RSA R2
issuer=C = US, ST = Texas, L = Houston, O = SSL Corporation, CN = SSL.com EV Root Certification Authority RSA R2

-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
Bag Attributes
    friendlyName: wile e coyote
    localKeyID: 54 69 6D 65 20 31 36 35 39 38 30 32 39 34 31 36 33 32 
subject=C = CA, ST = Wileshire, L = Wilewood, O = "ACME, INC.", CN = Wile E Coyote
issuer=C = CA, ST = Wileshire, L = Wilewood, O = "ACME, INC.", CN = Wile E Coyote

-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

The test.pfx file is attached, passphrase for both keystore and for the Wile E Coyote's private key is 1234 in case you want to play with it.

test.zip

So, to reiterate, the problem with OpenSSL CLI is that it is impossible to create PKCS12 file and have imported standalone certificates (i.e. not part of a keypair) marked as trusted using the OID shown.

Do I need to create a new NID?

Honestly I have no idea how that should be handled. Maybe looking at keytool source code could help.

Checking KeyStore.TrustedCertificateEntry as well as PKCS12Attribute classes shouldn't hurt either.

EDIT: Checking RFC 7292 Section 4.2 might also be useful. It seems that 2.16.840.1.113894.746875.1.1: <Unsupported tag 6> message might be referencing this part of specification:

   safeContentsBag BAG-TYPE ::=
       {SafeContents IDENTIFIED BY {bagtypes 6}}

That is defined in Section 4.2.6 as a recursive structure that can contain other bag types (including certificate bags), and its OID is 1.2.840.113549.1.12.10.1.6.

So first question is what exactly <unsupported tag 6> means and whether it is a correct error message to begin with, given that OpenSSL CLI can clearly parse the certificate stored inside.

Furthermore, in Section 4.2.3 it says for The CertBag Type:

Object identifiers are used to distinguish between different certificate types.

One way to read that is "CertBag attributes can contain OIDs to distinguish between different certificate types".

Sadly, I couldn't find an ASN.1 parser capable of fully parsing the structure of PKCS12 file.

levicki avatar Aug 06 '22 16:08 levicki

I would also add that OpenSSL output is really confusing and poorly structured -- it is not clear if there is one certificate bag, or multiple bags, and if there is only one certificate bag it is not clear whether it includes shrouded keybag as well. Perhaps looking at the ASN.1 dump would clarify this.

levicki avatar Aug 06 '22 17:08 levicki

OK I'll try with that but my Linux based test with those CLIs is still valid? I'm still creating a PKCS#12 with just a public certificate but unable to load into Java

grahamwoodward avatar Aug 06 '22 17:08 grahamwoodward

"I would also add that OpenSSL output is really confusing and poorly structured -- it is not clear if there is one certificate bag, or multiple bags, and if there is only one certificate bag it is not clear whether it includes shrouded keybag as well. Perhaps looking at the ASN.1 dump would clarify this."

you're not wrong lol @levicki

grahamwoodward avatar Aug 06 '22 17:08 grahamwoodward

@grahamwoodward I edited my original comment to add some (hopefully helpful) clarification.

As for the second comment I posted, in retrospect it is easy to criticize. Great skill and knowledge are required to understand any standard and be capable of writing code that correctly implements it in all its complexity.

It is often overlooked that presenting and allowing manipulation of the data in a structured form which is easily accessible to end users who have no clue about the underlying standard is a totally different, perhaps even greater skill -- one of reducing this tremendous complexity.

levicki avatar Aug 06 '22 18:08 levicki

@levicki yeah I hear you, the code is very complex and takes a lot of understanding. This is my first foray into this repo but keen to get this resolved.

grahamwoodward avatar Aug 06 '22 19:08 grahamwoodward

Isn't this the issue

https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java#L105

https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java#L723

grahamwoodward avatar Aug 07 '22 07:08 grahamwoodward

although guess that's JDK7 so fairly old

grahamwoodward avatar Aug 07 '22 07:08 grahamwoodward

https://github.com/openjdk/jdk18/blob/master/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java#L84

so this matches up to the patch posted originally

grahamwoodward avatar Aug 07 '22 09:08 grahamwoodward

I share some notes after looking at the code because i saw some misleading information on this thread.

  • The java keytool adds an unsupported vendor attribute on some bags.
  • The bag itself is a Safebag (specified at https://www.rfc-editor.org/rfc/rfc7292#section-4.2), with a CertBag type (type=3), which is correctly handled by openssl
  • The keytool adds the unsupported attribute '2.16.840.1.113894.746875.1.1', which is registered to oracle (http://www.oid-info.com/get/2.16.840.1.113894), and the the '746875.1.1' suffix is documented in the java source code (http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java#l167) as {jdk(746875) crypto(1) id-at-trustedKeyUsage(1)}. So this attribute could be labelled as 'oracleJdkTrustedKeyUsage'.
  • The single value of this attribute is set as anyExtendedKeyUsage, oid 2.5.29.37.0. (http://oid-info.com/get/2.5.29.37.0). The openssl prints <Unsupported tag 6> because the print_attribute function (defined at https://github.com/openssl/openssl/blob/master/apps/pkcs12.c#L1137) does not handle the type V_ASN1_OBJECT.

Im not sure openssl should support vendor attributes, and I cannot find any resources specifying this particular one besides java source code. However the use-case appears relevant, so we could still add a custom flag to handle this as Im not sure supporting custom attributes that may be encoded in different ways would be that simple.

Here is my quick analysis: the oid would need to be defined in objects.txt to provide a meaningful label and a type to check against and to know how to encode it. To support writing this attribute: the attribute would need to be handled in crypto/pkcs12/pkcs12_attr, then the apps/pkcs12 would need to be updated to support a new option to add this attribute; and the demos and tests adapted. To support displaying this attribute correctly: the apps/pkcs12 should handle the V_ASN1_OBJECT type, and use the appropriate formatter to parse and display the values. The 'ext-key-usage 0' is already defined for x509.

@t8m @mattcaswell Is it ok to add a dedicated flag to pkcs12 in order to add a dedicated attribute to a CertBag? We could use something similar to x509 -addtrust anyExtendedKeyUsage; for instance -addoracletrust anyExtendedKeyUsage.

@grahamwoodward I can help you with this if needed.

cghislai avatar Aug 07 '22 13:08 cghislai

@cghislai that's excellent thanks, much appreciated.

grahamwoodward avatar Aug 07 '22 13:08 grahamwoodward

@cghislai one "issue" I see though is in PKCS12_create here https://github.com/openssl/openssl/blob/master/crypto/pkcs12/p12_crt.c#L66

in my example/use case because I'm creating the PKCS#12 like so openssl pkcs12 -export -name alias -passout pass:123456 -out cert.pfx -nokeys -in cert.pem

the cert.pem is added to the STACK_OF(X509) ca variable and cert is NULL, so I'd need to add this new attribute within the loop for what the code thinks are the the list of CA certs, although I don't specify any on the CLI

https://github.com/openssl/openssl/blob/master/crypto/pkcs12/p12_crt.c#L75

This kind of seems wrong to me but I'm probably missing something.

grahamwoodward avatar Aug 07 '22 14:08 grahamwoodward

This kind of seems wrong to me but I'm probably missing something.

I think its normal - a new bag is created for each of the ca you add, so the attribute must be added on each of those bags. Unless Im missing something?

It appears to me you would need to implement a method similar to https://github.com/openssl/openssl/blob/master/crypto/pkcs12/p12_attr.c#L29, and call it from within that loop depending on the option param, similar to what is done on line 69 above (https://github.com/openssl/openssl/blob/master/crypto/pkcs12/p12_crt.c#L68).

cghislai avatar Aug 07 '22 16:08 cghislai

@cghislai

Im not sure openssl should support vendor attributes.

If bag attributes have a standard format defined by the RFC (say if they have to be formatted as OIDs and vendors respect that format), then how would supporting vendor defined bag attributes like ORACLE_TrustedKeyUsage be any different from supporting custom OIDs when creating a certificate?

Or are you saying that Netscape-specific OIDs or Microsoft-specific OIDs aren't vendor attributes?

I do agree that free-form bag attributes should be rejected, but I don't think that's the case here.

-addoracletrust anyExtendedKeyUsage

Sorry, but that's a horrible syntax -- anyExtendedKeyUsage seems superfluous, and -addoracletrust is plain incorrect -- you aren't trusting Oracle (remember the end user doesn't know the OID definition), you are adding Trusted(Public)KeyUsage property to enable trust in Java appllications when loading X509 certificate from PKCS12 file, which isn't exclusive to Oracle Java (OpenJDK most likely uses the same implementation). Therefore, I would suggest naming the flag -javatrustedcert or similar.

Please consider the behavior of keytool before deciding on the behavior of OpenSSL and on the syntax:

Which type of import is intended is indicated by the value of the -alias option. If the alias does not point to a key entry, then the keytool command assumes you are adding a trusted certificate entry. In this case, the alias should not already exist in the keystore. If the alias does already exist, then the keytool command outputs an error because there is already a trusted certificate for that alias, and does not import the certificate. If the alias points to a key entry, then the keytool command assumes you are importing a certificate reply.

I believe that different usage options are possible:

  1. Have OpenSSL CLI automatically add the OID to any certificate imported into the PKCS12 file which isn't imported with a matching private key (and an openssl.conf option to disable/enable that behavior which can default to disabled).
  2. Have a flag -javatrustedcert which applies during import and errors out if you are importing a keypair.
  3. Have a flag -javatrustedcert which can be applied to existing certificate by providing its alias as a parameter, again erroring out if its localkeyid matches a private key in the PKCS12 file.

Personally I would assume that you either want all certificates without private key to be trusted (since you are most likely establishing a trust chain for a server certificate), or none of them (if you don't need this feature) so I would prefer openssl.conf approach as shown in the option 1 above.

levicki avatar Aug 07 '22 16:08 levicki

@levicki We have to keep in mind the TODO in the java codebase, indicating the oid might be migrated in a future versions. Thats why i suggested the oracle name within the option; I had in mind addjdktrust for future jdk. I read it as add an oracle trust anchor, not as trust oracle. Allowing to pass the option value (anyExtendedKeyUsage) would allow to support others key usages. Even though within java it does not appear anything else is supported at the moment, it mimics how this attributes is set for x509, which sounds better usability wise, but its superfluous for this specific use case indeed.

As for the behaviour, i think an option makes sense to prevent breaking other tools by adding a new unsupported attributes using default options. All public keys not part of the key pair, each put in its own bag, would get the extra attribute if specified on the command line. So I would go for your option 2, but I would not error if no certificate is imported. Rather, like for the caname option, i would just warn if it specified while nothing is exported.

Or are you saying that Netscape-specific OIDs or Microsoft-specific OIDs aren't vendor attributes?

I agree, it should be supported. I find it inconvenient that it is not documented nowhere. For netscape, the attribute definitions have been published somewhere (https://www.ietf.org/rfc/rfc2798.txt).

cghislai avatar Aug 07 '22 16:08 cghislai