Статус: Новичок
Группы: Участники
Зарегистрирован: 28.04.2021(UTC) Сообщений: 4 Сказал(а) «Спасибо»: 2 раз
|
Добрый день. Евгений Афанасьев, спасибо! Получилось сформировать необходимый ключ для браузера (код приложил ниже). Буду рад услышать замечания или предложения по улучшению этого решения. Может следует на что-то обратить особенное внимание при использовании подобного кода? Код:
package ru.cipher.gost.test;
import JCSP.Encryption.Common;
import com.objsys.asn1j.runtime.Asn1BerDecodeBuffer;
import com.objsys.asn1j.runtime.Asn1DerEncodeBuffer;
import com.objsys.asn1j.runtime.Asn1ObjectIdentifier;
import com.objsys.asn1j.runtime.Asn1Tag;
import ru.CryptoPro.Crypto.CryptoProvider;
import ru.CryptoPro.JCP.ASN.Gost28147_89_EncryptionSyntax.Gost28147_89_EncryptedKey;
import ru.CryptoPro.JCP.JCP;
import ru.CryptoPro.JCP.params.CryptParamsInterface;
import ru.CryptoPro.JCP.params.CryptParamsSpec;
import ru.CryptoPro.JCP.params.OID;
import ru.CryptoPro.JCP.spec.X509PublicKeySpec;
import ru.CryptoPro.JCSP.JCSP;
import ru.CryptoPro.JCSP.Key.GostPrivateKey;
import ru.CryptoPro.JCSP.Key.GostPublicKey;
import ru.CryptoPro.JCSP.Key.PublicKeySpec;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.spec.X509EncodedKeySpec;
public class EncryptDataForRecipientsInBrowser extends Common {
private static final String PROVIDER = JCSP.PROVIDER_NAME;
private static final String DATA_CIPHER_ALG = CryptoProvider.GOST_CIPHER_NAME + "/CNT/NoPadding";
private static final String WRAP_KEY_ALG = CryptoProvider.GOST_CIPHER_NAME + "/PRO12_EXPORT/NoPadding";
private static final String EXCHANGE_KEY_ALG = JCP.GOST_DH_2012_512_NAME;
private static final String SIGN_ALG = JCP.GOST_SIGN_2012_512_NAME;
private static final String EPHEMERAL_KEY_ALG = JCP.GOST_EPH_DH_2012_512_NAME;
public static void main(String... args) throws Exception {
EncryptDataForRecipientsInBrowser encrypt = new EncryptDataForRecipientsInBrowser();
encrypt.execute();
}
public void execute() throws Exception {
final boolean createKeys = false;
final boolean deleteKeys = false;
try {
// Чтение пользовательский ключей и сертификаторв открытых ключей
prepare(PROVIDER, EXCHANGE_KEY_ALG, SIGN_ALG, createKeys, createKeys);
// Шифрование данных на новом сессионном ключе
EncryptedData encryptedData = encryptData("Данные для шифрования");
// Шифрование сессионного ключа для двух получателей
SecretKeyForRecipient alicaKey = prepareSecretKeyForRecipient(encryptedData.sessionKey, alicaCert);
SecretKeyForRecipient bobKey = prepareSecretKeyForRecipient(encryptedData.sessionKey, bobCert);
// вывод результата для подстановки в браузере
System.out.println("{\n" +
" \"exportedKey\": {\n" +
" \"alica\": {\"dn\":\"" + alicaKey.certDn + "\",\"key\":\"" + formatSecretKeyForBrowser(alicaKey) + "\"},\n" +
" \"bob\": {\"dn\":\"" + bobKey.certDn + "\",\"key\":\"" + formatSecretKeyForBrowser(bobKey) + "\"}\n" +
" },\n" +
" \"encryptedData\": \"" + toHexBytesStr(encryptedData.encryptedData, "") + "\",\n" +
" \"oSesKeyIV\": \"" + toHexBytesStr(encryptedData.iv) + "\"\n" +
"}");
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
if (alicaPrivate != null) {
((GostPrivateKey) alicaPrivate).clear();
}
if (bobPrivate != null) {
((GostPrivateKey) bobPrivate).clear();
}
// Удаление контейнеров.
clear(deleteKeys);
}
}
private String formatSecretKeyForBrowser(SecretKeyForRecipient keyForRecipient) throws Exception {
StringBuilder resultKey = new StringBuilder();
// Первая часть ключа
char[] firstKeyPartChars = CryptParamsSpec.OID_Gost28147_89_Rosstandart_TC26_Z_ParamSet.toString().toCharArray();
byte[] firstKeyPartBytes = new byte[firstKeyPartChars.length];
for (int i = 0; i < firstKeyPartBytes.length; i++) {
firstKeyPartBytes[i] = (byte) firstKeyPartChars[i];
}
resultKey.append(toHexBytesStr(firstKeyPartBytes));
resultKey.append(':');
// Вторая часть ключа. Публичный эфемерный ключ
byte[] ephPublicBlob = ((PublicKeySpec)(((GostPublicKey) keyForRecipient.ephPublicKey)).getSpec()).getBlob();
resultKey.append(toHexBytesStr(ephPublicBlob));
resultKey.append(':');
// Третья часть ключа. Секретный сессионный ключ
resultKey.append(toHexBytesStr(new int[] {
0x01, // bType = SIMPLEBLOB
0x20, // bVersion = 0x20
0x00, 0x00, // reserved
0x1E, 0x66, 0x00, 0x00, // KeyAlg = CALG_G28147
0xFD, 0x51, 0x4A, 0x37, // Magic = GR3410_1_MAGIC
0x1E, 0x66, 0x00, 0x00 // EncryptKeyAlgId = CALG_G28147
})).append(" ");
Asn1BerDecodeBuffer wrappedSessionKeyBuf = new Asn1BerDecodeBuffer(keyForRecipient.wrappedKey);
Gost28147_89_EncryptedKey gostEncryptedKey = new Gost28147_89_EncryptedKey();
gostEncryptedKey.decode(wrappedSessionKeyBuf);
resultKey.append(toHexBytesStr(keyForRecipient.ukm)).append(" ");
resultKey.append(toHexBytesStr(gostEncryptedKey.encryptedKey.value)).append(" ");
resultKey.append(toHexBytesStr(gostEncryptedKey.macKey.value)).append(" ");
resultKey.append(toHexBytesStr(oidToAsn1SeqBytes(CryptParamsSpec.OID_Gost28147_89_Rosstandart_TC26_Z_ParamSet)));
return resultKey.toString();
}
private SecretKeyForRecipient prepareSecretKeyForRecipient(SecretKey sessionKey, Certificate recipientCert) throws Exception {
PublicKey recipientPublicKey = jspToJcspKey(recipientCert.getPublicKey());
SecureRandom random = SecureRandom.getInstance(JCP.CP_RANDOM, PROVIDER);
// Создание эфемерного ключа нужного типа
// Инициализируется параметрами открытого ключа получателя
KeyPairGenerator kpg = KeyPairGenerator.getInstance(EPHEMERAL_KEY_ALG, PROVIDER);
kpg.initialize(X509PublicKeySpec.fromKey(recipientPublicKey));
KeyPair ephPair = kpg.generateKeyPair();
// Создается UKM
byte[] ukm = new byte[8];
random.nextBytes(ukm);
// Согласование ключей
KeyAgreement keyAgreement = KeyAgreement.getInstance(EXCHANGE_KEY_ALG, PROVIDER);
keyAgreement.init(ephPair.getPrivate(), new IvParameterSpec(ukm), null);
keyAgreement.doPhase(recipientPublicKey, true);
SecretKey agreeKey = keyAgreement.generateSecret(CryptoProvider.GOST_CIPHER_NAME);
// Экспорт сессионного ключа.
Cipher keyCipher = Cipher.getInstance(WRAP_KEY_ALG, PROVIDER);
keyCipher.init(Cipher.WRAP_MODE, agreeKey);
byte[] wrappedSessionKey = keyCipher.wrap(sessionKey);
X509Certificate cert509 = (X509Certificate) recipientCert;
return new SecretKeyForRecipient(wrappedSessionKey, ukm, ephPair.getPublic(), cert509.getSubjectDN().getName());
}
private static class SecretKeyForRecipient {
final byte[] wrappedKey;
final byte[] ukm;
final PublicKey ephPublicKey;
final String certDn;
public SecretKeyForRecipient(byte[] wrappedKey, byte[] ukm, PublicKey ephPublicKey, String certDn) {
this.wrappedKey = wrappedKey;
this.ukm = ukm;
this.ephPublicKey = ephPublicKey;
this.certDn = certDn;
}
}
private EncryptedData encryptData(String data) throws Exception {
KeyGenerator kg = KeyGenerator.getInstance(JCSP.GOST_CIPHER_NAME, PROVIDER);
kg.init(CryptParamsSpec.getInstance(CryptParamsSpec.Rosstandart_TC26_Z));
SecretKey sessionKey = kg.generateKey();
SecureRandom random = SecureRandom.getInstance(JCP.CP_RANDOM, PROVIDER);
byte[] cipherIv = new byte[8];
random.nextBytes(cipherIv);
Cipher cipher = Cipher.getInstance(DATA_CIPHER_ALG, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, sessionKey, new IvParameterSpec(cipherIv));
byte[] encryptedData = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return new EncryptedData(encryptedData, sessionKey, cipherIv);
}
private static class EncryptedData {
final byte[] encryptedData;
final SecretKey sessionKey;
final byte[] iv;
public EncryptedData(byte[] encryptedData, SecretKey sessionKey, byte[] iv) {
this.encryptedData = encryptedData;
this.sessionKey = sessionKey;
this.iv = iv;
}
}
// utils
private String toHexBytesStr(int[] bytes) {
byte[] byteBytes = new byte[bytes.length];
for (int i = 0; i < bytes.length; i++) {
byteBytes[i] = (byte)(bytes[i] & 0xFF);
}
return toHexBytesStr(byteBytes);
}
private String toHexBytesStr(byte[] bytes) {
return toHexBytesStr(bytes, " ");
}
private String toHexBytesStr(byte[] bytes, String delim) {
StringBuilder str = new StringBuilder();
for (byte b : bytes) {
String hexByteStr = Integer.toHexString(((int) b) & 0xFF);
str.append(hexByteStr.length() == 1 ? "0" + hexByteStr : hexByteStr);
str.append(delim);
}
if (str.length() > 0) {
str.setLength(str.length() - delim.length());
}
return str.toString().toUpperCase();
}
private byte[] oidToAsn1SeqBytes(OID oid) throws Exception {
Asn1ObjectIdentifier asn1ObjId = new Asn1ObjectIdentifier(oid.value);
Asn1DerEncodeBuffer encodeBuffer = new Asn1DerEncodeBuffer();
asn1ObjId.encode(encodeBuffer);
byte[] mainBytes = encodeBuffer.getMsgCopy();
encodeBuffer.reset();
encodeBuffer.encodeTagAndLength(Asn1Tag.SEQUENCE, mainBytes.length);
byte[] headerBytes = encodeBuffer.getMsgCopy();
byte[] result = new byte[headerBytes.length + mainBytes.length];
System.arraycopy(headerBytes, 0, result, 0, headerBytes.length);
System.arraycopy(mainBytes, 0, result, headerBytes.length , mainBytes.length);
return result;
}
private PublicKey jspToJcspKey(PublicKey key) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm(), PROVIDER);
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(key.getEncoded());
return keyFactory.generatePublic(pubKeySpec);
}
// certificates and private keys params
@Override
protected String getAlicaAlias() {
return "ce_alica";
}
@Override
protected char[] getAlicaPassword() {
return "ce_alica".toCharArray();
}
@Override
protected String getBobAlias() {
return "ce_bob";
}
@Override
protected char[] getBobPassword() {
return "ce_bob".toCharArray();
}
@Override
public void execute(String pairProvider,
String agreeProvider,
String simProvider,
CryptParamsInterface simParams,
String exchKeyAlgorithm,
String wrapAlgorithm,
String signAlgorithm,
boolean create,
boolean delete) {
}
}
Код:
var CAPICOM_CURRENT_USER_STORE = 2;
var CAPICOM_MY_STORE = "My";
var CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED = 2;
var RECIPIENT_USERNAME = "alica"; // alica или bob
(function Decrypt() {
cadesplugin.async_spawn(function* () {
const data = {
"exportedKey": {
"alica":{"dn":"CN=ce_alica, O=CryptoPro, C=RU","key":"31 2E 32 2E 36 34 33 2E 37 2E 31 2E 32 2E 35 2E 31 2E 31:06 20 00 00 3D 2E 00 00 4D 41 47 31 00 04 00 00 30 15 06 09 2A 85 03 07 01 02 01 02 01 06 08 2A 85 03 07 01 01 02 03 F7 73 5F BF 44 51 E8 61 4A F2 6F F6 BA 12 11 C8 81 B7 DD E9 BC 72 28 03 5F 52 92 A8 DC E9 E5 08 DD 54 FE FA 91 60 34 18 0D 6B FA 5D 3F 01 8F 81 C0 6B 30 C0 D8 0B D3 19 D8 3C 64 CA 8F FE 67 8B 55 0C 1E 34 18 55 2A EC F3 5E 94 2F 1F CC A4 CC EB D3 32 72 23 19 76 B7 1F 21 1A 6F 6D D1 AD 80 A6 01 2C 3E 66 ED 36 73 7F DB A3 97 06 C3 74 67 E2 AD FD 82 D5 DB 00 6A 3F 8D 4E ED CC 64 0D C3:01 20 00 00 1E 66 00 00 FD 51 4A 37 1E 66 00 00 CA 82 F4 5C 67 8B E4 8E 32 40 91 C3 E6 2B 19 BD 59 82 81 CC 2C 7B A1 63 D5 1D 5A 5E FE 28 5F 57 E2 87 50 94 CD E6 BC 7E B9 1B 61 1C 30 0B 06 09 2A 85 03 07 01 02 05 01 01"},
"bob":{"dn":"CN=ce_bob, O=CryptoPro, C=RU","key":"31 2E 32 2E 36 34 33 2E 37 2E 31 2E 32 2E 35 2E 31 2E 31:06 20 00 00 3D 2E 00 00 4D 41 47 31 00 04 00 00 30 15 06 09 2A 85 03 07 01 02 01 02 01 06 08 2A 85 03 07 01 01 02 03 3B 30 90 3D C9 A0 7C 61 D6 33 C9 0E 7D B0 1D 61 3B 18 20 FA 8D 71 36 19 75 BE 23 28 4A 61 DF 0F E0 83 61 F9 BC B6 A4 F1 B1 FF C7 20 7D F4 B6 D9 C4 47 73 0E FD 1F DF BA A8 CB C8 B6 40 3A 60 E4 1E CA 43 5A 05 CD 97 AC 19 B3 18 80 5E B7 D2 8C D1 4D 25 17 95 E6 52 02 FC 21 74 4C 11 0F 4D 1B 14 A6 F4 59 7B F4 AD 34 75 11 5D FD 44 6D EC 37 AC 6F E8 41 3F 52 36 0C 2D CF F4 CC 9E 02 98 FF:01 20 00 00 1E 66 00 00 FD 51 4A 37 1E 66 00 00 07 14 18 83 E1 88 85 34 DF 92 E7 C8 D0 E9 6C 19 25 29 12 E0 E6 16 E0 DF 12 34 C7 02 E2 87 C8 42 4E 54 1F DB D6 3D 7A 04 A3 85 70 2B 30 0B 06 09 2A 85 03 07 01 02 05 01 01"}
},
"encryptedData":"78962F709E1E05BB0D43ADDAC82317B813BBEC7D4F83F86705F25402026BA1AB3EF63DF683FF3D93",
"oSesKeyIV":"A2 93 76 E1 F6 82 CF 6A"
};
const keyData = data.exportedKey[RECIPIENT_USERNAME];
if (!keyData) {
alert("Ключ для пользователя " + RECIPIENT_USERNAME + " не найден");
return;
}
const oStore = yield cadesplugin.CreateObjectAsync("CAdESCOM.Store");
yield oStore.Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STORE, CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED);
const allCertificates = yield oStore.Certificates;
const allCertificatesCount = yield allCertificates.Count;
console.log("Ищем сертификат с DN: '" + keyData.dn + "'");
let certificate = null;
for (let certIdx = 0; certIdx < allCertificatesCount; certIdx++) {
const cert = yield allCertificates.Item(certIdx + 1);
const subjectName = yield cert.SubjectName;
if (subjectName === keyData.dn) {
certificate = cert;
break;
}
}
if (certificate == null) {
alert("Сертификат не найден " + keyData.dn);
return;
}
const oSymAlgo = yield cadesplugin.CreateObjectAsync("CAdESCOM.SymmetricAlgorithm");
yield oSymAlgo.ImportKey(keyData.key, certificate);
yield oSymAlgo.propset_IV(data.oSesKeyIV);
const resultBase64 = yield oSymAlgo.Decrypt(data.encryptedData, 1);
console.log(Base64.decode(resultBase64));
});
})()
|