I am making a JNA PKCS11 wrapper, and a strange thing happens when creating objects in the token (tested with CKO_DATA and CKO_CERTIFICATE). The result is CKR_TEMPLATE_INCONSISTENT in most of the time, and the object can be created after several tries. Maybe you know what happens.
The JNA interface and structure,
NativeLong C_CreateObject(final NativeLong hSession, final CK_ATTRIBUTE[] pTemplate, final NativeLong ulCount, final IntByReference phObject);
public class CK_ATTRIBUTE extends Structure {
public NativeLong type;
public Pointer pValue;
public NativeLong ulValueLen;
public static class ByReference extends CK_ATTRIBUTE implements Structure.ByReference {
}
public static class ByValue extends CK_ATTRIBUTE implements Structure.ByValue {
}
public CK_ATTRIBUTE() {
setAlignType(ALIGN_NONE);
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "type", "pValue", "ulValueLen" });
}
}
The wrapper,
public class Attribute {
private final CKA cka;
private final byte[] data;
public Attribute(final CKA cka, final byte[] data) {
this.cka = cka;
this.data = data.clone();
}
public CKA getCKA() {
return cka;
}
public byte[] getData() {
return data;
}
}
public static CK_ATTRIBUTE[] createNativeAttributes(final List<Attribute> attributes) {
final CK_ATTRIBUTE[] nativeAttributes = (CK_ATTRIBUTE[]) new CK_ATTRIBUTE().toArray(attributes.size());
for (int i = 0; i < attributes.size(); i++) {
final Attribute attribute = attributes.get(i);
nativeAttributes[i].type = attribute.getCKA().getValue();
final int len = attribute.getData().length;
final Pointer pointer = new Memory(len);
nativeAttributes[i].pValue = pointer;
pointer.write(0, attribute.getData(), 0, len);
nativeAttributes[i].ulValueLen = new NativeLong(len);
}
return nativeAttributes;
}
public NativeLong createObject(final NativeLong hSession, final CK_ATRIBUTE[] pTemplate) throws CryptokiException {
if (hSession == null) {
throw new IllegalArgumentException("hSession cannot be null");
}
if (pTemplate == null || pTemplate.length == 0) {
throw new IllegalArgumentException("pTemplate cannot be empty");
}
final IntByReference phObject = new IntByReference(0);
generateException(cryptoki.C_CreateObject(hSession, pTemplate, new NativeLong(pTemplate.length), phObject));
return new NativeLong(phObject.getValue());
}
The test snippet,
// create CKO_DATA
final List<Attribute> attributes = new ArrayList<>();
attributes.add(new Attribute(CKA.CLASS, new byte[] { CKO.DATA.getValue().byteValue() }));
attributes.add(new Attribute(CKA.TOKEN, new byte[] { Cryptoki.CK_TRUE }));
attributes.add(new Attribute(CKA.APPLICATION, "My Application".getBytes()));
attributes.add(new Attribute(CKA.VALUE, new byte[] { 0x01, 0x02, 0x03, 0x04 }));
attributes.add(new Attribute(CKA.LABEL, "Test Label".getBytes()));
final CK_ATTRIBUTE[] pTemplate = CryptokiWrapper.createNativeAttributes(attributes);
final NativeLong hObject = wrapper.createObject(hSessionRW, pTemplate);
// create CKO_CERTIFICATE
final CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
try (final InputStream inputStream = new FileInputStream("src/main/resources/test.cer")) {
final X509Certificate x509Certificate = (X509Certificate) certificateFactory.generateCertificate(inputStream);
final List<Attribute> attributes = new ArrayList<>();
attributes.add(new Attribute(CKA.CLASS, new byte[] { CKO.CERTIFICATE.getValue().byteValue() }));
attributes.add(new Attribute(CKA.CERTIFICATE_TYPE, new byte[] { CKC.X_509.getValue().byteValue() }));
attributes.add(new Attribute(CKA.TOKEN, new byte[] { Cryptoki.CK_TRUE }));
attributes.add(new Attribute(CKA.LABEL, "Test Cert".getBytes()));
attributes.add(new Attribute(CKA.SUBJECT, x509Certificate.getSubjectDN().getName().getBytes()));
attributes.add(new Attribute(CKA.ID, new byte[] { 0x01, 0x02 }));
attributes.add(new Attribute(CKA.VALUE, x509Certificate.getEncoded()));
final CK_ATTRIBUTE[] pTemplate = CryptokiWrapper.createNativeAttributes(attributes);
final NativeLong hObject = wrapper.createObject(hSessionRW, pTemplate);
}
The probability of success is much higher if run the application in debug mode and set some breakpoints.
Note, all the codes runs under a single thread.
Finally I find out the reason of this issue,
attributes.add(new Attribute(CKA.CLASS, new byte[] { CKO.DATA.getValue().byteValue() }));
this line is wrong, the value of CKO_DATA is 0x00000000, which means it is a 32bit integer, so the length of it is 4, and the value is an array of byte; and after some test, the value is a little endian sequence.
The reason why sometimes the execution of method is succeeded is, sometimes the sequence is 00 00 00 00 in memory and fit the requires of the method, especially in the debug mode.