11

I am working on signing and encoding of CMS/PKCS#7 messages (something similar to C# SignedCms).

I have x509certificate from the keystore, rsa private key,
ContentInfo. ContentType is "oidPkcs7Data".

I don't quite understand what should I do next.

I thought:

  1. generate a signature and sign ContentInfo data
    Signature signature = Signature.getInstance("MD5withRSA");
    signature.initSign(rsaPrivateKeyFromStore); 
    signature.update(contentInfo.getData());
    signedData = signature.sign();
  1. encode signedData+signature.
    PKCS7 pkcs7 = new PKCS7(signedData);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    pkcs7.encodeSignedData(baos);

But I got the exception

sun.security.pkcs.ParsingException: Unable to parse the encoded bytes
    at sun.security.pkcs.PKCS7.(PKCS7.java:94)

Obviously I am doing it wrong.

Also I'd like to do it without BouncyCastle or Classpth or smth like these ones.

Is there possible to use just sun.security.* classes? I use java 1.5.

I am a new in DigitalSignature world and any help or advice is appreciated.

UPD

I generated my own certificate and tried to sign the data with it.

.Net code

        X509Certificate2 certificate = new X509Certificate2("X:\\mypfxstore.pfx", "123");
        String text = "text";
        ContentInfo contentInfo = new ContentInfo(System.Text.Encoding.UTF8.GetBytes(text));
        SignedCms cms = new SignedCms(contentInfo, false);
        CmsSigner signer = new CmsSigner(certificate);
        signer.IncludeOption = X509IncludeOption.None;
        signer.DigestAlgorithm = new Oid("SHA1");
        cms.ComputeSignature(signer, false);
        byte[] signature = cms.Encode();
        print(signature);

.Java code

    char[] password = "123".toCharArray();
    String text = "text";

    FileInputStream fis = new FileInputStream("X:\\mypfxstore.pfx");
    KeyStore ks = KeyStore.getInstance("pkcs12");
    ks.load(fis, password);

    String alias = ks.aliases().nextElement();
    PrivateKey pKey = (PrivateKey)ks.getKey(alias, password);
    X509Certificate c = (X509Certificate)ks.getCertificate(alias);

    //Data to sign
    byte[] dataToSign = text.getBytes("UTF-8");
    //compute signature:
    Signature signature = Signature.getInstance("SHA1WithRSA");
    signature.initSign(pKey);
    signature.update(dataToSign);
    byte[] signedData = signature.sign();

    //load X500Name
    X500Name xName      = X500Name.asX500Name(c.getSubjectX500Principal());
    //load serial number
    BigInteger serial   = c.getSerialNumber();
    //laod digest algorithm
    AlgorithmId digestAlgorithmId = new AlgorithmId(AlgorithmId.SHA_oid);
    //load signing algorithm
    AlgorithmId signAlgorithmId = new AlgorithmId(AlgorithmId.RSAEncryption_oid);

    //Create SignerInfo:
    SignerInfo sInfo = new SignerInfo(xName, serial, digestAlgorithmId, signAlgorithmId, signedData);

    //Create ContentInfo:
    ContentInfo cInfo = new ContentInfo(ContentInfo.DIGESTED_DATA_OID, new DerValue(DerValue.tag_OctetString, dataToSign));

    //Create PKCS7 Signed data
    PKCS7 p7 = new PKCS7(new AlgorithmId[] { digestAlgorithmId }, cInfo,
            new java.security.cert.X509Certificate[] { /*cert,*/ },
            new SignerInfo[] { sInfo });

    //Write PKCS7 to bYteArray
    ByteArrayOutputStream bOut = new DerOutputStream();
    p7.encodeSignedData(bOut);
    byte[] encoded = bOut.toByteArray();

    print(encoded);

Java output

length=264
3082010406092A864886F70D010702A081F63081F3020101310B300906052B0E03021A0500
301306092A864886F70D0 -> 10705A <- 0060404746578743181CB3081C8020101302630123110300E06
035504031307436F6D70616E790210FCAF9B5224FB4B9F4000B5127D881E2E300906052B0E0302
1A0500300D06092A864886F70D0101010500048180636ADD9F7E218AF3CBC5A75FA2076A53BE49
03DC864E87EBA3C1EE594FAACAFE93CA6F3410D847AC0C0ACB9FD88EC9CF6B00379FA9AD256C86
7204ED81E3FA2F8F492109FF87E81398B7B489B00A35914A2B51919DAAEC2BA87CEFB5AF52294E
2448B5B150D50A39BA0471A9AA1EA2B38A4E23BBA56E029842459F0D5BA3D511

.Net output

length=264
3082010406092A864886F70D010702A081F63081F3020101310B300906052B0E03021A0500
301306092A864886F70D0 -> 10701A <- 0060404746578743181CB3081C8020101302630123110300E06
035504031307436F6D70616E790210FCAF9B5224FB4B9F4000B5127D881E2E300906052B0E0302
1A0500300D06092A864886F70D0101010500048180636ADD9F7E218AF3CBC5A75FA2076A53BE49
03DC864E87EBA3C1EE594FAACAFE93CA6F3410D847AC0C0ACB9FD88EC9CF6B00379FA9AD256C86
7204ED81E3FA2F8F492109FF87E81398B7B489B00A35914A2B51919DAAEC2BA87CEFB5AF52294E
2448B5B150D50A39BA0471A9AA1EA2B38A4E23BBA56E029842459F0D5BA3D511

Certificate example example

nixspirit
  • 213
  • 1
  • 2
  • 6
  • I don't think this is available in Java for use of others than internal java code.I mean it is most likely that you can't do it without resorting to 3rd party libraries. – Jim Apr 18 '12 at 21:11
  • yeah, I gave up using sun.* classes =) but anyway I think it can be done by using them. I looked at BouncyCastle. I managed to sign and enode my data but the signature is different from that one C# SignedCms generated. Actualy some parts of my signature are the same but the rest is different from .Net signature. e.g. .Net `308206BC06092A86488`, Java `308006092A864886F70D010702A0`. And Java signature has different length aswell – nixspirit Apr 19 '12 at 10:29
  • Different key lengths and different algorithms. What you show here is an ASN.1 encoded object identifier with an algorithm specification inside. You can use Bouncy or openssl to decode/print it. – Maarten Bodewes May 22 '12 at 19:39

1 Answers1

9
package test.pkcs7;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.math.BigInteger;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.Enumeration;

import sun.security.pkcs.ContentInfo;
import sun.security.pkcs.PKCS7;
import sun.security.pkcs.SignerInfo;
import sun.security.util.DerOutputStream;
import sun.security.util.DerValue;
import sun.security.x509.AlgorithmId;
import sun.security.x509.X500Name;

public class GenPKCS {

    static final String STORENAME = "c:/fileName.p12";
    static final String STOREPASS = "password";

    public static void main(String[] args) throws Exception{

        //First load the keystore object by providing the p12 file path
        KeyStore clientStore = KeyStore.getInstance("PKCS12");
        //replace testPass with the p12 password/pin
        clientStore.load(new FileInputStream(STORENAME), STOREPASS.toCharArray());

        Enumeration<String> aliases = clientStore.aliases();
        String aliaz = "";
        while(aliases.hasMoreElements()){
            aliaz = aliases.nextElement();
            if(clientStore.isKeyEntry(aliaz)){
                break;
            }
        }
        X509Certificate c = (X509Certificate)clientStore.getCertificate(aliaz);

        //Data to sign
        byte[] dataToSign = "SigmaWorld".getBytes();
        //compute signature:
        Signature signature = Signature.getInstance("Sha1WithRSA");
        signature.initSign((PrivateKey)clientStore.getKey(aliaz, STOREPASS.toCharArray()));
        signature.update(dataToSign);
        byte[] signedData = signature.sign();

        //load X500Name
        X500Name xName      = X500Name.asX500Name(c.getSubjectX500Principal());
        //load serial number
        BigInteger serial   = c.getSerialNumber();
        //laod digest algorithm
        AlgorithmId digestAlgorithmId = new AlgorithmId(AlgorithmId.SHA_oid);
        //load signing algorithm
        AlgorithmId signAlgorithmId = new AlgorithmId(AlgorithmId.RSAEncryption_oid);

        //Create SignerInfo:
        SignerInfo sInfo = new SignerInfo(xName, serial, digestAlgorithmId, signAlgorithmId, signedData);
        //Create ContentInfo:
        ContentInfo cInfo = new ContentInfo(ContentInfo.DIGESTED_DATA_OID, new DerValue(DerValue.tag_OctetString, dataToSign));
        //Create PKCS7 Signed data
        PKCS7 p7 = new PKCS7(new AlgorithmId[] { digestAlgorithmId }, cInfo,
                new java.security.cert.X509Certificate[] { c },
                new SignerInfo[] { sInfo });
        //Write PKCS7 to bYteArray
        ByteArrayOutputStream bOut = new DerOutputStream();
        p7.encodeSignedData(bOut);
        byte[] encodedPKCS7 = bOut.toByteArray();
    }
}

The following changes are to be done in the Java code to make the output similar to .NET:

//Create ContentInfo:
ContentInfo cInfo = new ContentInfo(ContentInfo.DIGESTED_DATA_OID, new DerValue(DerValue.tag_OctetString, dataToSign));

change to

//Create ContentInfo:
ContentInfo cInfo = new ContentInfo(ContentInfo.DATA_OID, new DerValue(DerValue.tag_OctetString, dataToSign));
Gilles 'SO- stop being evil'
  • 50,912
  • 13
  • 120
  • 179
Mohit Sethi
  • 692
  • 4
  • 7
  • 1
    thanks a lot. it is very close to the original signature, that I could generaty with .Net's SignedCms. Anyway the signature (that was genereted with the code you provide) has different length from that .Net's signature. – nixspirit Jun 12 '12 at 07:32
  • actually I can't generate the same signature neither with sun.* classes not with the bouncycastle . Obviously I am doing smth wrong. But what.. – nixspirit Jun 12 '12 at 07:40
  • Hi, Please provide me in hex sample public key, private key, data to be signed and the output from .net. I will analyze and try to fix the issue so that you are able to validate the signatures from cross platforms between java and .net. The signature generated will always be different because of randomization in the process of padding the data before encipherment with the private key. – Mohit Sethi Jun 18 '12 at 06:38
  • Thanks a lot @7sigma. You help me to not give up with this issue. I updated my post with C# and Java code and outputs. I generated my own certificate and tried to sign data with it. Here is my store [link](https://dl.dropbox.com/u/1170068/mypfxstore.pfx). You'll see one small difference between the outputs. – nixspirit Jun 19 '12 at 17:34
  • Thanks a lot @nixspirit for providing the environment. I was able to get the desired output in Java Code by changing one of the statement after analysis of the data provided by you. After the modification the output is similar as the one from .NET code. Please find the changes in my post as EDIT. I am analyzing what was right or wrong in the previous encoding with respect to PKCS7 specifications. – Mohit Sethi Jun 20 '12 at 07:20