diff --git a/action/actions.py b/action/actions.py
index 6753c36..62a3a43 100644
--- a/action/actions.py
+++ b/action/actions.py
@@ -2,10 +2,12 @@
 
 from util import mergedict
 import action.user
+import action.sslcert
 import action.repo
 
 
 actions = dict()
 mergedict(actions, action.user.actions, 'user.')
+mergedict(actions, action.sslcert.actions, 'sslcert.')
 mergedict(actions, action.repo.actions, 'repo.')
 
diff --git a/action/sslcert.py b/action/sslcert.py
new file mode 100644
index 0000000..911d172
--- /dev/null
+++ b/action/sslcert.py
@@ -0,0 +1,54 @@
+
+
+import exception
+from action.action import Action
+from action.user import UserBase
+
+
+class SslcertCreate(UserBase):
+  def process(self, request):
+    user_id = self.parse_user_id(request)
+    data = request.model.sslcerts.extract_data( str(request.postvars.get('data', '')) )
+
+    user = request.model.users.get_by_id(user_id)
+    if not user:
+      raise exception.ActionError( request.t('User not found') )
+
+    sslcert = None
+    try:
+      sslcert = request.model.sslcerts.create(user, data)
+    except Exception as e:
+      self.propagate_exception(e)
+
+    request.connection.commit()
+    return self.redirect_to_user(request, user, ['edit'])
+
+
+class SslcertDelete(UserBase):
+  def process(self, request):
+    sslcert_id = 0
+    try:
+      sslcert_id = int(request.postvars.get('sslcert_id', 0))
+    except Exception:
+      raise exception.ActionError( request.t('SSL Certificate Id incorrect') )
+    if not sslcert_id:
+      raise exception.ActionError( request.t('SSL Certificate Id incorrect') )
+
+    sslcert = request.model.sslcerts.get_by_id(sslcert_id)
+    if not sslcert:
+      raise exception.ActionError( request.t('SSL Certificate not found') )
+    user = sslcert.get_user()
+
+    try:
+      sslcert.delete()
+    except Exception as e:
+      self.propagate_exception(e)
+
+    request.connection.commit()
+    return self.redirect_to_user(request, user, ['edit'])
+
+
+actions = {
+  'create' : SslcertCreate(),
+  'delete' : SslcertDelete(),
+}
diff --git a/doc/earthworm.sql b/doc/earthworm.sql
index 4dfef0c..4515300 100644
--- a/doc/earthworm.sql
+++ b/doc/earthworm.sql
@@ -1,11 +1,11 @@
 -- phpMyAdmin SQL Dump
--- version 4.7.0
+-- version 4.8.5
 -- https://www.phpmyadmin.net/
 --
 -- Host: localhost
--- Generation Time: Sep 25, 2019 at 12:10 AM
--- Server version: 10.1.37-MariaDB-0+deb9u1
--- PHP Version: 5.6.36-0+deb8u1
+-- Generation Time: Dec 19, 2019 at 08:09 PM
+-- Server version: 10.1.41-MariaDB-0+deb9u1
+-- PHP Version: 7.0.33-0+deb9u6
 
 SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
 SET AUTOCOMMIT = 0;
@@ -37,10 +37,6 @@ CREATE TABLE `ew_repositories` (
   `description` text COLLATE utf8mb4_unicode_ci NOT NULL
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
 
---
--- Dumping data for table `ew_repositories`
---
-
 -- --------------------------------------------------------
 
 --
@@ -55,10 +51,18 @@ CREATE TABLE `ew_rights` (
   `mode` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
 
+-- --------------------------------------------------------
+
 --
--- Dumping data for table `ew_rights`
+-- Table structure for table `ew_sslcerts`
 --
 
+CREATE TABLE `ew_sslcerts` (
+  `id` int(11) NOT NULL,
+  `user_id` int(11) NOT NULL,
+  `data` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
 -- --------------------------------------------------------
 
 --
@@ -95,10 +99,6 @@ CREATE TABLE `ew_users` (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
 
 --
--- Dumping data for table `ew_users`
---
-
---
 -- Indexes for dumped tables
 --
 
@@ -115,6 +115,12 @@ ALTER TABLE `ew_rights`
   ADD PRIMARY KEY (`id`);
 
 --
+-- Indexes for table `ew_sslcerts`
+--
+ALTER TABLE `ew_sslcerts`
+  ADD PRIMARY KEY (`id`);
+
+--
 -- Indexes for table `ew_test`
 --
 ALTER TABLE `ew_test`
@@ -134,22 +140,32 @@ ALTER TABLE `ew_users`
 -- AUTO_INCREMENT for table `ew_repositories`
 --
 ALTER TABLE `ew_repositories`
-  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=15;
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
 --
 -- AUTO_INCREMENT for table `ew_rights`
 --
 ALTER TABLE `ew_rights`
-  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT for table `ew_sslcerts`
+--
+ALTER TABLE `ew_sslcerts`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
 --
 -- AUTO_INCREMENT for table `ew_test`
 --
 ALTER TABLE `ew_test`
-  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
 --
 -- AUTO_INCREMENT for table `ew_users`
 --
 ALTER TABLE `ew_users`
-  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=38;COMMIT;
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+COMMIT;
 
 /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
 /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
diff --git a/model/model.py b/model/model.py
index dd7876a..ad5b416 100644
--- a/model/model.py
+++ b/model/model.py
@@ -2,6 +2,7 @@
 from model.rights import MyRights
 from model.rights import InternalRights
 from model.users import Users
+from model.sslcerts import Sslcerts
 from model.repositories import Repositories
 
 
@@ -14,6 +15,7 @@ class Model:
     self.translator = translator
     
     self.users = Users(self)
+    self.sslcerts = Sslcerts(self)
     self.repositories = Repositories(self)
 
   def verify_path_entry(self, entry):
diff --git a/model/sslcerts.py b/model/sslcerts.py
new file mode 100644
index 0000000..1afdede
--- /dev/null
+++ b/model/sslcerts.py
@@ -0,0 +1,128 @@
+
+import exception
+
+from model.base import ModelItemBase, ModelManagerBase
+from model.users import User
+
+
+
+class Sslcert(ModelItemBase):
+  def __init__(self, sslcerts, row, user = None):
+    super().__init__(sslcerts, row)
+
+    self.id = int(row['id'])
+    self.user_id = int(row['user_id'])
+    self.data = str(row['data'])
+
+    assert(not user or (type(user) is User and user.id == self.user_id))
+    self.user = user
+
+  def get_user(self):
+    if self.user == None:
+      self.user = self.model.users.get_by_id(self.user_id)
+    assert(self.user)
+    assert(self.user.id == self.user_id)
+    return self.user
+
+  def reset_cache(self):
+    self.manager.reset_cache(self.id, self.data)
+
+  def can_delete(self):
+    return self.user_id == self.rights.user_id or self.rights.issuperuser()
+
+  def delete(self):
+    if self.can_delete():
+      self.connection.execute(
+        'DELETE FROM %T WHERE `id`=%d',
+        self.table(), self.id )
+      self.reset_cache()
+    else:
+      raise exception.ModelDeny()
+
+
+class Sslcerts(ModelManagerBase):
+  def table(self):
+    return 'sslcerts'
+
+  def itemtype(self):
+    return Sslcert
+
+  def reset_cache(self, id, data):
+    super().reset_cache(id)
+    self.connection.cache.reset(self.table(), {'data': data})
+
+  def extract_data(self, data):
+    prefix = '-----BEGIN CERTIFICATE-----'
+    suffix = '-----END CERTIFICATE-----'
+    data = str(data)
+    i0 = data.find(prefix)
+    i1 = data.find(suffix)
+    if i0 >= 0 and i1 >= 0 and i0 + len(prefix) <= i1:
+      data = data[i0 + len(prefix):i1]
+    data = ''.join(data.split())
+    return data
+
+  def verify_data(self, data):
+    b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
+    if not data or not type(data) is str:
+      return False
+    for c in data:
+      if c not in b64chars:
+        return False
+    return True
+
+  def can_read(self, user):
+    assert(type(user) is User)
+    return user.id == self.rights.user_id \
+        or self.rights.issuperuser()
+
+  def can_create(self, user):
+    assert(type(user) is User)
+    return user.id == self.rights.user_id or self.rights.issuperuser()
+
+  def create(self, user, data):
+    if not self.can_create(user):
+      raise exception.ModelDeny()
+    if not self.verify_data(data):
+      raise exception.ModelWrongData(self.t('Bad SSL certificate data'))
+    if self.get_by_data(data):
+      raise exception.ModelWrongData(self.t('SSL Certificate is not unique'))
+
+    self.connection.execute(
+      'INSERT INTO %T SET `user_id`=%d, `data`=%s',
+      self.table(), user.id, data )
+    id = self.connection.insert_id()
+    self.reset_cache(id, data)
+
+    return self.get_by_id(id, user)
+
+  def get_by_id(self, id, user = None):
+    assert(type(id) is int)
+    row = self.connection.cache.row(self.table(), id)
+    if not row:
+      return None
+    if not user:
+      user = self.model.users.get_by_id(row['user_id'])
+      if not user:
+        return None
+    if not self.can_read(user):
+      return None
+    return Sslcert(self, row, user)
+
+  def get_by_data(self, data):
+    assert(type(data) is str)
+    rows = self.connection.cache.select(self.table(), {'data': data})
+    if not rows or len(rows) > 1:
+      return None
+    return Sslcert(self, rows[0])
+
+  def get_list(self, user):
+    assert(type(user) is User)
+    result = list()
+    if not self.can_read(user):
+      return result
+    rows = self.connection.query_dict('SELECT * FROM %T WHERE `user_id`=%d ORDER BY `data`', self.table(), user.id)
+    for row in rows:
+      result.append(Sslcert(self, row, user))
+    return result
+
diff --git a/model/users.py b/model/users.py
index f17c86a..1d6d6d6 100644
--- a/model/users.py
+++ b/model/users.py
@@ -66,6 +66,10 @@ class User(ModelItemBase):
         if hash != self._password:
           raise exception.ModelWrongData(self.t('Password incorrect'))
 
+      sslcerts = self.model.sslcerts.get_list(self)
+      for sslcert in sslcerts:
+        sslcert.delete()
+
       repositories = self.model.repositories.get_list(self)
       for repo in repositories:
         repo.delete()
diff --git a/page/user.py b/page/user.py
index 7f319a0..d6a0c79 100644
--- a/page/user.py
+++ b/page/user.py
@@ -118,7 +118,31 @@ class UserUpdatePage(Page):
     form.add_submit()
     form.end()
     answer.content += form.content
-    
+
+    sslcerts = request.model.sslcerts.get_list(user)
+    for sslcert in sslcerts:
+      maxlen = 64
+      data = sslcert.data
+      html_lines = list()
+      while data:
+        html_lines.append( answer.e(data[0:maxlen]) )
+        data = data[maxlen:]
+      form = Form(request)
+      form.begin('Client SSL certificate', 'sslcert.delete')
+      form.add_hidden('sslcert_id', sslcert.id)
+      form.add_field_raw('data:', '<br />'.join(html_lines))
+      form.add_submit('Delete')
+      form.end()
+      answer.content += form.content
+
+    form = Form(request)
+    form.begin('Add client SSL certificate', 'sslcert.create')
+    form.add_hidden('user_id', user.id)
+    form.add_textarea('data:', 'data')
+    form.add_submit()
+    form.end()
+    answer.content += form.content
+
     if user.id != request.model.myrights.user_id and not user.get_superuser() is None:
       form = Form(request)
       form.begin('Global rights', 'user.setsuperuser')
diff --git a/repoproxy.py b/repoproxy.py
index 6c835df..e1d76d2 100644
--- a/repoproxy.py
+++ b/repoproxy.py
@@ -2,6 +2,7 @@
 
 import base64
 import http.client
+import urllib.parse
 
 import db.holder
 from model.model import Model
@@ -133,6 +134,7 @@ class RepoProxy:
     
     login = ''
     password = ''
+    sslcert = ''
     if 'HTTP_AUTHORIZATION' in request.env:
       try:
         authtype, credentials = str(request.env['HTTP_AUTHORIZATION']).split()
@@ -140,17 +142,27 @@ class RepoProxy:
         login, password = base64.b64decode(credentials).decode('utf8').split(':')
       except Exception:
         return self.unauthorized()
-    
+    elif 'HTTP_X_SSL_CLIENT_CERT' in request.env:
+      sslcert = str(urllib.parse.unquote(request.env['HTTP_X_SSL_CLIENT_CERT']))
+
     url = None
     with db.holder.Holder(request.server.dbpool, readonly = True) as connection:
       request.connection = connection
       request.model = Model(connection, Translator(), 0)
-      
+  
       user = None
+      user_id = None
       if login:
         user_id = request.model.users.check_password(login, password)
         if not user_id:
           return self.unauthorized()
+      elif sslcert:
+        sslcert_pure = request.model.sslcerts.extract_data(sslcert)
+        sslcert_object = request.model.sslcerts.get_by_data(sslcert_pure)
+        if sslcert_object:
+          user_id = sslcert_object.user_id
+
+      if user_id:
         request.model = Model(connection, Translator(), user_id)
         user = request.model.users.get_by_id(user_id)
         assert(user)