5

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.

Skyzer
  • 584
  • 3
  • 11
  • 27
  • I confirm that getEncoded() returns the DER encoded PKCS#10 request. This byte array should be used as input for the MAC update method. Actually your code looks good according to your specs. – Jcs Nov 14 '13 at 16:51
  • @Jcs Thanks. If that one is correct, then other questions are still open like the HMAC seal message that supposed to be `DER coded PKCS#10`. Is it the whole `kpGen.getEncoded()` or only the public key from that? Because in the spec is said that never send private key to remote server. Another question about HMAC seal is the key that is SMS-activation code. Is it correct way to give it to hmac as key parameter hex decoded? And lastly because of unclear definition what is the algorhitm for PKCS - RSA or DSA? – Skyzer Nov 14 '13 at 17:43
  • Not enough space here, I add an answer. – Jcs Nov 14 '13 at 21:27
  • @Jcs Thank you for response, with your help I got closer to solution and have just edited my question about HMAC – Skyzer Nov 16 '13 at 02:26

2 Answers2

2

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.

Jcs
  • 13,279
  • 5
  • 53
  • 70
  • Thanks for the details, it really helped alot and narrowed down the problem to HMAC creation. Could you take a look at that and whether it's correct? – Skyzer Nov 16 '13 at 01:48
0

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.

David Hook
  • 531
  • 3
  • 3
  • Thank you, I took it into consideration. I don't convert message back to string, only to DER encoded `new JcaPKCS10CertificationRequest(req1.getEncoded()).setProvider("BC").getEncoded();` But still my question is open whether my HMAC creation is correct? – Skyzer Nov 20 '13 at 14:19