From d65feae0835f0ab6cee2a7935bd92c86f2ab6bff Mon Sep 17 00:00:00 2001 From: Valery Qwertovsky <5704043+Qwertovsky@users.noreply.github.com> Date: Fri, 5 Jan 2024 23:04:18 +0300 Subject: [PATCH] Add cert-file option --- README.md | 10 +++++- .../com/qwertovsky/cert_gost/CliOptions.java | 1 + .../java/com/qwertovsky/cert_gost/Main.java | 20 +++++++++-- .../qwertovsky/cert_gost/store/PkcsStore.java | 35 ++++++++++++++----- 4 files changed, 54 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 4f42c46..b37f0c7 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,15 @@ java -jar gost_sign.jar -i file.pdf --pkcs-id test --pkcs-library /usr/lib/librt ``` Options: + option key | argument | default | description ---|---|---|--- -d,--date | text | now | Date of sign (use ISO 8601 format) --detached | | | Don't include input document to SIG file -h,--help | | | Print help -i,--input | file | error | File to sign ---pkcs-id | text | | Certificate id on token. Private and public keys should share this id. Id is ASCII encoded (74657374 = test) +--pkcs-id | text | | Certificate id on token. Private and public keys should share this id. pkcs-tool uses ASCII encoded version (74657374 = test). +--cert-file | text | | Insurer certificate on disk (DER format). The program looks for keys according this certificate --pkcs-library | file | | Path to PKCS library Write certificate on token: @@ -25,6 +27,12 @@ Write certificate on token: pkcs11-tool --module /usr/lib/librtpkcs11ecp.so --type cert --login --write-object test.pem --id 74657374 ``` +Make DER certificate from PEM: + +``` +openssl x509 -in certificate.pem -out certificate.der -outform DER +``` + ## Add signature to PDF document ``` java -jar gost_sign.jar -i file.pdf --pkcs-id test --pkcs-library /usr/lib/librtpkcs11ecp.so --pdf --pdf-visual --pdf-position-x 100 --pdf-position-y 100 diff --git a/src/main/java/com/qwertovsky/cert_gost/CliOptions.java b/src/main/java/com/qwertovsky/cert_gost/CliOptions.java index 2fd9e51..39396e8 100644 --- a/src/main/java/com/qwertovsky/cert_gost/CliOptions.java +++ b/src/main/java/com/qwertovsky/cert_gost/CliOptions.java @@ -6,6 +6,7 @@ public interface CliOptions { static final String INPUT = "input"; static final String HELP = "help"; static final String PKCS_ID = "pkcs-id"; + static final String CERT_FILE = "cert-file"; static final String PKCS_LIBRARY = "pkcs-library"; static final String PFX_FILE = "pfx-file"; static final String PFX_ALIAS = "pfx-alias"; diff --git a/src/main/java/com/qwertovsky/cert_gost/Main.java b/src/main/java/com/qwertovsky/cert_gost/Main.java index 8a639a9..138c589 100644 --- a/src/main/java/com/qwertovsky/cert_gost/Main.java +++ b/src/main/java/com/qwertovsky/cert_gost/Main.java @@ -92,7 +92,7 @@ public static void main(String[] args) throws Exception { StoreType storeType = null; if (commandLine.hasOption(CliOptions.PFX_FILE)) { storeType = StoreType.PFX; - } else if (commandLine.hasOption(CliOptions.PKCS_ID)) { + } else if (commandLine.hasOption(CliOptions.PKCS_ID) || commandLine.hasOption(CliOptions.CERT_FILE)) { storeType = StoreType.PKCS11; } if (storeType == null) { @@ -106,7 +106,6 @@ public static void main(String[] args) throws Exception { switch (storeType) { case PKCS11: { - String certId = commandLine.getOptionValue(CliOptions.PKCS_ID); String libraryPath = commandLine.getOptionValue(CliOptions.PKCS_LIBRARY); if (!commandLine.hasOption(CliOptions.PKCS_LIBRARY)) { @@ -119,7 +118,14 @@ public static void main(String[] args) throws Exception { pkcs11 = Native.load(libraryPath, RtPkcs11.class); pkcsSession = new NativeLong(Pkcs11Constants.CK_INVALID_HANDLE); Pkcs11Operations.initializePkcs11AndLoginToFirstToken(pkcs11, pkcsSession, String.valueOf(pinChars).getBytes()); - store = new PkcsStore(pkcs11, pkcsSession, certId); + String certId = commandLine.getOptionValue(CliOptions.PKCS_ID); + if (certId != null) { + store = new PkcsStore(pkcs11, pkcsSession, certId); + } else { + String certPath = commandLine.getOptionValue(CliOptions.CERT_FILE); + store = new PkcsStore(pkcs11, pkcsSession, new File(certPath)); + } + break; } case PFX: { @@ -232,6 +238,14 @@ private static Options createOptions() { .build(); cliOptions.addOption(pkcsIdOption); + Option certFileOption = Option.builder() + .longOpt(CliOptions.CERT_FILE) + .argName("cert file on disk") + .desc("Insurer certificate DER file. Private and public keys for this certificate should be on token") + .hasArg(true) + .build(); + cliOptions.addOption(certFileOption); + Option pkcsLibraryOption = Option.builder() .longOpt(CliOptions.PKCS_LIBRARY) .argName("/usr/lib/library_path.so") diff --git a/src/main/java/com/qwertovsky/cert_gost/store/PkcsStore.java b/src/main/java/com/qwertovsky/cert_gost/store/PkcsStore.java index 80ff9b9..7673647 100644 --- a/src/main/java/com/qwertovsky/cert_gost/store/PkcsStore.java +++ b/src/main/java/com/qwertovsky/cert_gost/store/PkcsStore.java @@ -1,5 +1,7 @@ package com.qwertovsky.cert_gost.store; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -27,17 +29,30 @@ public class PkcsStore implements GostStore { private final Pkcs11 pkcs11; private final NativeLong session; - private final String certId; - private final NativeLong privateKey; - private final Pkcs11Signer pkcs11Signer; - private final StreamDigestCalculator digestCalculator; + private final X509CertificateHolder certificateHolder; + private NativeLong privateKey; + private Pkcs11Signer pkcs11Signer; + private StreamDigestCalculator digestCalculator; public PkcsStore(Pkcs11 pkcs11, NativeLong session, String certId) throws Exception { this.pkcs11 = pkcs11; this.session = session; - this.certId = certId; - - X509CertificateHolder certificateHolder = getCertificateHolder(); + + this.certificateHolder = getCertificateHolder(certId); + this.init(pkcs11, session, certificateHolder); + } + + public PkcsStore(Pkcs11 pkcs11, NativeLong session, File certFile) throws Exception { + this.pkcs11 = pkcs11; + this.session = session; + + try(FileInputStream fis = new FileInputStream(certFile)) { + this.certificateHolder = new X509CertificateHolder(fis.readAllBytes()); + } + this.init(pkcs11, session, certificateHolder); + } + + private final void init(Pkcs11 pkcs11, NativeLong session, X509CertificateHolder certificateHolder) throws Exception { privateKey = Pkcs11Operations.findPrivateKeyByCertificateValue(pkcs11, session, certificateHolder.getEncoded()); AlgorithmIdentifier algorithm = certificateHolder.getSignatureAlgorithm(); SignAlgorithm signAlgorithm = SignAlgorithm.byAlgorithm(algorithm); @@ -73,6 +88,10 @@ public Digest getDigest(AlgorithmIdentifier digestAlg) throws Exception { @Override public X509CertificateHolder getCertificateHolder() throws Exception { + return certificateHolder; + } + + private X509CertificateHolder getCertificateHolder(String certId) throws Exception { System.out.println("Finding signer certificate"); final CK_ATTRIBUTE[] certificateTemplate; certificateTemplate = (CK_ATTRIBUTE[]) (new CK_ATTRIBUTE()).toArray(3); @@ -80,7 +99,7 @@ public X509CertificateHolder getCertificateHolder() throws Exception { certificateTemplate[1].setAttr(Pkcs11Constants.CKA_ID, certId.getBytes()); // Certificate ID certificateTemplate[2].setAttr(Pkcs11Constants.CKA_CERTIFICATE_TYPE, Pkcs11Constants.CKC_X_509); -// certificateTemplate[3].setAttr(Pkcs11Constants.CKA_CERTIFICATE_CATEGORY, Constants.CK_CERTIFICATE_CATEGORY_TOKEN_USER); +// certificateTemplate[3].setAttr(Pkcs11Constants.CKA_CERTIFICATE_CATEGORY, Pkcs11Constants.CK_CERTIFICATE_CATEGORY_TOKEN_USER); byte[] signerCertificateValue = Pkcs11Operations.getFirstCertificateValue(pkcs11, session, certificateTemplate); X509CertificateHolder certificateHolder = new X509CertificateHolder(signerCertificateValue); return certificateHolder;