Различная цифровая подпись для одного и того же сообщения, когда веб-сервис Java развернут в разных операционных системах, работающих на одном сервере приложений.

1

Я программирую веб-сервис, который требует цифровой подписи для отправленных и полученных XML-сообщений, которые запрограммированы в JAVA, используя библиотеку bouncycastle для подписывания отправленных сообщений и проверки полученных. Webservice развернут на сервере приложений Weblogic. Я заметил, что то же точное сообщение создает различную подпись, когда веб-сервис развертывается на сервере Windows, чем при развертывании на сервере Linux.

Я попробовал развернуть тот же самый точный код на разных машинах, на которых есть сервер приложений Weblogic, и те, которые работают в Windows, производят одну и ту же подпись, а те, которые работают в Linux, производят другую. Сущность, которая собирается использовать вебсервис, успешно проверила подпись из сообщений, созданных на сервере Windows, но не смогла сделать то же самое для тех, что были созданы на сервере Linux (они используют Windows в качестве операционной системы на своих серверы).

Как закрытый ключ, так и сертификат, используемый при подписании и проверке цифровых подписей, считываются из файлов с помощью FileInputStream. Чтобы убедиться, что проблема не в сертификатах, я попытался прочесть сертификаты из жестко закодированной строки внутри кода, и такая же проблема возникает. Я также написал простой веб-сервис, который проверяет подпись, которую я создаю, те, которые производятся в Windows, успешно проверены на верификаторе, развернутом на окнах, в то время как те, которые производятся на Linux, не являются и наоборот.

Принимая во внимание, что XML-сообщения переформатируются перед процессом подписания (все белые пробелы удаляются, а затем XML переформатируется), есть ли какие-либо предложения или идеи о причине этого, поскольку у меня заканчиваются идеи? Имеет ли это какое-либо отношение к кодировке символов или способу перевода строки и возврата каретки в разные операционные системы?

Благодарю.

Изменение: Вот упрощенный код для процесса подписи, который следует той же логике, что и в моем исходном коде, импорт не добавлен, и обработка исключений в этом коде и большинство функций не были объединены, чтобы упростить, также класс "Утилиты", который используется в процессе форматирования XML, не предоставляется для простоты, но есть описание, написанное рядом с каждым вызовом функции из этого класса, я предоставил этот класс и исходный код, если это необходимо:

public class Initializer {
    private static Signer signer = null; // a reference to the class that will do the signing process
    private static XPathExpression mssgBody;
    private static XPathExpression signatureNodePath;

    static {
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            KeyStore keystore;

            keystore = KeyStore.getInstance("PKCS12", "BC");

            keystore.load(new FileInputStream("/home/path/path/MyKeyPair.p12"), "123".toCharArray()); //loading My keypair to a keystore (only the private key will be used) 

            Key key = keystore.getKey("CertificateAlias", "Password".toCharArray());

            java.security.cert.Certificate cert;
            cert = keystore.getCertificate("CertificateAlias");
            PrivateKey privateKey = (PrivateKey)key;

            signer = new Signer(privateKey, "UTF-16LE", "SHA256withRSA"); //Creating a new Signer object and providing the private key and the message encoding and the signature algorithm
            mssgBody = Utils.compileExpression(null, "//MsgBody", null); //  set the  path for the message body that will be signed
            signatureNodePath = Utils.compileExpression(null, "//Signature", null); // set the path for the signature node where the signature will be put

        } catch (KeyStoreException e) {
        } catch (NoSuchProviderException e) {
        } catch (IOException e) {
        } catch (NoSuchAlgorithmException e) {
        } catch (CertificateException e) {
        } catch (UnrecoverableKeyException e) {
        } catch (XPathExpressionException e) {
        }
    }

    public static String sign(Node request) throws XPathExpressionException, Exception { //the function that will be called by the web service
        String mssgBodyString = Utils.convertNode2String(Utils.extractNodeList(request, mssgBody).item(0)); // Extract the message body and convert it into a String
        mssgBodyString = Utils.getFromattedXML(mssgBodyString); //format the string containing XML data, each new opening or closing tag will be on a new line and each new nested tag will be indented by two spaces
        String signature = signer.sign(mssgBodyString); // Return signature

        return signature;
    }
}

public class Signer{

    private KeyStore signKeyStore;

    private PrivateKey privateKey;

    /** Final Instances **/
    private String MSG_ENCODER;
    private String SIGNATURE_ALG;

    public Signer(PrivateKey prk, String msgEncoder, String signatureAlg) {

        privateKey = prk;
        MSG_ENCODER = msgEncoder; //"UTF-16LE"
        SIGNATURE_ALG = signatureAlg; //"SHA256withRSA"
    }

        /******************* Signing process ************************/

    public String sign(final String msg) throws Exception {  // the function that will be called form the initializer class
        return sign(msg, SIGNATURE_ALG, MSG_ENCODER);
    }


    /**
     * @param msgStr: String for which digital signature will be calculated
     * @param signatureAlg: Name of requested Signature Algorithm
     * @param msgEncoder: Encoder used encoding the input message to bytes array
     * @param sigEncoder: Type of byte encoder  (Hex, Base64, ...)
     *
     * @return strSignature: String Formatted Signature for passed string data, based on passed encoder
     * @throws Exception
     */
    public String sign(final String msgStr, final String signatureAlg, final String msgEncoder) throws Exception {

        byte[] signature;
        String strSignature = "";
        signature = this.sign(msgStr.getBytes(msgEncoder));

        // encode the result signature based on the specified encoder
        strSignature = encode(signature);

        return strSignature;
    }

    public byte[] sign(final byte[] data) throws Exception {

        Signature signer;
        byte[] signature = null;


        try {

            signer = Signature.getInstance(SIGNATURE_ALG, new BouncyCastleProvider());

            signer.initSign(privateKey); // calling initSign from java.security Package
            signer.update(data, 0, data.length);
            signature = signer.sign();

        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("Message Signing failed; ", e);
        }

        return signature;
    }

     /*** encoding / decoding****/

    /**
     * encodes byte array into String using Base64
     * @param bytes
     * @return  encoded String
     */
    private String encode(byte[] bytes) {
        return new String(Base64.encode(bytes));
    }

    /**
     * Decode String using Base64
     * @param str
     * @return
     */
    private byte[] decode(String str) {
        return Base64.decode(str);

    }
}
  • 0
    Пожалуйста, предоставьте образцы файлов.
  • 0
    Я думаю, проблема в том, что вы подписываете строку вместо byte[] . В идеале вы должны использовать некоторый API, который может подписывать XML-узел напрямую, чтобы у вас не было этой проблемы. Я использую openSAML для Java, я думаю, что он может быть использован и для C #.
Показать ещё 3 комментария
Теги:
web-services
digital-signature
operating-system

1 ответ

0
Лучший ответ

Как оказалось, проблема заключалась в том, что новая линия обрабатывается по-разному между Windows и Linux; в Windows новая строка добавляет возврат каретки и линию, в то время как в Linux он добавляет только фид строки.

Код, который я работаю, требует, чтобы XML был отформатирован с новой строкой и двумя пространствами отступа между родительским тегом и его дочерними тегами, я использовал следующий код внутри моего метода "convertNode2String" в классе Utils:

    if (indent){
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
    }

Предыдущий код добавляет 0x0D0A в каждую новую строку в Windows, а в Linux он добавляет 0x0A на каждую новую строку. Это делает очевидным, почему подпись в разных версиях отличается от Windows, чем в Linux.

Поскольку объект, который будет потреблять веб-сервис, использует Windows в качестве своей операционной системы, решение этой проблемы состоит в том, чтобы изменить новую строку в среде Linux с LF только до CRLF при вызове метода, мы можем сделать это, используя java.lang.System.setProperty(String key, String value) для свойства "line.separator", а затем верните его в исходное состояние в конце метода.

Чтобы сделать его неспецифичным для определенной ОС или если значение по умолчанию не только LF, мы должны сохранить текущее значение "line.separator", используя метод "getProperty".

    String defaultSeparator = System.getProperty("line.separator");
    String WindowsSeparator = "\r\n";
    System.setProperty("line.separator", WindowsSeparator);

А после форматирования XML выполняется:

    System.setProperty("line.separator", defaultSeparator);

Надеюсь, кто-то найдет это полезным.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню