Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@islom929/react-eimzo

npm version npm downloads license

React hook for E-IMZO digital signatures. Simple API, zero UI dependencies — works with any component library.

Install

npm install @islom929/react-eimzo

No additional setup required. SDK is bundled and auto-injected.

Quick Start

1. Wrap your app with EimzoProvider

import { EimzoProvider } from '@islom929/react-eimzo'

function App() {
  return (
    <EimzoProvider
      apiKeys={['yourdomain.uz', 'YOUR_API_KEY_HERE']}
    >
      <YourApp />
    </EimzoProvider>
  )
}

Default keys for localhost and 127.0.0.1 are always included.

2. Use the hook

import { useEimzo } from '@islom929/react-eimzo'
import type { ICertificate } from '@islom929/react-eimzo'

function SignDocument() {
  const { sign, loadKeys, keyList, isInstalled, isLoading } = useEimzo()

  const handleSign = (cert: ICertificate) => {
    sign({
      keyId: cert,
      data: JSON.stringify({ document: 'content' }),
      onSuccess: (pkcs7) => {
        console.log('Signed:', pkcs7)
      },
      onError: (err) => {
        console.error('Error:', err)
      },
    })
  }

  return (
    <div>
      <button
        onClick={() => void loadKeys()}
        disabled={!isInstalled || isLoading}
      >
        Load keys
      </button>

      {keyList.map((cert) => (
        <button key={cert.serialNumber} onClick={() => handleSign(cert)}>
          {cert.CN}
        </button>
      ))}
    </div>
  )
}

Usage Examples

Sign with PFX certificate

User selects a certificate from the list. E-IMZO app prompts for password.

import { useEimzo } from '@islom929/react-eimzo'
import type { ICertificate } from '@islom929/react-eimzo'

function PfxSign() {
  const { sign, loadKeys, keyList, isInstalled, isLoading } = useEimzo()
  const [result, setResult] = useState('')

  useEffect(() => {
    if (isInstalled) loadKeys()
  }, [isInstalled])

  const handleSign = (cert: ICertificate) => {
    sign({
      keyId: cert,
      data: JSON.stringify({ orderId: 123, amount: 50000 }),
      verifyPassword: true,
      onSuccess: (pkcs7) => {
        setResult(pkcs7)
        // Send to backend
        fetch('/api/verify', {
          method: 'POST',
          body: JSON.stringify({ pkcs7 }),
        })
      },
      onError: (err) => {
        alert(err) // "Ввод пароля отменен" if user cancels
      },
    })
  }

  return (
    <div>
      <h3>Select certificate:</h3>
      {keyList.map((cert, i) => (
        <div key={`${cert.serialNumber}-${i}`}>
          <p>{cert.CN}{cert.O}</p>
          <p>PINFL: {cert.PINFL} | STIR: {cert.TIN}</p>
          <p>Valid until: {new Date(cert.validTo).toLocaleDateString()}</p>
          <button
            onClick={() => handleSign(cert)}
            disabled={cert.expired || isLoading}
          >
            {cert.expired ? 'Expired' : 'Sign'}
          </button>
        </div>
      ))}
    </div>
  )
}

Sign with tokens

No certificate selection needed. Pass device type directly. Device status is checked automatically on mount.

function TokenSign() {
  const { sign, deviceStatus, isLoading } = useEimzo()

  const handleTokenSign = (device: 'idcard' | 'baikey' | 'ckc') => {
    sign({
      keyId: device,
      data: JSON.stringify({ document: 'content' }),
      onSuccess: (pkcs7) => console.log('Signed:', pkcs7),
      onError: (err) => console.error(err),
    })
  }

  return (
    <div>
      <button
        onClick={() => handleTokenSign('idcard')}
        disabled={!deviceStatus.idcard || isLoading}
      >
        ID Card {deviceStatus.idcard ? '(connected)' : '(not connected)'}
      </button>

      <button
        onClick={() => handleTokenSign('baikey')}
        disabled={!deviceStatus.baikey || isLoading}
      >
        BAIK Token {deviceStatus.baikey ? '(connected)' : '(not connected)'}
      </button>

      <button
        onClick={() => handleTokenSign('ckc')}
        disabled={!deviceStatus.ckc || isLoading}
      >
        CKC {deviceStatus.ckc ? '(connected)' : '(not connected)'}
      </button>
    </div>
  )
}

Error handling

function EimzoStatus() {
  const { isInstalled, error, version } = useEimzo()

  if (error) {
    return (
      <div>
        <p style={{ color: 'red' }}>{error}</p>
        <a href="https://e-imzo.uz/main/downloads/">Download E-IMZO</a>
      </div>
    )
  }

  if (!isInstalled) {
    return <p>Loading...</p>
  }

  return <p>E-IMZO v{version?.major}.{version?.minor} ready</p>
}

Error handling in sign

Errors from E-IMZO SDK are passed directly to onError. Common errors:

sign({
  keyId: cert,
  data: '...',
  onSuccess: (pkcs7) => { /* success */ },
  onError: (err) => {
    // err contains the SDK error message:
    // "Ввод пароля отменен" — user cancelled password dialog
    // "BadPaddingException" — wrong password
    // Other SDK-native errors
    console.error(err)
  },
})

Possible errors

When Error Description
Install "E-IMZO не запущен. Убедитесь, что приложение E-IMZO установлено и запущено." E-IMZO app is not running or not installed
Install "Версия E-IMZO устарела (X.XX). Минимальная версия: 3.36" E-IMZO version is too old
loadKeys "Please install new version of E-IMZO" SDK version check failed
sign "Ввод пароля отменен" User cancelled password dialog
sign "BadPaddingException" Wrong password entered
sign WebSocket error code (number) Connection to E-IMZO lost

Version check

The package automatically checks E-IMZO version on mount. Minimum required version is 3.36.

function VersionInfo() {
  const { version, isInstalled } = useEimzo()

  if (!isInstalled || !version) return null

  const v = parseInt(version.major) * 100 + parseInt(version.minor)

  return (
    <div>
      <p>E-IMZO v{version.major}.{version.minor}</p>
      <p>PFX: supported</p>
      <p>ID-card: {v >= 412 ? 'supported' : 'not supported (need v4.12+)'}</p>
      <p>BAIK/CKC: {v >= 486 ? 'supported' : 'not supported (need v4.86+)'}</p>
    </div>
  )
}

E-IMZO version determines which key types are available:

Version PFX ID-card BAIK CKC
v3.36+ yes no no no
v4.12+ yes yes no no
v4.86+ yes yes yes yes

API

EimzoProvider

Wraps your app. Initializes E-IMZO SDK automatically.

Prop Type Description
apiKeys string[] Optional. Additional domain + API key pairs
children ReactNode Required

useEimzo()

Property Type Description
isInstalled boolean E-IMZO app detected and running
isLoading boolean Key loading, preparation, or signing is in progress
error string | null Latest E-IMZO operation error, null if OK
version IEimzoVersion | null E-IMZO version { major, minor }
keyList ICertificate[] Available certificates
deviceStatus IDeviceStatus Connected hardware devices
loadKeys (options?) => Promise<void> Load certificates into keyList; cached unless force is true
reloadKeys (options?) => Promise<void> Force-refresh certificates; rejects if the refresh fails
prepareKey (certificate, verifyPassword?) => Promise<string> Load a certificate and optionally verify its password/PIN
signAsync (params) => Promise<string> Promise-first signing API; resolves with PKCS#7
sign (params: ISignParams) => void Sign data

Device status (idcard, baikey, ckc) is probed in the background once installation succeeds. A slow or unavailable hardware probe does not block PFX key loading or signing.

sign(params)

Param Type Description
keyId ICertificate | string Certificate object or 'idcard' / 'baikey' / 'ckc'
data string Data to sign (usually JSON.stringify)
verifyPassword boolean Optional. Verify PFX password or FTJC PIN before signing
onSuccess (pkcs7: string) => void Called with base64 PKCS#7 signature
onError (error: string) => void Optional. Called on failure

signAsync(params)

Use signAsync when the surrounding code already uses async/await:

const { signAsync } = useEimzo()

const pkcs7 = await signAsync({
  keyId: certificate,
  data: JSON.stringify(payload),
  verifyPassword: true,
})

Use prepareKey(certificate, true) when password/PIN verification must happen before another operation, such as requesting a short-lived server challenge. Pass the returned key ID string to signAsync.

loadKeys({ includeLegacyTokens: true }) includes legacy FTJC certificates. Use reloadKeys(...) when a certificate or token may have been attached after the first list operation.

v0.6 security compatibility

Starting with v0.6, the bundled SDK no longer modifies native Date.prototype or String.prototype objects. The documented React API (EimzoProvider, useEimzo, loadKeys, and sign) is unchanged.

Applications that used undocumented SDK globals directly must migrate:

  • Use an application-local date formatter instead of legacy date extensions.
  • Use Base64.encode(value) / Base64.decode(value) instead of legacy string extensions.
  • The legacy certificate-field splitting extension was internal and has no public replacement.

Behavior

Install is deduplicated per page load. Parallel mounts (including React StrictMode double-invocation) share one installation promise. A failed attempt is cleared, so a later key load can retry after the user starts E-IMZO.

loadKeys() is cached. Once the first successful call populates keyList, later calls resolve immediately. Use reloadKeys() or loadKeys({ force: true }) for a fresh device/certificate list. Requesting includeLegacyTokens: true also refreshes a cache that only contains PFX certificates. For compatibility with v0.5, loadKeys() records an error but resolves after a failed load; the new reloadKeys() API rejects so callers can handle refresh failures explicitly.

apiKeys are managed as domain/key pairs. Reusing a domain during a later provider initialization updates its key instead of corrupting the flat SDK key list. Treat apiKeys as initialization configuration rather than a frequently changing prop.

Types

import type {
  ICertificate,
  ILoadKeysOptions,
  ISignAsyncParams,
  ISignParams,
  IDeviceStatus,
  IEimzoContext,
  IEimzoProviderProps,
  IEimzoVersion,
  TKeyType,
} from '@islom929/react-eimzo'

IEimzoVersion

Field Type Description
major string Major version (e.g. "4")
minor string Minor version (e.g. "86")

ICertificate

Field Type Description
CN string Full name
PINFL string Personal ID number
TIN string Tax ID (STIR)
O string Organization
T string Title/Position
UID string User ID
serialNumber string Certificate serial number
validFrom Date Start of validity
validTo Date End of validity
type 'pfx' | 'ftjc' Certificate type
expired boolean Whether certificate has expired

IDeviceStatus

Field Type Description
idcard boolean ID card / EIMZO-Token connected
baikey boolean BAIK-Token connected
ckc boolean CKC device connected

Supported Key Types

Type Description E-IMZO Version
PFX Local certificate file (ERI) v3.36+
ID-card / EIMZO-Token Physical smart card v4.12+
BAIK-Token BAIK hardware token v4.86+
CKC CryptKeyContainer (universal) v4.86+

How It Works

Your React App
    ↓ useEimzo()
@islom929/react-eimzo
    ↓ WebSocket (wss://127.0.0.1:64443)
E-IMZO Desktop App
    ↓
PFX files / USB tokens / ID cards
  1. Package injects E-IMZO SDK into the page automatically
  2. SDK connects to E-IMZO desktop app via WebSocket
  3. loadKeys() fetches available certificates
  4. sign() sends data to E-IMZO app for signing
  5. E-IMZO app prompts user for password/PIN
  6. Signed PKCS#7 (base64) returned via onSuccess

Requirements

License

MIT

About

React hook for E-IMZO, Uzbekistan's national digital signature system. WebSocket-based, supports PFX certificates and hardware tokens, version-aware, StrictMode-safe. Zero UI dependencies.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages