← Назад к вопросам

Как работает под капотом плагин КриптоПро?

1.8 Middle🔥 111 комментариев
#Soft Skills и рабочие процессы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

КриптоПро плагин: архитектура и работа

КриптоПро - это российское ПО для работы с электронной подписью и шифрованием. Расскажу как он интегрируется с браузером и как с ним работать на фронтенде.

Что такое КриптоПро

КриптоПро CSP (Cryptography Service Provider) - это криптографический модуль российского производства, основанный на ГОСТ.

// КриптоПро предоставляет:
const cryptoPro = {
  // Криптографические функции
  hashing: 'SHA256, GOST 34.11-94, GOST 34.11-2012',
  
  // Работа с сертификатами
  certificates: 'Хранение, получение, работа с ключами',
  
  // Электронная подпись
  digitalSignature: 'Подпись документов, проверка подписей',
  
  // Шифрование
  encryption: 'Шифрование данных',
  
  // Работа с ключами
  keyManagement: 'Управление ключами в токене/контейнере'
};

Архитектура интеграции

// Слои архитектуры
const architecture = {
  // Слой 1: Браузер
  browser: {
    javascript: 'Ваш код на JavaScript',
    api: 'CadespluginAPI или CADESCOM API'
  },
  
  // Слой 2: NPAPI плагин (старый подход) или ActiveX (IE)
  plugin: {
    npapi: 'Netscape Plugin API',
    activex: 'Microsoft ActiveX (только IE)'
  },
  
  // Слой 3: WebSocket или TCP socket
  communication: 'Локальное соединение с демоном КриптоПро',
  
  // Слой 4: КриптоПро демон
  daemon: {
    service: 'cryptopro.service или cadesplugin.service',
    port: 13579, // Порт по умолчанию
    functionality: 'Работа с сертификатами, подпись, шифрование'
  },
  
  // Слой 5: ОС и аппаратура
  os: {
    hsm: 'Аппаратные токены (рутокены)',
    smartCard: 'Смарт-карты',
    storage: 'Локальное хранилище сертификатов'
  }
};

Инициализация плагина в браузере

// 1. Проверить доступность плагина
const checkCryptoPro = async () => {
  try {
    // Способ 1: КриптоПро 4.0+ (современный)
    if (window.cadesplugin) {
      console.log('Cadesplugin API доступен');
      return true;
    }
    
    // Способ 2: Старый NPAPI плагин
    if (navigator.plugins['CryptoPro']) {
      console.log('NPAPI плагин доступен');
      return true;
    }
    
    // Способ 3: ActiveX (только IE)
    if (new ActiveXObject('CadespluginLocalObj.CadesSigner')) {
      console.log('ActiveX доступен');
      return true;
    }
  } catch (error) {
    console.error('КриптоПро не установлен');
    return false;
  }
};

// 2. Инициализировать
const initCryptoPro = async () => {
  if (!window.cadesplugin) {
    console.error('Плагин не загружен');
    return;
  }
  
  // Активировать плагин
  window.cadesplugin.async_spawn((api) => {
    // api - асинхронный API
    console.log('Плагин инициализирован');
  });
};

Получение сертификатов

const getCertificates = async () => {
  return new Promise((resolve, reject) => {
    // Используем асинхронный API
    window.cadesplugin.async_spawn((api) => {
      try {
        // Получить хранилище сертификатов
        const store = api.CreateObject('CAdESCOM.Store');
        
        // Открыть хранилище текущего пользователя
        store.Open(
          api.CADESCOM_STORE_PROVIDER_TYPE.CADESCOM_PROVIDER_RSA_FULL, 
          api.CADESCOM_STORE_LOCATION_TYPE.CADESCOM_CURRENT_USER,
          api.CADESCOM_STORE_OPEN_MODE.CADESCOM_STORE_OPEN_EXISTING_ONLY
        );
        
        // Получить сертификаты
        const certs = store.Certificates;
        const certArray = [];
        
        for (let i = 1; i <= certs.Count; i++) {
          const cert = certs.Item(i);
          
          certArray.push({
            thumbprint: cert.Thumbprint,
            subjectName: cert.SubjectName,
            issuerName: cert.IssuerName,
            validFrom: cert.ValidFromDate,
            validTo: cert.ValidToDate,
            publicKey: cert.PublicKey(),
            hasPrivateKey: cert.HasPrivateKey
          });
        }
        
        resolve(certArray);
      } catch (error) {
        reject(new Error(`Ошибка получения сертификатов: ${error.message}`));
      }
    });
  });
};

// Использование
const certs = await getCertificates();
certs.forEach(cert => {
  console.log(`${cert.subjectName} (до ${cert.validTo})`);
});

Подпись документа

const signDocument = async (data, thumbprint) => {
  return new Promise((resolve, reject) => {
    window.cadesplugin.async_spawn((api) => {
      try {
        // 1. Получить сертификат по отпечатку
        const store = api.CreateObject('CAdESCOM.Store');
        store.Open(
          api.CADESCOM_STORE_PROVIDER_TYPE.CADESCOM_PROVIDER_RSA_FULL,
          api.CADESCOM_STORE_LOCATION_TYPE.CADESCOM_CURRENT_USER,
          api.CADESCOM_STORE_OPEN_MODE.CADESCOM_STORE_OPEN_EXISTING_ONLY
        );
        
        const certs = store.Certificates;
        let signingCert = null;
        
        for (let i = 1; i <= certs.Count; i++) {
          const cert = certs.Item(i);
          if (cert.Thumbprint === thumbprint) {
            signingCert = cert;
            break;
          }
        }
        
        if (!signingCert) {
          reject(new Error('Сертификат не найден'));
          return;
        }
        
        // 2. Создать объект для подписи
        const signer = api.CreateObject('CAdESCOM.CadesSigner');
        signer.Certificate = signingCert;
        
        // 3. Установить параметры подписи
        signer.TSAAddress = 'http://timestamp.cryptopro.ru'; // Временная метка
        signer.SigningAlgorithm = api.CADESCOM_ENCRYPTION_ALGORITHM.CADESCOM_ENCRYPTION_ALGORITHM_RSA_PSS;
        signer.HashAlgorithm = api.CADESCOM_HASH_ALGORITHM.CADESCOM_HASH_ALGORITHM_SHA256;
        
        // 4. Подписать данные
        const cadesSignedData = api.CreateObject('CAdESCOM.CadesSignedData');
        cadesSignedData.ContentEncoding = api.CADESCOM_BASE64_TO_BINARY;
        cadesSignedData.Content = data; // Base64 строка данных
        
        // CAdES-BES формат (Basic Electronic Signature)
        const signature = cadesSignedData.SignCades(
          signer,
          api.CADESCOM_CADES_TYPE.CADESCOM_CADES_BES
        );
        
        resolve(signature);
      } catch (error) {
        reject(new Error(`Ошибка подписи: ${error.message}`));
      }
    });
  });
};

// Использование
const documentData = btoa('Hello World'); // Base64 encoded
const selectedCertThumbprint = 'ABC123...';
const signature = await signDocument(documentData, selectedCertThumbprint);
console.log('Подпись:', signature);

Проверка подписи

const verifySignature = async (signature) => {
  return new Promise((resolve, reject) => {
    window.cadesplugin.async_spawn((api) => {
      try {
        const cadesSignedData = api.CreateObject('CAdESCOM.CadesSignedData');
        cadesSignedData.ContentEncoding = api.CADESCOM_BASE64_TO_BINARY;
        cadesSignedData.Content = signature;
        
        // Проверить подпись
        const signers = cadesSignedData.Signers;
        
        if (signers.Count === 0) {
          reject(new Error('В подписи нет подписантов'));
          return;
        }
        
        const signer = signers.Item(1);
        const cert = signer.Certificate;
        
        // Проверить сертификат
        const isValid = cadesSignedData.VerifyCades(
          signature,
          api.CADESCOM_CADES_TYPE.CADESCOM_CADES_BES,
          true // checkChain
        );
        
        resolve({
          isValid: isValid,
          signerName: cert.SubjectName,
          signingTime: signer.SigningTime
        });
      } catch (error) {
        reject(new Error(`Ошибка проверки: ${error.message}`));
      }
    });
  });
};

Обработка ошибок и статусы

const cryptoProStatuses = {
  NOT_INSTALLED: 'КриптоПро не установлен',
  NOT_RUNNING: 'Демон КриптоПро не запущен',
  NO_CERTIFICATES: 'Сертификаты не установлены',
  PLUGIN_ERROR: 'Ошибка плагина',
  NO_PIN_CODE: 'Необходимо ввести PIN-код (для токена)',
  USER_CANCELLED: 'Пользователь отменил операцию',
  INVALID_SIGNATURE: 'Подпись невалидна',
  EXPIRED_CERTIFICATE: 'Сертификат истёк'
};

// Обработка ошибок
const safeSignDocument = async (data, thumbprint) => {
  try {
    const signature = await signDocument(data, thumbprint);
    return { success: true, signature };
  } catch (error) {
    const errorMessage = error.message;
    
    if (errorMessage.includes('plugin is not available')) {
      return { success: false, error: cryptoProStatuses.NOT_INSTALLED };
    } else if (errorMessage.includes('pin')) {
      return { success: false, error: cryptoProStatuses.NO_PIN_CODE };
    } else {
      return { success: false, error: errorMessage };
    }
  }
};

Интеграция в веб-приложение

// React компонент для подписи
function DocumentSigner({ document }) {
  const [certificates, setCertificates] = useState([]);
  const [selectedCert, setSelectedCert] = useState(null);
  const [signature, setSignature] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    const loadCerts = async () => {
      try {
        setLoading(true);
        const certs = await getCertificates();
        setCertificates(certs);
      } catch (err) {
        setError('Не удалось загрузить сертификаты: ' + err.message);
      } finally {
        setLoading(false);
      }
    };
    
    loadCerts();
  }, []);
  
  const handleSign = async () => {
    if (!selectedCert) {
      setError('Выберите сертификат');
      return;
    }
    
    try {
      setLoading(true);
      const docBase64 = btoa(JSON.stringify(document));
      const sig = await signDocument(docBase64, selectedCert.thumbprint);
      setSignature(sig);
      setError(null);
    } catch (err) {
      setError('Ошибка подписи: ' + err.message);
    } finally {
      setLoading(false);
    }
  };
  
  return (
    <div>
      <select onChange={(e) => setSelectedCert(
        certificates.find(c => c.thumbprint === e.target.value)
      )}>
        <option>Выберите сертификат</option>
        {certificates.map(cert => (
          <option key={cert.thumbprint} value={cert.thumbprint}>
            {cert.subjectName}
          </option>
        ))}
      </select>
      <button onClick={handleSign} disabled={loading || !selectedCert}>
        {loading ? 'Подписание...' : 'Подписать'}
      </button>
      {error && <div className="error">{error}</div>}
      {signature && <div className="signature">Подпись получена</div>}
    </div>
  );
}

Выводы

  1. Архитектура: браузер -> плагин -> демон КриптоПро -> ОС
  2. API: используй cadesplugin.async_spawn для асинхронных операций
  3. Сертификаты: хранятся локально, плагин предоставляет доступ
  4. Подпись: CAdES-BES формат с временной меткой
  5. Безопасность: приватный ключ никогда не покидает локальное хранилище
  6. Совместимость: нужно проверять наличие плагина перед использованием
  7. Ошибки: обработать случаи когда плагин не установлен или демон не запущен