Статус: Новичок
Группы: Участники
Зарегистрирован: 28.02.2025(UTC) Сообщений: 6 
|
Добрый день! Есть сервис подписания файлов. При подписании формируется файл sig. При проверке подписываемого файла на сайтах КриптоПро либо Госуслугах выдает "Не удалось проверить подпись формата CMS. Ошибка: [Неправильное значение хеша]. Код: [0x80091007]." Вот сам сервис: Код:
public class SignatureServiceDisconnected implements SignatureService {
public static final String STR_CMS_OID_SIGNED = "1.2.840.113549.1.7.2";
public static final String STR_CMS_OID_DATA = "1.2.840.113549.1.7.1";
public static final String STR_CMS_OID_CONT_TYP_ATTR = "1.2.840.113549.1.9.3";
public static final String STR_CMS_OID_DIGEST_ATTR = "1.2.840.113549.1.9.4";
public static final String STR_CMS_OID_SIGN_TYM_ATTR = "1.2.840.113549.1.9.5";
private final KeyStore keyStore;
@Override
public String signature(Container<?> container, SigningDocumentRequest signingDocument) throws Exception {
JCPPrivateKeyEntry entry = (JCPPrivateKeyEntry) (Object) keyStore.getEntry(
container.getAlias(),
new KeyStore.PasswordProtection(container.getPassword().toCharArray())
);
PrivateKey privateKey = entry.getPrivateKey();
Certificate certificate = entry.getCertificate();
byte[] data = Base64.getDecoder().decode(signingDocument.document());
byte[] hashCMSEx = createHashCMSEx(data, new PrivateKey[]{privateKey}, new Certificate[]{certificate});
return Base64.getEncoder().encodeToString(hashCMSEx);
}
private static byte[] createHashCMSEx(byte[] data, PrivateKey[] keys, Certificate[] certs) throws Exception {
//create hashCMS
final ContentInfo all = new ContentInfo();
all.contentType = new Asn1ObjectIdentifier(new OID(STR_CMS_OID_SIGNED).value);
final SignedData cms = new SignedData();
all.content = cms;
cms.version = new CMSVersion(1);
// digest
cms.digestAlgorithms = new DigestAlgorithmIdentifiers(1);
final DigestAlgorithmIdentifier a = new DigestAlgorithmIdentifier(new OID(JCP.GOST_DIGEST_OID).value);
a.parameters = new Asn1Null();
cms.digestAlgorithms.elements[0] = a;
// Нельзя сделать подпись совмещенной, если нет данных, а
// есть только хеш с них.
cms.encapContentInfo = new EncapsulatedContentInfo(new Asn1ObjectIdentifier(new OID(STR_CMS_OID_DATA).value), null);
// certificates
final int nCerts = certs.length;
cms.certificates = new CertificateSet(nCerts);
cms.certificates.elements = new CertificateChoices[nCerts];
for (int i = 0; i < cms.certificates.elements.length; i++) {
final ru.CryptoPro.JCP.ASN.PKIX1Explicit88.Certificate certificate = new ru.CryptoPro.JCP.ASN.PKIX1Explicit88.Certificate();
final Asn1BerDecodeBuffer decodeBuffer = new Asn1BerDecodeBuffer(certs[i].getEncoded());
certificate.decode(decodeBuffer);
cms.certificates.elements[i] = new CertificateChoices();
cms.certificates.elements[i].set_certificate(certificate);
}
// Signature.getInstance
final Signature signature = Signature.getInstance(JCP.GOST_SIGN_2012_256_NAME, JCP.PROVIDER_NAME);
byte[] sign;
// signer infos
final int nsign = keys.length;
cms.signerInfos = new SignerInfos(nsign);
for (int i = 0; i < cms.signerInfos.elements.length; i++) {
cms.signerInfos.elements[i] = new SignerInfo();
cms.signerInfos.elements[i].version = new CMSVersion(1);
cms.signerInfos.elements[i].sid = new SignerIdentifier();
final byte[] encodedName = ((X509Certificate) certs[i]).getIssuerX500Principal().getEncoded();
final Asn1BerDecodeBuffer nameBuf = new Asn1BerDecodeBuffer(encodedName);
final Name name = new Name();
name.decode(nameBuf);
final CertificateSerialNumber num = new CertificateSerialNumber(((X509Certificate) certs[i]).getSerialNumber());
cms.signerInfos.elements[i].sid.set_issuerAndSerialNumber(new IssuerAndSerialNumber(name, num));
cms.signerInfos.elements[i].digestAlgorithm = new DigestAlgorithmIdentifier(new OID(JCP.GOST_DIGEST_OID).value);
cms.signerInfos.elements[i].digestAlgorithm.parameters = new Asn1Null();
cms.signerInfos.elements[i].signatureAlgorithm = new SignatureAlgorithmIdentifier(new OID(JCP.GOST_EL_KEY_OID).value);
cms.signerInfos.elements[i].signatureAlgorithm.parameters = new Asn1Null();
//signedAttributes
final int kmax = 3;
cms.signerInfos.elements[i].signedAttrs = new SignedAttributes(kmax);
//-contentType
int k = 0;
cms.signerInfos.elements[i].signedAttrs.elements[k] = new Attribute(
new OID(STR_CMS_OID_CONT_TYP_ATTR).value,
new Attribute_values(1)
);
final Asn1Type conttype = new Asn1ObjectIdentifier(new OID(STR_CMS_OID_DATA).value);
cms.signerInfos.elements[i].signedAttrs.elements[k].values.elements[0] = conttype;
//-Time
k += 1;
cms.signerInfos.elements[i].signedAttrs.elements[k] = new Attribute(
new OID(STR_CMS_OID_SIGN_TYM_ATTR).value,
new Attribute_values(1)
);
final Time time = new Time();
final Asn1UTCTime utcTime = new Asn1UTCTime();
//текущая дата с календаря
utcTime.setTime(Calendar.getInstance());
time.set_utcTime(utcTime);
cms.signerInfos.elements[i].signedAttrs.elements[k].values.elements[0] = time.getElement();
//-message digest
k += 1;
cms.signerInfos.elements[i].signedAttrs.elements[k] = new Attribute(
new OID(STR_CMS_OID_DIGEST_ATTR).value,
new Attribute_values(1)
);
final byte[] messageDigestBlob;
// Если вместо данных у нас хеш, то сразу его передаем, ничего не вычисляем.
messageDigestBlob = digestm(data, JCP.GOST_DIGEST_2012_256_NAME, JCP.PROVIDER_NAME);
final Asn1Type messageDigest = new Asn1OctetString(messageDigestBlob);
cms.signerInfos.elements[i].signedAttrs.elements[k].values.elements[0] = messageDigest;
// Добавление signingCertificateV2 в подписанные аттрибуты, чтобы подпись
// стала похожа на CAdES-BES.
//signature
Asn1BerEncodeBuffer encBufSignedAttr = new Asn1BerEncodeBuffer();
cms.signerInfos.elements[i].signedAttrs.encode(encBufSignedAttr);
final byte[] hsign = encBufSignedAttr.getMsgCopy();
signature.initSign(keys[i]);
signature.update(hsign);
sign = signature.sign();
cms.signerInfos.elements[i].signature = new SignatureValue(sign);
}
// encode
final Asn1BerEncodeBuffer asnBuf = new Asn1BerEncodeBuffer();
all.encode(asnBuf, true);
return asnBuf.getMsgCopy();
}
public static byte[] digestm(byte[] bytes, String digestAlgorithmName, String providerName) throws Exception {
final ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
final java.security.MessageDigest digest = providerName != null
? java.security.MessageDigest.getInstance(digestAlgorithmName, providerName)
: MessageDigest.getInstance(digestAlgorithmName);
final DigestInputStream digestStream = new DigestInputStream(stream, digest);
while (digestStream.available() != 0) {
digestStream.read();
}
return digest.digest();
}
}
|
|
|
|
Статус: Сотрудник
Группы: Участники
Зарегистрирован: 26.07.2011(UTC) Сообщений: 13,623   Сказал «Спасибо»: 567 раз Поблагодарили: 2283 раз в 1784 постах
|
Здравствуйте.
На входе что в signingDocument.document()? |
|
|
|
|
Статус: Новичок
Группы: Участники
Зарегистрирован: 28.02.2025(UTC) Сообщений: 6 
|
Автор: Андрей *  Здравствуйте.
На входе что в signingDocument.document()? String с файлом в Base64. Файл формата pdf.
|
|
|
|
Статус: Сотрудник
Группы: Участники
Зарегистрирован: 26.07.2011(UTC) Сообщений: 13,623   Сказал «Спасибо»: 567 раз Поблагодарили: 2283 раз в 1784 постах
|
Автор: Nitratius  Автор: Андрей *  Здравствуйте.
На входе что в signingDocument.document()? String с файлом в Base64. Файл формата pdf. взять любой файл (бинарный, можно pdf) закодировать (как на входе при подписании) вызвать только digestm для data записать в файл.hash приложить тестовый файл и файл с хешем или самостоятельно проверить внешними утилитами хеш от файла |
|
|
|
|
Статус: Сотрудник
Группы: Участники
Зарегистрирован: 26.07.2011(UTC) Сообщений: 13,623   Сказал «Спасибо»: 567 раз Поблагодарили: 2283 раз в 1784 постах
|
или ещё проще - вот файл  testfile.pdf (3kb) загружен 1 раз(а).жду от него base64 (что попадает в signingDocument.document()) |
|
|
|
|
Статус: Сотрудник
Группы: Участники
Зарегистрирован: 26.07.2011(UTC) Сообщений: 13,623   Сказал «Спасибо»: 567 раз Поблагодарили: 2283 раз в 1784 постах
|
хеш для файла выше такой должен быть в hex: A7B27353CEA6BFB2493CEAF0F55C50E78B5BC1CFA78F5C496FD4613A8478A4EE |
|
|
|
|
Статус: Новичок
Группы: Участники
Зарегистрирован: 28.02.2025(UTC) Сообщений: 6 
|
|
|
|
|
Статус: Сотрудник
Группы: Участники
Зарегистрирован: 26.07.2011(UTC) Сообщений: 13,623   Сказал «Спасибо»: 567 раз Поблагодарили: 2283 раз в 1784 постах
|
Декодировал. Хеш совпал, хорошо.
|
|
|
|
|
Статус: Новичок
Группы: Участники
Зарегистрирован: 28.02.2025(UTC) Сообщений: 6 
|
А есть какой-нибудь пример java кода подписания файла с помощью cryptopro jcp ?
|
|
|
|
Статус: Сотрудник
Группы: Участники
Зарегистрирован: 06.12.2008(UTC) Сообщений: 4,014  Откуда: Крипто-Про Сказал(а) «Спасибо»: 21 раз Поблагодарили: 721 раз в 680 постах
|
Здравствуйте. В примере фигурируют JCP.GOST_DIGEST_OID, JCP.GOST_EL_KEY_OID, что неверно, т.к. подпись создается ГОСТ 2012 (256). |
|
 1 пользователь поблагодарил Евгений Афанасьев за этот пост.
|
|
|
Быстрый переход
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.
Important Information:
The Форум КриптоПро uses cookies. By continuing to browse this site, you are agreeing to our use of cookies.
More Details
Close