[JAVA] Digital certificate this and that

Thing you want to do

Since this is the first implementation of XML signature, I decided to proceed while leaving various notes.

--I want to sign XML in Java --The format of the digital certificate is PKCS12 format

Operation

  1. Import the PKCS12 format (.p12) certificate into the Java keystore (cacert)
  2. Modify each parameter obtained from DB (if necessary)
    cacert path, keystore password, alias, encryption algorithm, digest algorithm, signature algorithm, etc. .. ..

The above operation is required every time the digital certificate is replaced.

What to look for

What is a digital certificate? ??

What is "normalization" of digital certificates? http://www.atmarkit.co.jp/fxml/tanpatsu/16xmlsecurity/xmlsecurity02.html

RSA key and certificate file format https://qiita.com/kunichiko/items/12cbccaadcbf41c72735

Extract private key, certificate, intermediate CA from PKCS # 12 file https://qiita.com/cs_sonar/items/f2753ffdebb1c67d5302

How to create a self-signed certificate https://qiita.com/akito1986/items/8eb41f5a43bb9421ae79

How to do XML signature in Java? ??

XML signature using Java's XML digital signature API http://qiita.com/KevinFQ/items/4e2484a659b618530e72

keytool command list http://itref.fc2web.com/java/keytool.html

[Java] Add / delete certificates from the keystore in Java https://blogs.yahoo.co.jp/dk521123/37097725.html

Various notes

Convert from server certificate pem to crt

$ openssl x509 -outform der in server-crt.pem -out server-crt.crt

Server certificate pem ⇒ Convert to p12

$ openssl pkcs12 -export -out server-crt.p12 -in server-crt.pem -inkey server-privatekey.pem -passin pass:root -passout pass:root

Server certificate p12 ⇒ Converted to crt

$ openssl pkcs12 -in server-crt.p12 -clcerts -nokeys -out server-crt.crt

Server certificate p12 ⇒ Convert to cer

$ openssl pkcs12 -in server-crt.p12 -des -out server-crt.cer

keytool (key and certificate management tool)

installation of keytool

https://jp.globalsign.com/support/faq/331.html

Create keystore file

Command reference: https://docs.oracle.com/javase/jp/6/technotes/tools/windows/keytool.html

It seems that Java will not be able to update automatically if cacerts changes. Backup required. http://d.hatena.ne.jp/uunfo/20110813/1313242092

keytool -genkeypair -keysize 2048 -keyalg RSA -sigalg SHA256withRSA -alias testcrt -keystore "C:\Program Files\Java\jreX.X.X_XXX\lib\security\cacerts" -storepass changeit

** Added 2017/10/16 ** Create a keystore file anywhere (When the common name is test1, the organizational unit is test2, the organization is test3, and the two-letter country code is JP)

$ keytool -genkey -dname "cn=test1, ou=test2, o=test3, c=JP" -keystore (keystore filename.keystore) -alias (name that identifies the certificate / key in the KeyStore)-keypass (certificate / key password * 6 characters or more)-storepass (keystore password)-keyalg RSA -keysize 2048 -validity 10000

Reference (per key pair generation) https://docs.oracle.com/javase/jp/8/docs/technotes/tools/unix/keytool.html

Add server certificate to keystore

I thought it would be a good idea to leave the certificate exposed as an external resource, so I considered importing the certificate into the keystore.

Import cer file into keystore

When I try to import a crt file with the following command, I get an error saying "keytool error: java.lang.Exception: Input is not an X.509 certificate". Import with cer.

$ keytool -import -alias testkey -keystore "C:\Program Files\Java\jreX.X.X_XXX\lib\security\cacerts" -file server-crt.cer

Import p12 file into keystore http://blogger.fastriver.net/2014/10/keytool.html

If you do not specify an alias (distinguishing name), it will be imported with the name "1" by default. If you want to add an alias, add an option such as "-srcalias 1 -destalias (arbitrary alias name)".

$ keytool -importkeystore -keystore "C:\Program Files\Java\jreX.X.X_XXX\lib\security\cacerts" -srckeystore server-crt.p12 -srcstoretype PKCS12 -srcalias 1 -destalias testcrt -srcstorepass root -deststorepass changeit

Check the imported certificate

$ keytool -list -v -alias testcrt -keystore "C:\Program Files\Java\jreX.X.X_XXX\lib\security\cacerts" -keypass root -storepass changeit

Delete the imported certificate from the keystore

$ keytool -delete -alias testcrt -keystore "C:\Program Files\Java\jreX.X.X_XXX\lib\security\cacerts" -keypass root -storepass changeit

Change the alias name (when changing from 11111111 to 22222222)

$ keytool -changealias -srcalias 11111111 -destalias 22222222 -keystore "C:\Program Files\Java\jreX.X.X_XXX\lib\security\cacerts" -keypass root -storepass changeit

Extend the certificate expiration date (when setting after 2000 days)

$ keytool -selfcert -alias testcrt -validity 2000 -keystore "C:\Program Files\Java\jreX.X.X_XXX\lib\security\cacerts" -keypass root -storepass changeit

Completed Java code

createSignedXml.java


    public String createSignedXml(String fileName)
            throws Exception {

        //Create Document instance
        DocumentBuilder documentBuilder = null;
        documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();

        Document document = documentBuilder.newDocument();

        //DOM generation of XML document (omitted)

        //XML signature creation
        //p12 file path
        File certDir = "XXXXXX";
        //Certificate file list under the certificate directory (assuming that only one file exists)
        File[] certP12List = certDir.listFiles((FileFilter) new SuffixFileFilter(".p12"));
        //Certificate file
        File certP12File = null;
        //Get the path of the keystore file
        String keyStoreFilePath = "/usr/java/jdkX.X.X_XXX/jre/lib/security/testkeystore";
        //Get keystore file
        File keyStoreFile = new File(keyStoreFilePath);
        //Keystore password
        String keyStorePass = "root";
        //Certificate password
        String certificatePass = "changeit";

        //Error if multiple p12 files are placed
        if (certP12List.length == 1) {
            certP12File = certP12List[0];
        } else {
            throw new IOException();
        }

        try {
            //Get the keystore
            KeyStore keyStore = getKeyStore(keyStoreFile, keyStorePass);

            //Distinguished name: Obtain a certificate matching XXX from the keystore
            X509Certificate x509cert = getCertificate(keyStore, XXX);

            //Certificate import judgment flag
            boolean importFlg = false;

            //For expiration date check
            // 0:Imported certificate is within expiration date, 1:Imported certificate has expired,
            // 2:Certificate of external resource has expired, 3:Both have expired, 4:Import error
            int checkResult = 0;

            if (x509cert != null) {
                //Expiration check for certificates located on external resources and imported certificates
                checkResult = checkCertificateExpired(x509cert, XXX, keyStoreFile, certP12File, keyStorePass, certificatePass);

                //Imported certificate has expired
                if (checkResult == 1) {
                    //Distinguished name: XXX certificate deletion
                    deleteProcessExec(XXX, keyStoreFile, certificatePass, keyStorePass);

                    importFlg = true;
                }
            } else {
                importFlg = true;
            }

            //If the certificate imported into the keystore has expired
            //Re-import new certificate
            if (importFlg) {
                //Perform certificate import
                importProcessExec(keyStoreFile, XXX, certP12File, certificatePass, keyStorePass);

                //Reacquire the keystore
                keyStore = getKeyStore(keyStoreFile, keyStorePass);

                //Distinguished name: Re-obtain a certificate matching XXX from the keystore
                x509cert = getCertificate(keyStore, XXX);

                //Expiration date check
                checkResult = checkCertificateExpired(x509cert, keyStoreFile, certP12File, keyStorePass, certificatePass);
            }

            //Both the certificate placed in the external resource and the imported certificate have expired.
            //External resource certificate import error
            if (checkResult >= 3) {
                //Distinguished name: XXX certificate deletion
                deleteProcessExec(XXX, keyStoreFile, certificatePass, keyStorePass);

                throw new CertificateException();
            }

            //Get the private key
            RSAPrivateKey privateKey = (RSAPrivateKey) keyStore.getKey(XXX, certificatePassArray);

            //Creating a signature context
            DOMSignContext dsc = new DOMSignContext(privateKey, document.getDocumentElement());

            XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

            //Creating a reference element
            Reference ref = fac.newReference
                    ("#XXXXXX", fac.newDigestMethod(DigestMethod.SHA256, null),
                            Collections.singletonList
                                    (fac.newTransform(Transform.ENVELOPED,
                                            (TransformParameterSpec) null)), null, null);

            //Creating signature information
            SignedInfo si = fac.newSignedInfo
                    (fac.newCanonicalizationMethod
                            (CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
                                    (C14NMethodParameterSpec) null),
                            fac.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", null),
                            Collections.singletonList(ref));

            KeyInfoFactory kif = fac.getKeyInfoFactory();
            X509Data x509Data = kif.newX509Data(Collections.singletonList(x509cert));
            KeyInfo ki = kif.newKeyInfo(Collections.singletonList(x509Data));

            //Sign.
            XMLSignature signature = fac.newXMLSignature(si, ki);
            signature.sign(dsc);

        } catch (XMLSignatureException e) {
            throw new XMLSignatureException("An error occurred during XML signature generation or validation process", e);
        } catch (MarshalException e) {
            throw new MarshalException(e);
        } catch (InvalidAlgorithmParameterException e) {
            throw new InvalidAlgorithmParameterException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new NoSuchAlgorithmException(e);
        } catch (KeyStoreException e) {
            throw new KeyStoreException(e);
        } catch (CertificateException e) {
            throw new CertificateException(e);
        } catch (UnrecoverableKeyException e) {
            throw new UnrecoverableKeyException();
        }

        return this.write(document);
    }

    /**
     *Get the keystore
     *
     * @param keyStoreFile Keystore file
     * @param keyStorePass Keystore password
     * @return keyStore keystore object
     */
    private KeyStore getKeyStore(File keyStoreFile, String keyStorePass) {
        KeyStore keyStore = null;

        try (FileInputStream keyStoreStream = new FileInputStream(keyStoreFile)) {
            //Keystore type:JKS(For PKCS12, take the argument"PKCS12"To fix)
            keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(keyStoreStream, keyStorePass.toCharArray());
        } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
            //TODO auto-generated catch block
            e.printStackTrace();
        }

        return keyStore;
    }

    /**
     *Obtain a certificate that matches the distinguished name from the keystore
     *
     * @param keyStore keystore object
     * @param alias Distinguished name
     * @return certificate certificate object
     */
    private X509Certificate getCertificate(KeyStore keyStore, String alias){
        X509Certificate certificate = null;
        try {
            certificate = (X509Certificate) keyStore.getCertificate(alias);
        } catch (KeyStoreException e) {
            //TODO auto-generated catch block
            e.printStackTrace();
        }
        return certificate;
    }

    /**
     *Certificate expiration check
     *
     * @param x509cert X509 certificate object
     * @param keyStoreFile Keystore file
     * @param certFile Certificate file
     * @param keyStorePass Keystore password
     * @param certificatePass Certificate password
     * @param srcAlias Temporary distinguished name immediately after import
     * @return 0:Imported certificate is within expiration date, 1:Imported certificate has expired,
     * 2:Certificate of external resource has expired, 3:Both have expired, 4:External resource certificate import error
     */
    private int checkCertificateExpired(X509Certificate x509cert,
            File keyStoreFile, File certFile, String keyStorePass, String certificatePass,
            String srcAlias) {
        //Processing result
        int result = 0;
        //Expiration flag for imported certificate
        // true:Expired, false:Within the expiration date
        boolean importExpiredFlg = false;
        //Expiration flag for certificates placed on external resources
        boolean extExpiredFlg = false;
        //Get the expiration date of the certificate imported into the keystore
        Date importExpiredDate = x509cert.getNotAfter();
        //Current date and time
        Date now = new Date();

        /**************************Expiration date check of imported certificate START**************************/

        //Determine if the imported certificate has expired
        if (now.compareTo(importExpiredDate) > 0) {
            //If it has expired
            importExpiredFlg = true;
            result = 1;
        } else {
            //Since it is within the expiration date, no further checks are required
            return 0;
        }
        /***************************Expiration date check of imported certificate END***************************/

        /***********************Expiration check for certificates placed on external resources START**********************/

        try {
            //Temporary Distinguished Name
            String destAlias = System.currentTimeMillis();

            //Import command execution
            importProcessExec(keyStoreFile, destAlias, certFile, certificatePass, keyStorePass);

            //Get the keystore
            KeyStore tmpKeyStore = getKeyStore(keyStoreFile, keyStorePass);
            //Distinguished name: Obtain a certificate that matches destAlias from the keystore
            X509Certificate tmpCert = getCertificate(tmpKeyStore, destAlias);

            //Get the expiration date of the imported certificate
            Date extExpiredDate = null;
            if (tmpCert != null) {
                extExpiredDate = tmpCert.getNotAfter();
            } else {
                //Import error
                return 4;
            }

            //Compare the current time with the certificate expiration date of the external resource
            if (now.compareTo(extExpiredDate) > 0) {
                //If it has expired
                extExpiredFlg = true;
                result = 2;
            }

            //Execute command to delete temporarily imported certificate
            deleteProcessExec(destAlias, keyStoreFile, certificatePass, keyStorePass);

        } catch(InterruptedException e) {
            //TODO auto-generated catch block
            e.printStackTrace();
        }
        /**********************Expiration check END for certificates placed on external resources*********************/


        //Imported certificates and certificates placed on external resources
        //If both have expired, result:3
        if (importExpiredFlg && extExpiredFlg) {
            result = 3;
        }

        return result;

    }

    /**
     *Execute the certificate deletion command.
     *
     * @param alias Distinguished name
     * @param keyStoreFile Keystore file
     * @param certificatePass Certificate password
     * @param keyStorePass Keystore password
     * @return true:Successful completion, false:Abnormal termination
     * @throws InterruptedException
     */
    private boolean deleteProcessExec(String alias, File keyStoreFile, String certificatePass,
            String keyStorePass)
            throws InterruptedException {
        //Get the command to delete the certificate imported into the keystore
        List<String> command = getDeleteCommand(alias, keyStoreFile, certificatePass, keyStorePass);
        //Delete command execution
        return processExec(command);
    }

    /**
     *Get the certificate deletion command.
     *
     * @param alias Distinguished name
     * @param keyStoreFile Keystore file
     * @param certificatePass Certificate password
     * @param keyStorePass Keystore password
     * @return resultList delete command
     */
    private List<String> getDeleteCommand(String alias, File keyStoreFile, String certificatePass,
            String keyStorePass) {
        //Command to delete the certificate imported into the keystore
        String command = "keytool -delete -alias %alias% -keystore %keyStoreFilePath% -keypass %certificatePass% -storepass %keyStorePass%";

        //Split the command into a string array
        String[] commandList = command.split(" ");

        List<String> resultList = new ArrayList<String>();
        for (String cmd : commandList) {
            switch (cmd) {
            case "%alias%":
                cmd = cmd.replace("%alias%", alias);
                break;
            case "%keyStoreFilePath%":
                cmd = cmd.replace("%keyStoreFilePath%", keyStoreFile.getPath());
                break;
            case "%certificatePass%":
                cmd = cmd.replace("%certificatePass%", certificatePass);
                break;
            case "%keyStorePass%":
                cmd = cmd.replace("%keyStorePass%", keyStorePass);
                break;
            }
            resultList.add(cmd);
        }

        return resultList;
    }

    /**
     *Execute the certificate import command.
     *
     * @param certFilePath Certificate file
     * @param destAlias Distinguished name
     * @param keyStoreFilePath Keystore file
     * @param certificatePass Certificate password
     * @param keyStorePass Keystore password
     * @return true:Successful completion, false:Abnormal termination
     * @throws InterruptedException
     */
    private boolean importProcessExec(File keyStoreFile, String destAlias, File certFile,
            String certificatePass,
            String keyStorePass) throws InterruptedException {
        //Command to import certificate
        List<String> command = getImportCommand(keyStoreFile, destAlias, certFile, certificatePass,
                keyStorePass);
        //Import command execution
        return processExec(command);
    }

    /**
     *Get the certificate import command.
     *
     * @param certFilePath Certificate file
     * @param destAlias Distinguished name
     * @param keyStoreFilePath Keystore file
     * @param certificatePass Certificate password
     * @param keyStorePass Keystore password
     * @return resultList import command
     */
    private List<String> getImportCommand(File keyStoreFile, String destAlias, File certFile,
            String certificatePass, String keyStorePass) {
        //Command to import certificate
        String command = "keytool -importkeystore -keystore %keyStoreFilePath% -srckeystore %certFilePath% -srcstoretype PKCS12 -srcalias 1 -destalias %destalias% -srcstorepass %certificatePass% -deststorepass %keyStorePass%";

        //Split the command into a string array
        String[] commandList = command.split(" ");

        List<String> resultList = new ArrayList<String>();

        for (String cmd : commandList) {
            switch (cmd) {
            case "%keyStoreFilePath%":
                cmd = cmd.replace("%keyStoreFilePath%", keyStoreFile.getPath());
                break;
            case "%certFilePath%":
                cmd = cmd.replace("%certFilePath%", certFile.getPath());
                break;
            case "%destalias%":
                cmd = cmd.replace("%destalias%", destAlias);
                break;
            case "%certificatePass%":
                cmd = cmd.replace("%certificatePass%", certificatePass);
                break;
            case "%keyStorePass%":
                cmd = cmd.replace("%keyStorePass%", keyStorePass);
                break;
            }
            resultList.add(cmd);
        }

        return resultList;
    }

    /**
     *Run an external process.
     *
     * @param command Command contents
     * @return true:Successful completion, false:Abnormal termination
     */
    private boolean processExec(List<String> command) {
        //Processing result
        boolean result = false;

        try {
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            Process Process = processBuilder.start();

            //Wait until the process ends normally
            if (Process.waitFor() == 0) {
                result = true;
                log.info("Process Success: " + command.toString());
            } else {
                log.warn("Process Failed: " + command.toString());
            }

            //Standard output
            String strInput;
            BufferedReader ipbr = new BufferedReader(new InputStreamReader(Process.getInputStream()));
            while((strInput = ipbr.readLine()) != null) {
                log.info(strInput);
            }
            ipbr.close();

            //Error output
            String strErr;
            BufferedReader erbr = new BufferedReader(new InputStreamReader(Process.getErrorStream()));
            while((strErr = erbr.readLine()) != null) {
                log.info(strErr);
            }
            erbr.close();

            //InputStream in the background after using ProcessBuilder, OutputStream,ErrorStream is opened.
            //Close all streams to avoid running out of resources.
            Process.getInputStream().close();
            Process.getOutputStream().close();
            Process.getErrorStream().close();
        } catch (InterruptedException | IOException e) {
            //TODO auto-generated catch block
            e.printStackTrace();
        }

        return result;
    }

Recommended Posts

Digital certificate this and that
Docker settings and this and that
[Rails] strftime this and that
Base64 encoder this and that
This and that of the JDK
This and that about Base64 (Java)
This and that of exclusive control
This and that of Swift corner Radius
This and that of conditional branching of rails development
This and that for editing ini in Java. : inieditor-java
I tried to verify this and that of Spring @ Transactional
String operations this and that-Java edition-
This and that of the implementation of date judgment within the period in Java