Question

I'm stuck in creating correctly PKCS#10 certificate signing request and HMAC seal. I need to create them and send to remote service that will produce for me certificate in PKCS#7 format. More exactly:

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)   

I'm wrapping the result into SoapMessage and sending to remote service and get a response. The response is error because either CSR or HMAC were generated incorrectly. The remote service doesn't send more specific error message, but as I said the error is because of my incorrectly generated CSR or HMAC. The subject and HMAC key are example values given by remote service, that's why the issue can't be because of them.

Here is the code how I implemented it

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;
    }
}

What I am not sure is how according to the requirement make the PKCS request DER encoded?
Another question is how to give to hmac seal a DER coded pkcs input? And lastly how to get the byte array of generated HMAC seal, because the remote service requires it as base64 encoded byte array?

EDIT

As @Jcs pointed out my PKCS creation is correct but right now I'm not sure about the correct HMAC creation. I got now beter response from remote server and the error message I get now is error in MAC value. My current methods for HMac creation are following:

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;
    }

These two methods return different values for HMAC. For the method arguments byte[] message is the DER coded PKCS getPKCS10() and second argument key is the SMS activation code as a 10 character string 1234567890. Now I'm really struck and have tried many different possibilities but still the same error message from the remote server.

Was it helpful?

Solution

About the PKCS#10 request

First of all, don't worry about the PKCS#10 request. It does not contain the private key, only the subject name and the public key. The private key is only used to sign the request. This signature is a private key Proof of Possession i.e. it proves that you really owns the private key corresponding to the public key in the request. The way you are building the PKCS#10 request seems good.

About the HMAC seal

I think that this seal is used to verify the identity of the requester with a shared secret: the SMS-activation code. I think this is some kind of one time password. I'm not sure you have to Hex-decode this code, I think you should use this code as 10 byte ascii-encoded string i.e if the code is 0123456789 the HMAC key is 0x30313233343536373839.

About the algorithm

Well... it only depends on what the service is expecting and/or allows. DSA is for signature only, RSA works for both signature and encryption.

OTHER TIPS

I think you will find:

String messageString = new String(message, "US-ASCII");

and:

byte[] m = messageString.getBytes();

is highly unlikely to result in the same value if message is originally a DER encoded byte array. If you need to handle Strings some of the time I would recommend converting everything to either Hex or Base64, so the byte array conversion is unambiguous.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top