Создайте PKCS#10 CSR и печать HMAC

Я застрял в правильном создании запроса на подпись сертификата PKCS # 10 и печати HMAC. Мне нужно их создать и отправить на удаленный сервис, который выдаст мне сертификат в формате PKCS#7. Точнее:

Create a key pair for certificate and generate PKCS#10 request 
(private key is never sent to the remote service)   
 use: key length 1024bit, SHA-1 algorithm, DER –encoded  
 Subject info: CN=name, serialNumber=userID, C=country (as above)   
Create HMAC seal  
 use DER coded PKCS#10 above as input  
 SMS-activation code as the key (10-digits)   

Я оборачиваю результат в SoapMessage и отправляю на удаленную службу и получаю ответ. Ответ — ошибка, потому что либо CSR, либо HMAC были сгенерированы неправильно. Удаленная служба не отправляет более конкретное сообщение об ошибке, но, как я уже сказал, ошибка связана с моим неправильно сгенерированным CSR или HMAC. Тема и ключ HMAC являются примерными значениями, предоставленными удаленной службой, поэтому проблема не может быть из-за них.

Вот код, как я его реализовал

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.SignatureException;

import javax.security.auth.x500.X500Principal;

import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

public class CustomerCert {

    private final KeyPairGenerator keyGen;
    private final KeyPair keypair;
    private final PublicKey publicKey;
    private final PrivateKey privateKey;
    private final byte[] pkcs10;
    private HMac hmac;
    private byte[] hmacBytes;

    public CustomerCert(String company, String userId, String country) 
            throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException {
         keyGen = KeyPairGenerator.getInstance("DSA");
         keyGen.initialize(1024, new SecureRandom());
         keypair = keyGen.generateKeyPair();
         publicKey = keypair.getPublic();
         privateKey = keypair.getPrivate();
         pkcs10 = this.generatePKCS10(company, userId, country);

    }

    private byte[] generatePKCS10(String company, String userId, String country) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException {
        String sigAlg = "SHA1withDSA";  
        String params = "CN=" + company + ", serialNumber=" + userId + ", C=" + country;
        X500Principal principal = new X500Principal(params);  

        PKCS10CertificationRequest kpGen = new PKCS10CertificationRequest(sigAlg, principal, publicKey, new DERSet(), privateKey, null);
        byte[] c = kpGen.getEncoded();
        return c;
    }

    public String getCSRasString() throws UnsupportedEncodingException {
        return new String(pkcs10, "ASCII"); // ISO-8859-1
    }

    public byte[] createHMacSeal(byte[] message, String key) throws UnsupportedEncodingException
    {
        hmac = new HMac(new SHA1Digest());
        hmacBytes = new byte[hmac.getMacSize()];
        {
            hmac.init(new KeyParameter(Hex.decode(key)));
            hmac.update(message, 0, message.length);
            hmac.doFinal(hmacBytes, 0);
        }
        return hmacBytes;
    }
}

В чем я не уверен, так это в том, как в соответствии с требованием сделать запрос PKCS кодированным DER?
Другой вопрос заключается в том, как дать hmac seal ввод pkcs в кодировке DER? И, наконец, как получить массив байтов сгенерированной печати HMAC, потому что удаленная служба требует его в виде массива байтов в кодировке base64?

ИЗМЕНИТЬ

Как указал @Jcs, мое создание PKCS правильное, но сейчас я не уверен в правильном создании HMAC. Теперь я получил лучший ответ от удаленного сервера, и теперь я получаю сообщение об ошибке error in MAC value. Мои текущие методы создания HMac следующие:

public byte[] createHMacSeal(byte[] message, String key)  {
        String messageString = new String(message, "US-ASCII");
        HMac hmac = new HMac(new SHA1Digest());
        byte[] resBuf = new byte[hmac.getMacSize()];
        {
            byte[] m = messageString.getBytes();
            if (messageString.startsWith("0x"))
            {
                m = Hex.decode(messageString.substring(2));
            }
            hmac.init(new KeyParameter(key.getBytes("US-ASCII")));
            hmac.update(m, 0, m.length);
            hmac.doFinal(resBuf, 0);
            hmacBytes = resBuf;
        }
        return hmacBytes;

public byte[] createHMacSeal(byte[] message, String key) {
        SecretKey secretKey = new SecretKeySpec(key.getBytes("US-ASCII"), "HMac-SHA1");
        Mac mac;
        mac = Mac.getInstance("HMac-SHA1", "BC");
        mac.init(secretKey);
        mac.reset();
        mac.update(message, 0, message.length);
        hmacBytes = mac.doFinal();
        return hmacBytes;
    }

Эти два метода возвращают разные значения для HMAC. Для аргументов метода byte[] message — это PKCS getPKCS10() в кодировке DER, а второй аргумент key — это код активации SMS в виде строки из 10 символов 1234567890. Теперь я действительно поражен и пробовал много разных возможностей, но все равно то же сообщение об ошибке с удаленного сервера.


person Skyzer    schedule 14.11.2013    source источник
comment
Я подтверждаю, что getEncoded() возвращает запрос PKCS#10 в кодировке DER. Этот массив байтов следует использовать в качестве входных данных для метода обновления MAC. На самом деле ваш код выглядит хорошо в соответствии с вашими спецификациями.   -  person Jcs    schedule 14.11.2013
comment
@JCS Спасибо. Если это правильно, то другие вопросы остаются открытыми, например сообщение о печати HMAC, которое должно быть DER coded PKCS#10. Это весь kpGen.getEncoded() или только открытый ключ от него? Потому что в спецификации сказано, что никогда не отправляйте закрытый ключ на удаленный сервер. Еще один вопрос по поводу печати HMAC – это ключ, который является кодом SMS-активации. Это правильный способ передать его в hmac как ключевой параметр, расшифрованный в шестнадцатеричном формате? И, наконец, из-за неясного определения, какой алгоритм для PKCS - RSA или DSA?   -  person Skyzer    schedule 14.11.2013
comment
Здесь недостаточно места, я добавляю ответ.   -  person Jcs    schedule 15.11.2013
comment
@Jcs Спасибо за ответ, с вашей помощью я приблизился к решению и только что отредактировал свой вопрос о HMAC.   -  person Skyzer    schedule 16.11.2013


Ответы (2)


О запросе PKCS#10

Прежде всего, не беспокойтесь о запросе PKCS#10. Он не содержит закрытый ключ, только имя субъекта и открытый ключ. Закрытый ключ используется только для подписи запроса. Эта подпись является закрытым ключом Доказательство владения, т. е. доказывает, что вы действительно владеете закрытым ключом, соответствующим открытому ключу в запросе. То, как вы создаете запрос PKCS # 10, кажется хорошим.

О печати HMAC

Я думаю, что эта печать используется для проверки личности запрашивающего с помощью общего секрета: кода активации SMS. Я думаю, что это какой-то одноразовый пароль. Я не уверен, что вам нужно шестнадцатеричное декодирование этого кода, я думаю, вы должны использовать этот код как 10-байтовую строку в кодировке ascii, т.е. если код 0123456789, ключ HMAC 0x30313233343536373839.

Об алгоритме

Ну... это зависит только от того, что служба ожидает и/или позволяет. DSA предназначен только для подписи, RSA работает как для подписи, так и для шифрования.

person Jcs    schedule 14.11.2013
comment
Спасибо за подробности, это действительно очень помогло и сузило проблему до создания HMAC. Не могли бы вы взглянуть на это и правильно ли это? - person Skyzer; 16.11.2013

Я думаю, вы найдете:

Строка messageString = новая строка (сообщение, "US-ASCII");

и:

byte[] m = messageString.getBytes();

маловероятно, что это приведет к тому же значению, если сообщение изначально представляет собой массив байтов в кодировке DER. Если вам нужно время от времени обрабатывать строки, я бы рекомендовал преобразовать все в Hex или Base64, чтобы преобразование байтового массива было однозначным.

person David Hook    schedule 17.11.2013
comment
Спасибо, я принял это во внимание. Я не конвертирую сообщение обратно в строку, только в кодировку DER new JcaPKCS10CertificationRequest(req1.getEncoded()).setProvider("BC").getEncoded(); Но все же мой вопрос остается открытым, правильно ли мое создание HMAC? - person Skyzer; 20.11.2013