-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Adding wrappers to Cryptui/Crypt32 and structures to WinCrypt. #915
Conversation
Working on fixing these. I had run 'ant dist test' but not 'test-platform'. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your work Roshan! I added comments to the bindings, as some of these need work to be correct.
Please have a look at the comments and see if they are helpful for you. If you find something unclear, feel free to ask.
public int cMsgCert; | ||
public PCERT_CONTEXT rgpMsgCert; | ||
public int cMsgCrl; | ||
public CRL_CONTEXT rgpMsgCrl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks strange - accoring to the documentation:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa381468(v=vs.85).aspx
this is a Pointer, not the structure itself.
public static class CTL_ENTRY extends Structure { | ||
public DATA_BLOB SubjectIdentifier; | ||
public int cAttribute; | ||
public CRYPT_ATTRIBUTE rgAttribute; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the documentation this is an array of CRYPT_ATTRIBUTE. This binding would not even work with a single CRYPT_ATTRIBUTE, as the attribute is not embedded in the structure. This might help:
public int cbSize; | ||
public CRL_CONTEXT pBaseCRLContext; | ||
public CRL_CONTEXT pDeltaCRLContext; | ||
public CRL_ENTRY pCrlEntry; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the above elements are defined as pointers to structures and not embedded. These should be defined as CRL_CONTEXT.ByReference
and CRL_ENTRY.ByReference
public CRL_CONTEXT pBaseCRLContext; | ||
public CRL_CONTEXT pDeltaCRLContext; | ||
public CRL_ENTRY pCrlEntry; | ||
public boolean fDeltaCrlEntry; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a BOOL
on the native side. To make this work you'll need to specify a type mapper to get this correctly mapped. See SSPI.SecPkgInfo
. There the W32APITypeMapper.DEFAULT
is used, that mapper not only maps java boolean to native BOOL, it also maps string correctly to a WString if the unicode binding is used.
This also applies to other structures below.
public int cbSize; | ||
public int dwRevocationResult; | ||
public LPSTR pszRevocationOid; | ||
public LPVOID pvOidSpecificInfo; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the info should be supplied from the java side I would think about using a plain Pointer, that way you can prepare a Memory object and set it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not used anymore but i've updated the type.
*/ | ||
boolean CertGetCertificateChain(Pointer hChainEngine, CERT_CONTEXT pCertContext, Pointer pTime, | ||
Pointer hAdditionalStore, CERT_CHAIN_PARA pChainPara, int dwFlags, Pointer pvReserved, | ||
CERT_CHAIN_CONTEXT ppChainContext); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good if a subclass of HANDLE
would be introduces for hChainEngine
and that the two predefined engines HCCE_CURRENT_USER
and HCCE_LOCAL_MACHINE
are bound.
pTime
should be a FILETIME
.
I would consider introducing HCERTSTORE
as a subclass for HANDLE
for the hAdditionalStore
parameter.
It would be good if a unittests would be added, that verifies, that the output parameter ppChainContext
is correctly filled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated these.
* @return If the function succeeds, the return value is TRUE. If the function | ||
* fails, the return value is FALSE. | ||
*/ | ||
boolean CertCloseStore(HANDLE hCertStore, int dwFlags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment for CertGetCertificateChain
the hCertStore
parameter is used inconsistently. It would be good if this would be unified.
* null character. If psz is NULL or csz is zero, returns the required | ||
* size of the destination string. | ||
*/ | ||
int CertNameToStr(int dwCertEncodingType, Pointer pName, int dwStrType, char[] psz, int csz); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pName
is a PCERT_NAME_BLOB
which is typedeffed from DATA_BLOB
. Can the correct type be used here?
The char[]
mapping for psz
is problematic. This is correct if the system property w32.ascii
is not set. This property determines if the Ascii or the unicode variant of the type- and functionmapper is used by default.
In this case I'd advise this: Bind CertNameToStr
with Pointer as psz
parameter and add utility method to Crypt32Util. That function would determine the needed size of the memory block to hold the name (depending on the value of the mentioned system property the factor 2 (unicode in windows is UCS2), allocate that memory, call the function and read the string from the memory.
See the comment about the return value:
Returns the number of characters converted, including the terminating null character.
If psz is NULL or csz is zero, returns the required size of the destination string.
* the policy. | ||
*/ | ||
boolean CertVerifyCertificateChainPolicy(int pszPolicyOID, CERT_CHAIN_CONTEXT pChainContext, | ||
CERT_CHAIN_POLICY_PARA pPolicyPara, CERT_CHAIN_POLICY_STATUS pPolicyStatus); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pszPolicyOID
is a string, not an int. Beware - this is a LPCSTR so must always be called with ascii type mapping!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the methods in Crypt32 have params for both UNICODE and ASCII, would it be okay to keep the type mapper as the DEFAULT one and use 'com.sun.jna.platform.win32.WTypes.LPSTR' for pszPolicyOID?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can do this, but then you need to fix it first. LPSTR is defined as:
typedef char CHAR;
typedef /* [string] */ __RPC_string CHAR *LPSTR;
So it is an array of chars. The current definition in WTypes.java
maps it to w_char. This should to it:
public static class LPSTR extends PointerType {
public static class ByReference extends LPSTR implements
Structure.ByReference {
}
public LPSTR() {
super(Pointer.NULL);
}
public LPSTR(Pointer pointer) {
super(pointer);
}
public LPSTR(String value) {
this(new Memory(value.length() + 1L));
this.setValue(value);
}
public void setValue(String value) {
this.getPointer().setString(0, value);
}
public String getValue() {
Pointer pointer = this.getPointer();
String str = null;
if (pointer != null)
str = pointer.getString(0);
return str;
}
@Override
public String toString() {
return this.getValue();
}
}
Please test this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I start working on this and push out the changes i made for the other comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated and tested this.
* CertFreeCertificateContext function. | ||
*/ | ||
CERT_CONTEXT CryptUIDlgSelectCertificateFromStore(HANDLE hCertStore, HWND hwnd, String pwszTitle, | ||
String pwszDisplayString, int dwDontUseColumn, int dwFlags, PointerType pvReserved); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parameter hCertStore
should be checked - see above comments. For the string mappings beware! these are defined as LPWSTR
and the binding to String is only correct if w32.ascii
is not set/set to false or the whole binding is mapped with the W32APITypeMapper.UNICODE
.
I missed another thing: This is a big enhancement, you should add an entry to |
Thank you for the comments, Matthias. I will start working on these. |
Hi Matthias, Could you take a look at the changes again and let me know if i need to makes any more changes? |
@@ -179,7 +179,7 @@ public String getString() { | |||
} | |||
|
|||
public static class LPSTR extends PointerType { | |||
public static class ByReference extends BSTR implements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure you don't extend BSTR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is correct. The inner class LPSTR.ByReference
is a LPSTR
class marked as ByReference
(the latter is a marker interface, that forces JNA to pass the value as a reference).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went through the code again. The main thing I noticed was the mapping of array elements. If the structures are read-only (info only flows from native to java), you can just add a getter to get the whole array via
Structure#toArray
If the info is also set from the Java side, then it is more a question when synchronization happens. Structures are automaticly synchronizing the java mirrors, when a transtion Java<->Native or Native<->Java happens. For arrays this is difficult....
* function fails, the return value is zero (FALSE). | ||
*/ | ||
boolean CryptSignMessage(CRYPT_SIGN_MESSAGE_PARA pSignPara, boolean fDetachedSignature, int cToBeSigned, | ||
Pointer rgpbToBeSigned, Pointer rgcbToBeSigned, Pointer pbSignedBlob, IntByReference pcbSignedBlob); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rgcbToBeSigned
is an array of DWORD
/int
rgpbToBeSigned
is an array of Pointers
I would expect, that the user setups one or multiple Memory
objects that hold the data to be signed and that binding would match this expected usage better.
@@ -179,7 +179,7 @@ public String getString() { | |||
} | |||
|
|||
public static class LPSTR extends PointerType { | |||
public static class ByReference extends BSTR implements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is correct. The inner class LPSTR.ByReference
is a LPSTR
class marked as ByReference
(the latter is a marker interface, that forces JNA to pass the value as a reference).
// You can use a command similar to the following to create a | ||
// certificate that can be used with this example: | ||
// | ||
// makecert -n "cn=cryptsigntest" -sk cryptsigntest -ss my |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about creating the certificates on the fly and add them to the user stored. At test shutdown these could be removed again.
That way you could really test the functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am having some issues with implementing the CryptAcquireContext wrapper in advapi32, which is part of the workflow for creating a self signed certificate. I will update this after i figure out the issue.
} | ||
IntByReference pcbSignedBlob2 = new IntByReference(0); | ||
Crypt32.INSTANCE.CryptSignMessage(sigParams, false, messages2.length, rgpbToBeSigned2, rgcbToBeSigned2, Pointer.NULL, pcbSignedBlob2); | ||
int signedSize2 = pcbSignedBlob2.getValue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not use StringArray here - its byte arreay, that are signed, so I would use a Memory (see my comment in the CryptSignMessage
method).
See also the comment in testCertGetCertificateChain
for usage of a generated certificate.
*/ | ||
public static Pointer allocateTCharMemory(int size) { | ||
size = Boolean.getBoolean("w32.ascii") ? size : size * 2; | ||
return new Memory(size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this used somewhere? The size looks off by one (if not otherwise stated I expect strings to be terminate by a NULL byte. So size
needs to be corrected, as size denotes number of characters, not bytes.
I would not use this function and add wrappers for the functions to Crypt32Util
that handle the allocation inline.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had misunderstood your last comment about CertNameToStr. I've updated it to have a utility wrapper method for it instead of this.
public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm; | ||
public Pointer pvHashAuxInfo; | ||
public int cMsgCert; | ||
public PCERT_CONTEXT.ByReference rgpMsgCert; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an array.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been having some issue getting the tests to pass after changing these to pointers and adding setters that handle conversion from arrays. I'll update this in the next commit once i figure out the right way to have it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had used this example to create the structure and test: https://www.eshayne.com/jnaex/index.html?example=6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am having difficulty(Invalid memory access) setting it to a pointer and getting it to work. But this seems to work:
'''
public int cMsgCert;
public PCERT_CONTEXT.ByReference rgpMsgCert;
public void setRgpMsgCert(CERT_CONTEXT.ByReference[] contexts) {
PCERT_CONTEXT.ByReference certContextArray = new PCERT_CONTEXT.ByReference();
PCERT_CONTEXT[] certContextArrayValues = (PCERT_CONTEXT[]) certContextArray.toArray(contexts.length);
for(int i = 0; i<contexts.length; i++) {
certContextArrayValues[i].certContext = contexts[i];
}
this.cMsgCert = contexts.length;
this.rgpMsgCert = certContextArray;
}
'''
If this implementation sounds good to you, i'll implement the same for the rest of the array members in this structure and test them.
(I think this structure is the last change i have to make. I've pushed out changes for the rest.)
public int cMsgCert; | ||
public PCERT_CONTEXT.ByReference rgpMsgCert; | ||
public int cMsgCrl; | ||
public CRL_CONTEXT.ByReference rgpMsgCrl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an array.
public int cMsgCrl; | ||
public CRL_CONTEXT.ByReference rgpMsgCrl; | ||
public int cAuthAttr; | ||
public CRYPT_ATTRIBUTE.ByReference rgAuthAttr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an array.
public int cAuthAttr; | ||
public CRYPT_ATTRIBUTE.ByReference rgAuthAttr; | ||
public int cUnauthAttr; | ||
public CRYPT_ATTRIBUTE.ByReference rgUnauthAttr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an array.
public int cUnauthAttr; | ||
public CRYPT_ATTRIBUTE.ByReference rgUnauthAttr; | ||
public int dwFlags; | ||
public int dwInnerContentType; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MSDN sees two further members here:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa381468(v=vs.85).aspx
HashEncryptionAlgorithm
and pvHashEncryptionAuxInfo
Thank you. I'm working on these and will send an update soon. |
Thank you for the updates. I will not have the time for a full review this weekend, but I rechecked the array structures and I think the approach will work for the plain structure arrays, but there are arrays, that are arrays of pointers to structures. These are the ones I found:
For CTL_USAGE you could see if you manage to bind it as a plain String[] (with an ASCII type mapper). In principle this should work. |
I tried this out for one of the array of pointer to structures and tested it with CryptSignMessage. The following approach works:
Could you confirm if this looks okay? Thanks! Edit: I added a test for retrieving from an array of pointer types (CERT_INFO.rgExtension) after a certificate is retrieved from the store and this seems to retrieve the array properly. Was that the issue you were concerned about? |
I had another look at the bindings and tried to implement the samples from MSDN using these. To get to a testable point I updated structures and bindings. From my perspective the most important point: The modified tests actually test the functionality. The sample programms from the MSDN look like a good place to start: https://msdn.microsoft.com/en-us/library/windows/desktop/aa382372(v=vs.85).aspx The primary problem of the API is the massive use of Pointer types and arrays. Arrays are problematic if the are pointers to structures, as the java side needs to keep a strong reference to prevent the structure from premature GC. This can be worked around, if the structure is used only one way (see CRYPT_SIGN_MESSAGE_PARA), but is problematic if the structure is written/modified from native and java side. I squashed your commits and added my modifications on top: https://github.com/matthiasblaesing/jna/tree/rosh89-CryptoLib This is not a complete solution, but it shows what I mean above. When I updated the SSPI bindings, I started with separate extensions of the bindings in a separate project. I implemented the basic functions to check if the bindings are viable. The result can be seen here: https://github.com/matthiasblaesing/jna-sspi-sample I repeat my question: What is the intention of the bindings? Most functions could be easier handled by using the bouncy castle libraries or even the JDK core classes. I would focus on the core of what you want and that can't be archived with pure java. |
- CryptSignMessage + CryptVerifyMessageSignature functions and structures rebound and updated testcase to test full cycle - In the structures holding arrays, the basic binding hold now only a pointer and adds a helper, that converts the pointer to the array of structures. Allowing improved bindings to use the pointer to add custom bindings. WinCryptUtil.MANAGED_CRYPT_SIGN_MESSAGE_PARA is a sample for this. - Updated a working test for Crypt32#CertNameToStr - Adding working version of Crypt32Util#CertNameToStr - Use custom PKCS12 Store to test CertGetCertificateChain and update bindings of associated structures - Use Structure#createFieldsOrder and hold fieldOrder in static field instead of recreating it on all calls - Move Indent to 4 spaces (as in surrounding code)
Merged updated changeset via e939e66 Thank you for working through this. |
Thanks Matthias. |
No description provided.