简体   繁体   中英

Signature generation with OpenSC Smart Card

I have Escrypt Smart Card to sign data bytes and get signature and certificate for it.

I have java tool to do it and everything was fine until Java 8 . Now application migrated to Java 11 . And problem start from here.

  1. SunPKCS11.jar/ library is not a part of Java 11, hence application breaks.
  2. I tried with different solution available over internet and oracle release notes , it suggest to use java.security package for cryptographic operation.

I am not able to switch from Java 8 to java 11, any support would be really useful.

Edit-1: Minimum usable code


  • Signature calculator is used to parse signature
  • It is written with Java 8 & SunPKCS11.jar want to migrate it to Java 11

'''

import java.io.IOException;
import java.util.Scanner;

import javax.xml.bind.annotation.adapters.HexBinaryAdapter;

import sun.security.pkcs11.wrapper.CK_ATTRIBUTE;
import sun.security.pkcs11.wrapper.CK_MECHANISM;
import sun.security.pkcs11.wrapper.CK_RSA_PKCS_PSS_PARAMS;
import sun.security.pkcs11.wrapper.PKCS11;
import sun.security.pkcs11.wrapper.PKCS11Constants;
import sun.security.pkcs11.wrapper.PKCS11Exception;

public class Application {

  public static void main(String[] args) {
    try {
      // PKCS11 middleware (UBK PKI) path
      String libraryPath = "";

      OpenScPkcs11 openScPkcs11 = new OpenScPkcs11(libraryPath);

      openScPkcs11.login("", "");
      byte[] hash = null;
      String keypath = null;
      String oid = null;
      String authbits = null;
      openScPkcs11.calcSign(hash, keypath, oid, authbits);

    }
    catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

}

class OpenScPkcs11 {

  private static HexBinaryAdapter hexBinaryAdapter = new HexBinaryAdapter();

  private int slotId = 0x3;
  private String libraryPath;
  private PKCS11 pkcs11Instance;
  private String signatureAlgo = "RSA";
  private long session = -1;
  private boolean isLoginDone = false;
  private SignatureMechanism.Algorithms algorithm = SignatureMechanism.Algorithms.SHA256;

  public OpenScPkcs11(String libraryPath) throws IOException, PKCS11Exception {
    this.libraryPath = libraryPath;
    initializeMiddleware();
  }


  public void calcSign(byte[] hash, String keyPath, String userOid, String authbits) throws Exception {
    byte[] signature = new byte[512];

    if (this.pkcs11Instance != null) {

      if (this.session < 0) {
        this.openSession();
      }

      if (!this.isLoginDone) {
        this.login("", "");
      }

      Mechanism mechanism = SignatureMechanism.getMechanism(this.algorithm);


      CK_ATTRIBUTE[] pTemplate = new CK_ATTRIBUTE[3];
      pTemplate[0] = new CK_ATTRIBUTE(PKCS11Constants.CKA_CLASS, PKCS11Constants.CKO_PRIVATE_KEY);
      pTemplate[1] = new CK_ATTRIBUTE(PKCS11Constants.CKA_VENDOR_DEFINED + 1, keyPath);
      pTemplate[2] = new CK_ATTRIBUTE(PKCS11Constants.CKA_VENDOR_DEFINED + 2, authbits);

      // define the attibutes for certificate
      CK_ATTRIBUTE[] certAttribute = new CK_ATTRIBUTE[1];
      certAttribute[0] = new CK_ATTRIBUTE(PKCS11Constants.CKA_VENDOR_DEFINED + 3);

      long[] c_FindObjects = null;

      this.pkcs11Instance.C_FindObjectsInit(this.session, pTemplate);
      c_FindObjects = this.pkcs11Instance.C_FindObjects(this.session, 32);
      this.pkcs11Instance.C_FindObjectsFinal(this.session);

      CK_MECHANISM pMechanism = null;

      // RSA Algorithm
      String signatureAlgorithmType = this.getSignatureAlgorithmType();

      if (signatureAlgorithmType.equalsIgnoreCase("RSA")) {
        pMechanism = new CK_MECHANISM(mechanism.getId());

        CK_RSA_PKCS_PSS_PARAMS ck_RSA_PKCS_PSS_PARAMS =
            new CK_RSA_PKCS_PSS_PARAMS(this.algorithm.name(), "MGF1", this.algorithm.name(), (int) mechanism.getsLen());
        pMechanism.pParameter = ck_RSA_PKCS_PSS_PARAMS;
      }
      else if (signatureAlgorithmType.equalsIgnoreCase("ECDSA")) { // ECDSA Algorithm
        pMechanism = new CK_MECHANISM(PKCS11Constants.CKM_ECDSA);
      }
      else {
        throw new Exception("Signature algorithm " + signatureAlgorithmType + " is not supported");
      }

      if ((c_FindObjects != null) && (c_FindObjects.length > 0)) {
        long c_FindObjectFound = c_FindObjects[0];
        boolean objFound = false;

        for (long c_FindObject : c_FindObjects) {
          this.pkcs11Instance.C_GetAttributeValue(this.session, c_FindObject, certAttribute);


          // Binary certificate as byte array
          byte[] certificateBytes = certAttribute[0].getByteArray();

          if ((userOid != null) && !userOid.isEmpty()) {
            // Match certificate with userOid, if matches (Certificate parser used)
            if (parseOidInfo(certificateBytes, userOid)) {
              c_FindObjectFound = c_FindObject;
              objFound = true;
              break;
            }
          }
        }

        if (objFound) {
          System.out.println("Signature found for given OID configuration.");
        }
        else {
          this.pkcs11Instance.C_GetAttributeValue(this.session, c_FindObjectFound, certAttribute);
          CertificateParser certificateParser = new CertificateParser(certAttribute[0].getByteArray());
        }
          this.pkcs11Instance.C_SignInit(this.session, pMechanism, c_FindObjectFound);
          this.pkcs11Instance.C_SignUpdate(this.session, 0, hash, 0, (int) mechanism.getsLen());
          signature = this.pkcs11Instance.C_SignFinal(this.session, mechanism.getSignFinalArgument());
        
      }
      else {
        String errorMessage = "Unable to find keys.";
        throw new Exception(errorMessage);
      }
    }
    else {
      throw new Exception("Initialize middleware first.");
    }
  }


  /**
   * @return
   */
  private String getSignatureAlgorithmType() {
    return this.signatureAlgo;
  }


  public void login(String userName, String password) throws Exception {
    if (this.pkcs11Instance != null) {
      openSession();
      String pwd = password;
      if (pwd == null || pwd.trim().isEmpty()) {
        Scanner sc = new Scanner(System.in);
        pwd = sc.next();
        sc.close();
      }
      this.pkcs11Instance.C_Login(this.session, PKCS11Constants.CKU_USER, pwd.toCharArray());
      this.isLoginDone = true;
    }
    else {
      throw new Exception("Initialize middleware first.");
    }
  }

  public void logout() throws PKCS11Exception {
    if (this.pkcs11Instance != null) {
      this.pkcs11Instance.C_Logout(this.session);
    }
  }


  public void openSession() throws Exception {
    if (this.pkcs11Instance != null) {
      if (this.session < 0) {

        long[] c_GetSlotList = this.pkcs11Instance.C_GetSlotList(true);

        if ((c_GetSlotList != null) && (c_GetSlotList.length > 0)) {
          for (long element : c_GetSlotList) {
            if (element == this.slotId) {
              this.session =
                  this.pkcs11Instance.C_OpenSession(this.slotId, PKCS11Constants.CKF_SERIAL_SESSION, null, null);
              break;
            }
          }
        }
      }
    }
    else {
      throw new Exception("Initialize middleware first.");
    }
  }

  public void closeSession() throws PKCS11Exception {
    if ((this.pkcs11Instance != null) && (this.session >= 0)) {
      this.pkcs11Instance.C_CloseSession(this.session);
      this.session = -1;
    }
  }

  public void initializeMiddleware(String libraryPath1) throws IOException, PKCS11Exception {
    this.pkcs11Instance = PKCS11.getInstance(libraryPath1, "C_GetFunctionList", null, false);
    this.libraryPath = libraryPath1;
  }

  public void initializeMiddleware() throws IOException, PKCS11Exception {
    this.pkcs11Instance = PKCS11.getInstance(this.libraryPath, "C_GetFunctionList", null, false);
  }

  public static String getString(final byte[] data) {
    if ((data != null) && (data.length > 0)) {
      return hexBinaryAdapter.marshal(data);
    }
    return null;
  }
}

class SignatureMechanism {

  public static enum Algorithms {

                                 SHA256(0x250),
                                 /**
                                  * Hash calculation Algorithm
                                  */
                                 RIPEMD160(0x240),
                                 /**
                                  * Hash calculation Algorithm
                                  */
                                 RIPEMD160_1(0x0),

                                 /**
                                  * Secure Hash Algorithm 512 for hash Calculation
                                  */
                                 SHA512(0x251);

    private int value = 0;

    private Algorithms(final int algorithmValue) {
      this.value = algorithmValue;
    }

    /**
     * @return the hash value
     */
    public int getValue() {
      return this.value;
    }
  }

  /**
   * @param algorithm : Algorithm used for hash calculation
   * @return signature mechanism
   */
  public static Mechanism getMechanism(final Algorithms algorithm) {

    Mechanism mechanism = new Mechanism();
    if (algorithm == Algorithms.SHA256) {
      mechanism.setHashAlg(PKCS11Constants.CKM_SHA256);
      mechanism.setMgf(PKCS11Constants.CKG_MGF1_SHA1 + 1);
      mechanism.setsLen(32);
      mechanism.setSignFinalArgument(512);
    } // TODO Verify with ETAS Middleware team
    else if (algorithm == Algorithms.SHA512) {
      mechanism.setHashAlg(PKCS11Constants.CKM_SHA512);
      mechanism.setMgf(PKCS11Constants.CKG_MGF1_SHA1 + 1);
      mechanism.setsLen(64);
      mechanism.setSignFinalArgument(1024);
    }
    else if (algorithm == Algorithms.RIPEMD160) {
      mechanism.setHashAlg(PKCS11Constants.CKM_RIPEMD160);
      mechanism.setMgf(0x80000001); // hard coded becuase it is defined by escrypt and not present in PKCS11
      mechanism.setsLen(20);
    }
    else if (algorithm == Algorithms.RIPEMD160_1) {
      mechanism.setId(PKCS11Constants.CKM_RIPEMD160_RSA_PKCS);
    }

    return mechanism;
  }

}
class Mechanism{

  private long hashAlg;
  private long mgf;
  private long sLen;
  private long ulMaxObjectCount = 32;
  private int signFinalArgument = 128;
  private long id = PKCS11Constants.CKM_RSA_PKCS_PSS;


  /**
   * @return the hashAlg
   */
  public long getHashAlg() {
    return hashAlg;
  }


  /**
   * @param hashAlg the hashAlg to set
   */
  public void setHashAlg(long hashAlg) {
    this.hashAlg = hashAlg;
  }

  /**
   * @return the mgf
   */
  public long getMgf() {
    return mgf;
  }


  /**
   * @param mgf the mgf to set
   */
  public void setMgf(long mgf) {
    this.mgf = mgf;
  }

  /**
   * @return the sLen
   */
  public long getsLen() {
    return sLen;
  }

  /**
   * @param sLen the sLen to set
   */
  public void setsLen(long sLen) {
    this.sLen = sLen;
  }

  /**
   * @return the ulMaxObjectCount
   */
  public long getUlMaxObjectCount() {
    return ulMaxObjectCount;
  }

  /**
   * @param ulMaxObjectCount the ulMaxObjectCount to set
   */
  public void setUlMaxObjectCount(long ulMaxObjectCount) {
    this.ulMaxObjectCount = ulMaxObjectCount;
  }

  /**
   * @return the signFinalArgument
   */
  public int getSignFinalArgument() {
    return signFinalArgument;
  }

  /**
   * @param signFinalArgument the signFinalArgument to set
   */
  public void setSignFinalArgument(int signFinalArgument) {
    this.signFinalArgument = signFinalArgument;
  }

  /**
   * @return the id
   */
  public long getId() {
    return id;
  }

  /**
   * @param id the id to set
   */
  public void setId(long id) {
    this.id = id;
  }

}

'''

Thank You!

Java 9 up no longer puts the standard-library in jar files (rt.jar, etc) but they are still there. Your problem is probably (though I didn't read through all the code and certainly can't test without your hardware) that Java 9 up also introduces modules . Much as in Java forever your code can reference another class or interface (or static member therof) by its simple name only if you import it or it is in the same package as your code (if any) or the special package java.lang , now in 9 up you can only access a package if it is in the same module as your code or the special java.base module or your module and/or the other module explicitly allow the access; the explicit specification closet to older Java is an 'open'.

Thus the quick solution is to add to your java command (explicitly or via _JAVA_OPTS or equivalent) --add-opens jdk.crypto.cryptoki/sun.security.pkcs11.*=ALL-UNNAMED -- this should produce substantially the same result as running on 8 (or lower).

The official solution is to put your code in a module -- either by itself or with other related code -- that requires the (or each) needed module, given it exports the needed parts, which I haven't checked for yours. However, since your code apparently isn't even in a package yet, putting it in a module will be some work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM