Java-реализация C # SignedCms

Я работаю над реализацией функциональности C # SignedCms в Java.

Я использую бодрые заклинатели. Проблема в том, что я получаю подпись Java, которая отличается от подписи, созданной с помощью SignedCms.

C# code
X509Certificate2 certificate = new X509Certificate2("myCertPath", "myPass"); 
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
Security.addProvider(new BouncyCastleProvider()); 
char[] password = "myPass".toCharArray(); 
String text = "text"; 
FileInputStream fis = new FileInputStream("myCertPath"); 
KeyStore ks = KeyStore.getInstance("pkcs12"); 
ks.load(fis, password); 

String alias = ks.aliases().nextElement(); 
PrivateKey pKey = (PrivateKey)ks.getKey(alias, password); 
X509Certificate cert = (X509Certificate)ks.getCertificate(alias); 
java.util.List certList = new ArrayList(); 
Store certs = new JcaCertStore(certList); 

CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); 
JcaSimpleSignerInfoGeneratorBuilder builder = new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setDirectSignature(true); 

gen.addSignerInfoGenerator(builder.build("SHA1withRSA", pKey, cert)); 
gen.addCertificates(certs); 

CMSTypedData msg = new CMSProcessableByteArray(text.getBytes()); 
CMSSignedData s = gen.generate(msg, false); 
print(s.getEncoded()); 

Они оба не включают сертификаты x509.

C# generated signature

длина = 434 308201AE06092A864886F70D010702A082019F3082019B020101310B300906052B0E03021A0500301306092 A864886F70D010701A006040474657874318201723082016E0201013081CB3081B6310B3009060355040613 02555331173015060355040A130E566572695369676E2C20496E632E311F301D060355040B1316566572695 369676E205472757374204E6574776F726B313B3039060355040B13325465726D73206F6620757365206174 2068747470733A2F2F7777772E766572697369676E2E636F6D2F7270612028632930393130302E060355040 31327566572695369676E20436C617373203320436F6465205369676E696E6720323030392D322043410210 1763F9A88334A01FFB3B7BAB384A9B93300906052B0E03021A0500300D06092A864886F70D0101010500048 1800B866A9A7045E3C86E5DB69CDAD5CED211A4A2362BCC4DDB2742BF0CDB65BC88556C97A6C08D68F8070D 89CC78ACD84A636F15B40D166E461411C6A04D5EC379283988DA4258B684FFEF9F08B293A03A0B40900E245 874D8C0587BBD58BDD915A50D27456E6EEB883846CAC485853BA5E22E45D333C940A958E641A00C9602B9

Java generagted signature

длина = 428 308006092A864886F70D010702A0803080020101310B300906052B0E03021A0500308006092A864886F70D0 107010000318201723082016E0201013081CB3081B6310B300906035504061302555331173015060355040A 130E566572695369676E2C20496E632E311F301D060355040B1316566572695369676E205472757374204E6 574776F726B313B3039060355040B13325465726D73206F66207573652061742068747470733A2F2F777777 2E766572697369676E2E636F6D2F7270612028632930393130302E06035504031327566572695369676E204 36C617373203320436F6465205369676E696E6720323030392D3220434102101763F9A88334A01FFB3B7BAB 384A9B93300906052B0E03021A0500300D06092A864886F70D01010105000481800B866A9A7045E3C86E5DB 69CDAD5CED211A4A2362BCC4DDB2742BF0CDB65BC88556C97A6C08D68F8070D89CC78ACD84A636F15B40D16 6E461411C6A04D5EC379283988DA4258B684FFEF9F08B293A03A0B40900E245874D8C0587BBD58BDD915A50 D27456E6EEB883846CAC485853BA5E22E45D333C940A958E641A00C9602B9000000000000

Я застрял в этом вопросе. Честно говоря, я в отчаянии и понятия не имею, что не так.

Любая помощь приветствуется.

UPD.

Вывод Java был закодирован в BER. Мне нужна была подпись в кодировке DER. Для преобразования BER в DER я использовал

ByteArrayOutputStream bOut = new ByteArrayOutputStream();
DEROutputStream dOut = new DEROutputStream(bOut);
dOut.writeObject(s.toASN1Structure().toASN1Primitive());
dOut.close();
bytep[ encoded = bOut.toByteArray();

Теперь выходы одинаковы.

Большое спасибо, ребята.

Ответы на вопрос(1)

Решение Вопроса

Looking at the ASN.1 DER encoding

Посмотрите на начало обеих полученных кодировок DER:

C#:   308201AE...
Java: 3080...

Кодировка C # имеет форму определенной длины, т.е.30 указывает наSEQUENCE, 82 указывает кодирование определенной длины, используя следующие два байта, и01AE является фактическим значением длины 430. Следующие 430 байтов плюс 4 считанных до сих пор составляют в общей сложности 434 байта.

Кодирование Java, с другой стороны, отличается тем, что оно указывает кодирование неопределенной длины (80). Строго говоря, это уже не кодировка DER, а кодировка BER. Это означает, что для этого элемента не указана явная длина, но этот элемент заканчивается специальнымEND OF CONTENTS элемент вместо этого, который закодирован как0000, Вы заметите довольно много из них в конце кодирования Java. Подробнее о деталях вэто руководство в BER / DER.

Остальные две структуры абсолютно идентичны, даже само значение подписи. Просто версия Java использует неопределенную длину, а версия C # использует определенную длину. Если проверяющая сторона понимает обе кодировки BER и DER, две подписи будут идентичны с точностью до кодирования. И кодирование не будет играть роль в процессе проверки подписи. Вот чтоCMS RFC говорит по этому поводу:

СsignedAttrs подарок:

Specifically, the initial input is the encapContentInfo eContent OCTET STRING to which the signing process is applied. Only the octets comprising the value of the eContent OCTET STRING are input to the message digest algorithm, not the tag or the length octets.

БезsignedAttrs:

When the signedAttrs field is absent, only the octets comprising the value of the SignedData encapContentInfo eContent OCTET STRING (e.g., the contents of a file) are input to the message digest calculation. This has the advantage that the length of the content being signed need not be known in advance of the signature generation process.

Другими словами: только байты, составляющие фактическое значениеeContent хешируются, и действительно только те. Ни его тег, ни его длина, а также теги и длины его фрагментов (в случае неопределенного составного кодирования) не могут быть хэшированы в процессе. Я признаю, что есть реализации, которые ошибаются, и это, безусловно, довольно сложная проблема.

Why use indefinite lengths in CMS SignedData?

Хотя это добавляет много проблем сложности и совместимости, это имеет смысл по одной причине (помимо того, что она на несколько байтов меньше): если вы создаете «прикрепленные подписи»; (те, где исходный документ встроен вEncapContentInfo элемент), выбор неопределенной длины позволяет создавать и проверять подпись в потоковом режиме: вы можете читать или записывать порцию за порцией. Принимая во внимание, что для определенной длины вы должны прочитать / записать все сразу, потому что вам нужно знать длину заранее, чтобы создать окончательный формат Tag-Length-Value для кодирования DER. Идея возможности потокового ввода-вывода очень мощна в этом контексте: представьте, что вы хотите создать прикрепленную подпись файла журнала размером несколько ГБ - любой подход без потоковой передачи быстро исчерпает память.

Java-версия Bouncy Castle недавно добавила поддержку потоковой передачи в контексте CMS, высока вероятность того, что она не будет слишком долгой, пока версия C # не обнаружит ее.

 nixspirit20 июн. 2012 г., 05:34
Большое спасибо за ваш ответ. Это помогло мне понять процесс. Теперь я только что конвертировал BER в DER и у меня получился тот же вывод. Я обновил свой пост конверсией BER-> DER.

Ваш ответ на вопрос