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.
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.
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
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With