Open mhummels opened 2 years ago
What an interesting subject!
1) Building OpenSSL on Windows should not be difficult-- the instructions are in https://github.com/openssl/openssl/blob/master/NOTES-WINDOWS.md
2) You will need the tpm2-tss
library with the ESYS API (no need to bother with FAPI). There is (a bit old) VS solution for that: https://github.com/tpm2-software/tpm2-tss/blob/master/tpm2-tss.sln
3) Under Windows the tpm2-tss
shall be able to communicate using TBS via the libtss2-tcti-tbs
.
4) Once the openssl and the tpm2-tss with tcti-tbs are built, building the tpm2-provider itself should be straightforward.
It would be great if you could then share your results and bugfixes to make this work also for others.
Thank you for the good quick start. I now managed to build everything on windows successfully but openssl is not willing to load the library:
openssl list -providers -provider tpm2
list: unable to load provider tpm2
Hint: use -provider-path option or OPENSSL_MODULES environment variable.
48110000:error:12800067:DSO support routines:win32_load:could not load the shared library:crypto\dso\dso_win32.c:108:filename(C:\Program Files\OpenSSL\lib\ossl-modules\tpm2.dll)
48110000:error:12800067:DSO support routines:DSO_load:could not load the shared library:crypto\dso\dso_lib.c:152:
48110000:error:078C0105:common libcrypto routines:provider_init:init fail:crypto\provider_core.c:910:name=tpm2
I found another issue where the entry point of the DLL could not have been found. So I checked if my binary has the correct entry point and it looks like its fine:
dumpbin /exports "C:\Program Files\OpenSSL\lib\ossl-modules\tpm2.dll"
Microsoft (R) COFF/PE Dumper Version 14.32.31328.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file tpm2.dll
File Type: DLL
Section contains the following exports for tpm2.dll
00000000 characteristics
0 time date stamp
0.00 version
0 ordinal base
2 number of functions
1 number of names
ordinal hint RVA name
1 0 0000D0C0 OSSL_provider_init = OSSL_provider_init
Summary
1000 .00cfg
1000 .data
1000 .pdata
A000 .rdata
1000 .reloc
1000 .retplne
1000 .rsrc
F000 .text
The tpm2.dll is exactly at the location OpenSSL tried to load from, but somehow it does not work. Do I need to recompile OpenSSL in order to use the provider or am I missing something else?
You should not need to recompile OpenSSL. The error is either because there is no OSSL_provider_init
function in the tpm2.dll
(I can see there is one) or because calling this function returned an error.
Let's assume there is something wrong inside the tpm2.dll. Did you build the tpm2 without NDEBUG
defined? If so, you should see some debugging stderr
messages when the tpm2 functions are called. If NDEBUG
is not defined and you still see no messages, then the OSSL_provider_init
is likely not called.
If this is the case, could you make sure (e.g. using https://www.dependencywalker.com/) that all libraries the tpm2.dll depends on are available as well?
Since there was no debug output I checked the dependencies. Depencywalker was telling me that everything is fine except some Windows core libraries which appears to be a problem with Dependencywalker itself. I also tried another Tool (https://github.com/lucasg/Dependencies) to check which also finds all dependencies.
Since I just copied the tpm2-tss libraries to OpenSSL\lib\ossl-modules, I tried to add this folder to %PATH% to be 100% sure and now I am a step forward. There is indeed some debug output:
C:\Program Files\OpenSSL\bin>openssl list -provider tpm2
PROVIDER INIT
WARNING:tcti:tcti-tbs.c:292:Tss2_Tcti_Tbs_Init() Failed to create context with TBS error: 0x8028400f
ERROR:tcti:tctildr-nodl.c:152:tctildr_get_default() No standard TCTI could be loaded
ERROR:tcti:tctildr.c:428:Tss2_TctiLdr_Initialize_Ex() Failed to instantiate TCTI
list: unable to load provider tpm2
Hint: use -provider-path option or OPENSSL_MODULES environment variable.
00660000:error:40000006:lib(128):OSSL_provider_init:reason(6):src\tpm2-provider.c:410:655370
00660000:error:078C0105:common libcrypto routines:provider_init:init fail:crypto\provider_core.c:910:name=tpm2
However it is still not working as now the problem appears to be the communication using TBS
EDIT: I just found that the error code refers to a missing TPM module. And the library seems to be correct, since the PC I am testing on is indeed missing the TPM module. So I will check again on another PC.
EDIT2: I checked the BIOS and enabled the TPM and now the tpm2 provider is working as expected. I will create a pull request with the small necessary changes to be able to build the provider on windows in the next days
Brilliant! Thank you for your perseverance.
Thank you for your great support!
I just tried to play around a little with generating CSRs and so on, but discovered an error with esys invalid ESAPI handle:
openssl req -new -provider tpm2 -provider default -sha256 -key testkey.pem -out intermediate.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:DE
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
ERROR:esys:esys_iutil.c:394:iesys_handle_to_tpm_handle() Error: Esys invalid ESAPI handle (40000001).
WARNING:esys:esys_iutil.c:416:iesys_is_platform_handle() Convert handle from TPM2_RH to ESYS_TR, got: 0x40000001
However, It seems that the csr is created correctly. Do you have an idea why I may got this error and why it is working though?
Here is the verification of the CSR for reference:
openssl req -in intermediate.csr -noout -text -verify
Certificate request self-signature verify OK
Certificate Request:
Data:
Version: 1 (0x0)
Subject: C = DE, ST = Some-State, O = Internet Widgits Pty Ltd
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:fe:ed:2c:af:a5:e3:45:50:9c:5a:97:c1:f6:07:
69:4a:5d:9d:25:fb:f4:78:53:f7:90:5e:38:01:e7:
02:ea:bb:a3:02:15:0d:14:2a:5d:6a:02:b9:e3:36:
d2:d0:bf:b1:7c:0f:b9:48:5b:7e:23:3a:88:dd:4c:
b9:60:0b:de:d1
ASN1 OID: prime256v1
NIST CURVE: P-256
Attributes:
(none)
Requested Extensions:
Signature Algorithm: ecdsa-with-SHA256
Signature Value:
30:46:02:21:00:e7:ac:88:36:44:d6:41:19:99:b8:ce:bb:c4:
09:44:b0:31:cf:46:ff:9a:0f:75:4d:3c:00:e7:51:c8:11:8d:
fc:02:21:00:ef:4f:2b:39:74:7e:d0:6c:d3:5a:46:0a:c1:f8:
fe:da:f1:18:17:12:c1:9e:50:66:d9:10:6b:0a:e2:bf:2b:b9
Could it be the case you have tpm2-tss
version 3.x and you didn't define HAVE_TSS2_ESYS3
?
I forgot to tell you this. Linux is doing this automatically, but on Windows (when using tpm2-tss
version 3.x) you have to define HAVE_TSS2_ESYS3
. The code contains several #ifdef HAVE_TSS2_ESYS3
.
There is also #ifdef WITH_TSS2_RC
, which enables nicer TPM error codes. It requires tss2-rc
. If you have built that (which is likely the case) I recommend defining WITH_TSS2_RC
too. It may be useful in the future.
P.S. There should be no more hidden defines :)
Okay, this helped again. The error with generating the CSR is gone now and works as expected. Now I signed that CSR with my root CA and imported the resulting intermediate-cert.pem
back to the machine. When I now try to use that to sign a client certificate request I got the following long list of errors:
openssl x509 -req -days 7200 -provider tpm2 -provider base -in device.csr -CA intermediate-cert.pem -CAkey intermediate-key.pem -CAcreateserial -out device-cert.pem -sha256
ERROR:esys:api\Esys_ContextSave.c:251:Esys_ContextSave_Finish() Received a non-TPM Error
ERROR:esys:api\Esys_ContextSave.c:92:Esys_ContextSave() Esys Finish ErrorCode (0x80280400)
ERROR:esys:esys_iutil.c:1145:iesys_check_sequence_async() Esys called in bad sequence.
ERROR:esys:api\Esys_FlushContext.c:66:Esys_FlushContext() Error in async function ErrorCode (0x00070007)
Certificate request self-signature did not match the contents
A4130000:error:40000013:tpm2::cannot duplicate context::-1:-2144861184 40:0x400
A4130000:error:06880006:asn1 encoding routines:ASN1_item_verify_ctx:EVP lib:crypto\asn1\a_verify.c:217:
ERROR:esys:esys_iutil.c:1145:iesys_check_sequence_async() Esys called in bad sequence.
ERROR:esys:api\Esys_FlushContext.c:66:Esys_FlushContext() Error in async function ErrorCode (0x00070007)
The device.csr
is the client certificate request I try to sign to obtain the device certificate. The intermediate-cert.pem
is the signed certificate (by my root CA) using the TPM wrapped private key `intermediate-key.pem'.
Again, I have no idea whats wrong here and hope the solution will be again as easy and quick as before to finally end this tpm nightmare ;-D.
This may be worse, unfortunately. Will you please send me the entire stderr trace that did end with the above error?
I have no idea how to get that in windows. I just tried to use 2> output.err
to pipe the stderr to file but the result is the exact same as I already sent above.
In the meantime I found that the error code 0x80280400 tells that the ContextSave Command is blocked by the TBS according to this issue. I am currently trying to tell windows that it shouldn't block but since there were so many changes between TPM 1.2 and 2.0 it does not seem to be that easy.
Oh, I missed the 0x80280400 error. You don't need to send me anything, the blocked ContextSave is the problem.
According to microsoft the TBS is blocking that command since Windows 10 1809 and later with no way to unlock it again. Do you think there is a way to work around that? Is the ContextSave really necessary, since I also found another issue stating that they moved away from dealing with contexts directly. Would this be an option for tpm2-openssl, too?
Nevertheless, I now think there is no way to get the tpm provider running on windows or is it? Otherwise I finally need to ditch the windows idea and move back to linux again to make it 'just work'.
This is sad. I use ContextSave/ContextLoad for duplicating contexts, which is required to implement OpenSSL's hash sequence duplication. Not every OpenSSL operation requires duplicated sequences, but there are few. Let me double check if there is another way of duplicating the contexts.
EDIT: Question raised here. The other project may have found a way around, but they don't have to use OpenSSL.
I think you also have knowledge about tpm2-pytss project. Would it be possible to issue client certificates as CA using pytss? I am not that deep into x509 certificates and how to issue them based on the CSR in combination with the TPM.
I just read the documentation of pytss and found that it would be able to wrap and unwrap keys using TPM, but then it would be possible to unwrap the key and export it anyway, which I would like to prevent. I also found the certify and create_certify functions, but I think it is not as easy as throw the CSR in and get the certificate as response or is it? I was not able to find another project or explanation that this was done before. It would be nice if you could give me some hints here.
I don't think the pytss can create X.509 certificates. The tpm2_pytss.ESAPI.certify
is tpm2_certify, which is something completely different. To be honest, I am not aware of anything else suitable (other than openssl). That's why I made this provider.
EDIT: You can make your own X.509 library, but in such case it may be easier to just modify openssl so it can hash without sequence duplication.
Okay, I see. According to the openssl provider manual the minimum available functions are signature context new and free. So I tried to just remove the OSSL_FUNC_signature_dupctx_fn
declaration and OSSL_FUNC_SIGNATURE_DUPCTX
entries in the arrays. This seemed to be working at first, but then I discovered that there is no file written anymore. This is the output generated with NDEBUG
not defined:
openssl x509 -req -days 7200 -provider tpm2 -provider base -in device.csr -CA intermediate-cert.pem -CAkey intermediate-key.pem -CAcreateserial -out device-cert.pem -sha256
PROVIDER INIT
EC NEW
EC IMPORT [ point-format encoding group pub point-format group-check use-cofactor-flag ]
EC GET_PARAMS [ bits security-bits max-size ]
SIGN DIGEST_INIT ecdsa MD=SHA256
SIGN DIGEST_START
SIGN DIGEST_UPDATE
So there is no actual error but also no output file. Any idea?
The manual is slightly confusing in this respect. It is optional at the lower layer only-- there can be a provider that does not implement OSSL_FUNC_SIGNATURE_DUPCTX
, but such provider cannot be used for X.509 signing, because the higher layers depend on these functions. There is no mechanism in openssl to work around missing OSSL_FUNC_SIGNATURE_DUPCTX
.
I am developing an own CA which needs to be platform independent (Linux and Windows). To secure the private key, I like to use the TPM module to store the key and use openssl with tpm2 provider for signing the certificate requests.
Generally this is not an issue, since there are plenty of ways to get openssl running on windows as well (to name a few I already tried: Win64Openssl, Git for Windows, WSL2 with Ubuntu 22.04, MinGW). The point where it gets complicated is the usage of the TPM module. For example the TPM cannot be accessed from the WSL2 subsystem. Most of the mentioned ways to get openssl running use the old OpenSSL versions 1.1.x.
So I think the only possible way to get that running is to compile this project natively on Windows. Is there a way to do that and get this running with the latest openssl version? I have absolutely no idea where to start
Thanks for your help