Retiring Project

http://lists.openstack.org/pipermail/openstack-sigs/2018-August/000481.html

Depends-On: 90ca23f2ef5bf2cfdaf63552a7d8d8be325a03e6

Change-Id: I9ebc8cfcbb8906e9c4e1fd9e91205fe364bdc3c9
This commit is contained in:
lhinds 2018-08-28 09:33:12 +01:00
parent 0ecce126dc
commit 93aacb43e6
111 changed files with 8 additions and 17880 deletions

View File

@ -1,7 +0,0 @@
[run]
branch = True
[report]
exclude_lines =
pragma: no cover
raise NotImplementedError

29
.gitignore vendored
View File

@ -1,29 +0,0 @@
*.pyc
temp-*.crt
config.cfg
.venv
*.sw[op]
certs/*.crt
dist/*
build/*
.tox/*
.DS_Store
*.egg
*.egg-info
.testrepository
build/*
cover/*
.cover
.coverage
doc/build
.eggs/
pep8.txt
AUTHORS
ChangeLog
# mentioned in README for test/bootstrap
anchor-test.example.com.key
anchor-test.example.com.csr
CA/serial
CA/*.key
CA/*.crt

View File

@ -1,7 +0,0 @@
[DEFAULT]
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
${PYTHON:-python} -m subunit.run discover -t ./ ./tests $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

View File

@ -1,17 +0,0 @@
This project is part of the OpenStack / Stackforge family. If you would like to
contribute to the development, you must follow the steps in this page:
http://docs.openstack.org/infra/manual/developers.html
Once those steps have been completed, changes to OpenStack should be submitted
for review via the Gerrit tool, following the workflow documented at:
http://docs.openstack.org/infra/manual/developers.html#development-workflow
(in short - install git-review package, then submit changes via `git review`)
Pull requests submitted through GitHub will be ignored.
Bugs should be filed on Launchpad, not GitHub:
https://bugs.launchpad.net/anchor

View File

@ -1,6 +0,0 @@
FROM python:2.7
RUN pip install pecan
ADD . /code
WORKDIR /code
RUN pip install -e .
ENTRYPOINT ["python","bin/container_bootstrap.py"]

View File

@ -1,358 +1,10 @@
Anchor
======
This project is no longer maintained.
.. image:: https://img.shields.io/pypi/v/anchor.svg
:target: https://pypi.python.org/pypi/anchor/
:alt: Latest Version
The contents of this repository are still available in the Git
source code management system. To see the contents of this
repository before it reached its end of life, please check out the
previous commit with "git checkout HEAD^1".
.. image:: https://img.shields.io/pypi/pyversions/anchor.svg
:target: https://pypi.python.org/pypi/anchor/
:alt: Python Versions
.. image:: https://img.shields.io/pypi/format/anchor.svg
:target: https://pypi.python.org/pypi/anchor/
:alt: Format
.. image:: https://img.shields.io/badge/license-Apache%202-blue.svg
:target: https://git.openstack.org/cgit/openstack/anchor/plain/LICENSE
:alt: License
Anchor is an ephemeral PKI service that, based on certain conditions,
automates the verification of CSRs and signs certificates for clients.
The validity period can be set in the config file with hour resolution.
Ideas behind Anchor
===================
A critical capability within PKI is to revoke a certificate - to ensure
that it is no longer trusted by any peer. Unfortunately research has
demonstrated that the two typical methods of revocation (Certificate
Revocation Lists and Online Certificate Status Protocol) both have
failings that make them unreliable, especially when attempting to
leverage PKI outside of web-browser software.
Through the use of short-lifetime certificates Anchor introduces the
concept of "passive revocation". By issuing certificates with lifetimes
measured in hours, revocation can be achieved by simply not re-issuing
certificates to clients.
The benefits of using Anchor instead of manual long-term certificates
are:
* quick certificate revoking / rotation
* always tested certificate update mechanism (used daily)
* easy integration with certmonger for service restarting
* certificates are signed only when validation is passed
* signing certificates follows consistent process
Installation
============
In order to install Anchor from source, the following system
dependencies need to be present:
* python 2.7
* python (dev files)
* libffi (dev)
* libssl (dev)
When everything is in place, Anchor can be installed in one of three
ways: a local development instance in a python virtual environment, a local
production instance or a test instance in a docker container.
For a development instance with virtualenv, run:
virtualenv .venv && source .venv/bin/activate && pip install .
For installing in production, either install a perpared system package,
or install globally in the system:
python setup.py install
Running the service
===================
In order to run the service, it needs to be started via the `pecan`
application server. The only extra parameter is a config file:
pecan serve anchor/config.py
For development, an additional `--reload` parameter may be used. It will
cause the service to reload every time a source file is changed, however
it requires installing an additional `watchdog` python module.
In the default configuration, Anchor will wait for web requests on port
5016 on local network interface. This can be adjusted in the `config.py`
file.
Preparing a test environment
============================
In order to test Anchor with the default configuration, the following
can be done to create a test CA. The test certificate can be then used
to sign the new certificates.
openssl req -out CA/root-ca.crt -keyout CA/root-ca-unwrapped.key \
-newkey rsa:4096 -subj "/CN=Anchor Test CA" -nodes -x509 -days 365 \
-sha256
chmod 0400 CA/root-ca-unwrapped.key
Next, a new certificate request may be generated:
openssl req -out anchor-test.example.com.csr -nodes \
-keyout anchor-test.example.com.key -newkey rsa:2048 \
-subj "/CN=anchor-test.example.com" -sha256
That reqest can be submitted using curl (while `pecan serve config.py`
is running):
curl http://0.0.0.0:5016/v1/sign/default -F user='myusername' \
-F secret='simplepassword' -F encoding=pem \
-F 'csr=<anchor-test.example.com.csr'
This will result in the signed request being created in the `certs`
directory.
Docker test environment
=======================
We have published a docker image for anchor at
https://hub.docker.com/r/openstacksecurity/anchor/ These instructions expect
the reader to have a working Docker install already. Docker should *not* be
used to serve Anchor in any production environments.
The behaviour of the Anchor container is controlled through docker volumes. To
run a plain version of Anchor, with a default configuration and a dynamically
generated private key simply invoke the container without any volumes. Note
that Anchor exposes port 5016:
docker run -p 5016:5016 openstacksecurity/anchor
The recommended way to use the anchor container is to use a pre-compiled private
key and certificate. You can read more about generating these (if you do not
already have them) in this readme.
Once a key and certificate have been created, they can be provided to Anchor
using docker volumes. In this example we've stored the sensitive data in
/var/keys (note, docker must be able to access the folder where you have stored
your keys). When the container starts it looks for a mounted volume in '/key'
and files called root-ca-unwrapped.key and root-ca.crt that it will use.
docker run -p 5016:5016 -v /var/keys:/key anchor
Anchor is highly configurable, you can read more about Anchor configuration in
the documentation here:
http://docs.openstack.org/developer/anchor/configuration.html the method for
exposing configuration to Anchor is very similar as for keys, simply provide
docker with the folder the config.json is within and create a volume called
/config In the below example, Anchor will start with a custom configuration but
as no key was provided it will generate one on the fly.
docker run -p 5016:5016 -v /var/config:/config anchor
Obviously it's possible to run Anchor with a custom configuration and a custom
key/certificate by running the following (note in this case we've used -d to
detach the container from our terminal)
docker run -d -p 5016:5016 -v /var/config:/config -v /var/keys:/key anchor
If you prefer to use locally built containers or want to modify the container
build you can do that, we provide a simple Dockerfile to make the process
easier.
Assuming you are already in the anchor directory, build a container
called 'anchor' that runs the anchor service, with any local changes
that have been made in the repo:
docker build -t anchor .
To start the service in the container and serve Anchor on port 5016:
docker run -p 5016:5016 anchor
When Anchor is running in a container, certificate requests will not pass
validation unless the docker network is added as a source_cidr in the Anchor
configuration and then passed into the container. Find the network by starting
the container, inspecting the docker network and finding the anchor container:
docker run -p 5016:5016 --name=anchor anchor
docker network inspect bridge
Under the 'containers' section, find the 'anchor' container and find the
IPv4Address. For example:
"Containers": {
"6998a....5f4a57": {
"Name": "anchor",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
Add this network as a source_cidr to the config.json, and pass it to the
docker container as described above.
Running Anchor in production
============================
Anchor shouldn't be exposed directly to the network. It's running via an
application server (Pecan) and doesn't have all the features you'd
normally expect from a http proxy - for example dealing well with
deliberately slow connections, or using multiple workers. Anchor can
however be run in production using a better frontend.
To run Anchor using uwsgi you can use the following command:
uwsgi --http-socket :5016 --venv path/to/venv --pecan config.py -p 4
In case a more complex scripted configuration is needed, for example to
handle custom headers, rate limiting, or source filtering a complete
HTTP proxy like Nginx may be needed. This is however out of scope for
Anchor project. You can read more about production deployment in
`Pecan documentation <http://pecan.readthedocs.org/en/latest/deployment.html>`_.
Additionally, using an AppArmor profile for Anchor is a good idea to
prevent exploits relying on one of the native libraries used by Anchor
(for example OpenSSL). This can be done with sample profiles which you
can find in the `tools/apparmor.anchor_*` files. The used file needs to
be reviewed and updated with the right paths depending on the deployment
location.
Validators
==========
One of the main features of Anchor are the validators which make sure
that all requests match a given set of rules. They're configured in
`config.json` and the sample configuration includes a few of them.
Each validator takes a dictionary of options which provide the specific
matching conditions.
Currently available validators are:
* `common_name` ensures CN matches one of names in `allowed_domains` or
ranges in `allowed_networks`
* `alternative_names` ensures alternative names match one of the names
in `allowed_domains`
* `alternative_names_ip` ensures alternative names match one of the
names in `allowed_domains` or IP ranges in `allowed_networks`
* `blacklist_names` ensures CN and alternative names do not contain any
of the configured `domains`
* `server_group` ensures the group the requester is contained within
`group_prefixes`
* `extensions` ensures only `allowed_extensions` are present in the
request
* `key_usage` ensures only `allowed_usage` is requested for the
certificate
* `ca_status` ensures the request does/doesn't require the CA flag
* `source_cidrs` ensures the request comes from one of the ranges in
`cidrs`
A configuration entry for a validator might look like one from the
sample config:
"key_usage": {
"allowed_usage": [
"Digital Signature",
"Key Encipherment",
"Non Repudiation"
]
}
Authentication
==============
Anchor can use one of the following authentication modules: static,
keystone, ldap.
Static: Username and password are present in `config.json`. This mode
should be used only for development and testing.
"auth": {
"static": {
"secret": "simplepassword",
"user": "myusername"
}
}
Keystone: Username is ignored, but password is a token valid in the
configured keystone location.
"auth": {
"keystone": {
"url": "https://keystone.example.com"
}
}
LDAP: Username and password are used to bind to an LDAP user in a
configured domain. User's groups for the `server_group` filter are
retrieved from attribute `memberOf` in search for
`(sAMAccountName=username@domain)`. The search is done in the configured
base.
"auth": {
"ldap": {
"host": "ldap.example.com",
"base": "ou=Users,dc=example,dc=com",
"domain": "example.com"
"port": 636,
"ssl": true
}
}
Signing backends
================
Anchor allows the use of configurable signing backend. Currently it provides two
implementation: one based on cryptography.io ("anchor"), the other using PKCS#11
libraries ("pkcs11"). The first one is used in the sample config. Other backends
may have extra dependencies: pkcs11 requires the PyKCS11 module, not required by
anchor by default.
The resulting certificate is stored locally if the `output_path` is set
to any string. This does not depend on the configured backend.
Backends can specify their own options - please refer to the backend
documentation for the specific list. The default backend takes the
following options:
* `cert_path`: path where local CA certificate can be found
* `key_path`: path to the key for that certificate
* `signing_hash`: which hash method to use when producing signatures
* `valid_hours`: number of hours the signed certificates are valid for
Sample configuration for the default backend:
"ca": {
"backend": "anchor"
"cert_path": "CA/root-ca.crt",
"key_path": "CA/root-ca-unwrapped.key",
"output_path": "certs",
"signing_hash": "sha256",
"valid_hours": 24
}
Other backends may be created too. For more information, please refer to the
documentation.
Fixups
======
Anchor can modify the submitted CSRs in order to enforce some rules,
remove deprecated elements, or just add information. Submitted CSR may
be modified or entirely redone. Fixup are loaded from "anchor.fixups"
namespace and can take parameters just like validators.
Reporting bugs and contributing
===============================
For bug reporting and contributing, please check the CONTRIBUTING.rst
file.
For any further questions, please email
openstack-dev@lists.openstack.org or join #openstack-dev on
Freenode.

View File

@ -1,284 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import base64
import binascii
import io
from cryptography.hazmat import backends as cio_backends
from cryptography.hazmat.primitives import hashes
from pyasn1.codec.ber import encoder as ber_encoder
from pyasn1.codec.der import decoder
from pyasn1.codec.der import encoder
from pyasn1.type import univ as asn1_univ
from pyasn1_modules import pem
from anchor.asn1 import rfc5280
from anchor.X509 import errors
from anchor.X509 import extension
from anchor.X509 import name
from anchor.X509 import signature
from anchor.X509 import utils
SIGNING_ALGORITHMS = {
('RSA', 'SHA224'): asn1_univ.ObjectIdentifier('1.2.840.113549.1.1.14'),
('RSA', 'SHA256'): asn1_univ.ObjectIdentifier('1.2.840.113549.1.1.11'),
('RSA', 'SHA384'): asn1_univ.ObjectIdentifier('1.2.840.113549.1.1.12'),
('RSA', 'SHA512'): asn1_univ.ObjectIdentifier('1.2.840.113549.1.1.13'),
('DSA', 'SHA224'): asn1_univ.ObjectIdentifier('2.16.840.1.101.3.4.3.1'),
('DSA', 'SHA256'): asn1_univ.ObjectIdentifier('2.16.840.1.101.3.4.3.2'),
}
SIGNING_ALGORITHMS_INV = dict((v, k) for k, v in SIGNING_ALGORITHMS.items())
class X509CertificateError(errors.X509Error):
"""Specific error for X509 certificate operations."""
pass
class X509Certificate(signature.SignatureMixin):
"""X509 certificate class."""
def __init__(self, certificate=None):
if certificate is None:
self._cert = rfc5280.Certificate()
self._cert['tbsCertificate'] = rfc5280.TBSCertificate()
else:
self._cert = certificate
@staticmethod
def from_open_file(f):
try:
der_content = pem.readPemFromFile(f)
certificate = decoder.decode(der_content,
asn1Spec=rfc5280.Certificate())[0]
return X509Certificate(certificate)
except Exception:
raise X509CertificateError("Could not read X509 certificate from "
"PEM data.")
@staticmethod
def from_buffer(data):
"""Build this X509 object from a data buffer in memory.
:param data: A data buffer
"""
return X509Certificate.from_open_file(io.StringIO(data))
@staticmethod
def from_file(path):
"""Build this X509 certificate object from a data file on disk.
:param path: A data buffer
"""
with open(path, 'r') as f:
return X509Certificate.from_open_file(f)
def as_pem(self):
"""Serialise this X509 certificate object as PEM string."""
header = '-----BEGIN CERTIFICATE-----'
footer = '-----END CERTIFICATE-----'
der_cert = encoder.encode(self._cert)
b64_encoder = (base64.encodestring if str is bytes else
base64.encodebytes)
b64_cert = b64_encoder(der_cert).decode('ascii')
return "%s\n%s%s\n" % (header, b64_cert, footer)
def set_version(self, v):
"""Set the version of this X509 certificate object.
:param v: The version
"""
self._cert['tbsCertificate']['version'] = v
def get_version(self):
"""Get the version of this X509 certificate object."""
return self._cert['tbsCertificate']['version']
def get_validity(self):
if self._cert['tbsCertificate']['validity'] is None:
self._cert['tbsCertificate']['validity'] = None
return self._cert['tbsCertificate']['validity']
def set_not_before(self, t):
"""Set the 'not before' date field.
:param t: time in seconds since the epoch
"""
asn1_time = utils.timestamp_to_asn1_time(t)
validity = self.get_validity()
validity['notBefore'] = asn1_time
def get_not_before(self):
"""Get the 'not before' date field as seconds since the epoch."""
validity = self.get_validity()
not_before = validity['notBefore']
return utils.asn1_time_to_timestamp(not_before)
def set_not_after(self, t):
"""Set the 'not after' date field.
:param t: time in seconds since the epoch
"""
asn1_time = utils.timestamp_to_asn1_time(t)
validity = self.get_validity()
validity['notAfter'] = asn1_time
def get_not_after(self):
"""Get the 'not after' date field as seconds since the epoch."""
validity = self.get_validity()
not_after = validity['notAfter']
return utils.asn1_time_to_timestamp(not_after)
def set_pubkey(self, pkey):
"""Set the public key field.
:param pkey: The public key, rfc5280.SubjectPublicKeyInfo description
"""
self._cert['tbsCertificate']['subjectPublicKeyInfo'] = pkey
def get_subject(self):
"""Get the subject name field value.
:return: An X509Name object instance
"""
val = self._cert['tbsCertificate']['subject'][0]
return name.X509Name(val)
def set_subject(self, subject):
"""Set the subject name filed value.
:param subject: An X509Name object instance
"""
val = subject._name_obj
if self._cert['tbsCertificate']['subject'] is None:
self._cert['tbsCertificate']['subject'] = rfc5280.Name()
self._cert['tbsCertificate']['subject'][0] = val
def set_issuer(self, issuer):
"""Set the issuer name field value.
:param issuer: An X509Name object instance
"""
val = issuer._name_obj
if self._cert['tbsCertificate']['issuer'] is None:
self._cert['tbsCertificate']['issuer'] = rfc5280.Name()
self._cert['tbsCertificate']['issuer'][0] = val
def get_issuer(self):
"""Get the issuer name field value.
:return: An X509Name object instance
"""
val = self._cert['tbsCertificate']['issuer'][0]
return name.X509Name(val)
def set_serial_number(self, serial):
"""Set the serial number
The serial number is a 32 bit integer value that should be unique to
each certificate issued by a given certificate authority.
:param serial: The serial number, 32 bit integer
"""
self._cert['tbsCertificate']['serialNumber'] = serial
def get_serial_number(self,):
return self._cert['tbsCertificate']['serialNumber']
def _get_extensions(self):
if self._cert['tbsCertificate']['extensions'] is None:
# this actually initialises the extensions tag rather than
# assign None
self._cert['tbsCertificate']['extensions'] = None
return self._cert['tbsCertificate']['extensions']
def get_extensions(self, ext_type=None):
extensions = self._get_extensions()
return [extension.construct_extension(e) for e in extensions
if ext_type is None or e['extnID'] == ext_type._oid]
def add_extension(self, ext, index):
"""Add an X509 V3 Certificate extension.
:param ext: An X509Extension instance
:param index: The index of the extension
"""
if not isinstance(ext, extension.X509Extension):
raise errors.X509Error("ext needs to be a pyasn1 extension")
extensions = self._get_extensions()
extensions[index] = ext.as_asn1()
def _get_bytes_to_sign(self):
return encoder.encode(self._cert['tbsCertificate'])
def _embed_signature_algorithm(self, algo_id):
self._cert['tbsCertificate']['signature'] = algo_id
def _embed_signature(self, algo_id, signature):
self._cert['signature'] = "'%s'H" % (
str(binascii.hexlify(signature).decode('ascii')),)
self._cert['signatureAlgorithm'] = algo_id
def _get_signature(self):
return utils.bin_to_bytes(self._cert['signature'])
def _get_signing_algorithm(self):
tbs_signature = self._cert['tbsCertificate']['signature']
cert_signature = self._cert['signatureAlgorithm']
if tbs_signature != cert_signature:
raise errors.X509Error("algorithms mismatch")
return tbs_signature['algorithm']
def as_der(self):
"""Return this X509 certificate as DER encoded data."""
return encoder.encode(self._cert)
def get_fingerprint(self, md='sha256'):
"""Get the fingerprint of this X509 certificate.
:param md: The message digest algorithm used to compute the fingerprint
:return: The fingerprint encoded as a hex string
"""
hash_class = utils.get_hash_class(md)
if hash_class is None:
raise errors.X509Error(
"Unknown hash %s" % (md,))
hasher = hashes.Hash(hash_class(),
backend=cio_backends.default_backend())
hasher.update(self.as_der())
return binascii.hexlify(hasher.finalize()).upper().decode('ascii')
def get_key_id(self):
"""Construct a key identifier from public key.
Return the hash useful for keyIdentifier field, constructed as
described in RFC5280 section 4.2.1.2, method 1. The result is
SHA1(subjectPublicKey).
"""
key_info = self._cert['tbsCertificate']['subjectPublicKeyInfo']
public_key = key_info['subjectPublicKey']
# get the actual bit string value, without the length and tags
value = ber_encoder.BitStringEncoder().encodeValue(
None, public_key, True, None)[0][1:]
digest = hashes.Hash(hashes.SHA1(),
backend=cio_backends.default_backend())
digest.update(value)
return digest.finalize()

View File

@ -1,31 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# not needed right now, just to be consistent and future-proof
from __future__ import absolute_import
class X509Error(Exception):
"""Base exception for X509 errors."""
def __init__(self, what):
super(X509Error, self).__init__(what)
class ASN1TimeError(Exception):
"""Base exception for ASN1-time related errors."""
pass
class ASN1StringError(X509Error):
"""Base exception for ASN1-string related errors."""
pass

View File

@ -1,523 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import functools
import netaddr
from pyasn1.codec.der import decoder
from pyasn1.codec.der import encoder
from pyasn1.type import constraint as asn1_constraint
from pyasn1.type import namedtype as asn1_namedtype
from pyasn1.type import tag as asn1_tag
from pyasn1.type import univ as asn1_univ
from anchor.asn1 import rfc5280
from anchor.X509 import errors
from anchor.X509 import utils
# missing extended use ids from rfc5280
id_kp_OCSPSigning = asn1_univ.ObjectIdentifier(rfc5280.id_kp.asTuple() + (9,))
anyExtendedKeyUsage = asn1_univ.ObjectIdentifier(
rfc5280.id_ce_extKeyUsage.asTuple() + (0,))
# names matching openssl
EXT_KEY_USAGE_NAMES = {
rfc5280.id_kp_serverAuth: "TLS Web Server Authentication",
rfc5280.id_kp_clientAuth: "TLS Web Client Authentication",
rfc5280.id_kp_codeSigning: "Code Signing",
rfc5280.id_kp_emailProtection: "E-mail Protection",
rfc5280.id_kp_timeStamping: "Time Stamping",
id_kp_OCSPSigning: "OCSP Signing",
anyExtendedKeyUsage: "Any Extended Key Usage",
}
EXT_KEY_USAGE_NAMES_INV = dict((v, k) for k, v in EXT_KEY_USAGE_NAMES.items())
EXT_KEY_USAGE_SHORT_NAMES = {
rfc5280.id_kp_serverAuth: "serverAuth",
rfc5280.id_kp_clientAuth: "clientAuth",
rfc5280.id_kp_codeSigning: "codeSigning",
rfc5280.id_kp_emailProtection: "emailProtection",
rfc5280.id_kp_timeStamping: "timeStamping",
id_kp_OCSPSigning: "ocspSigning",
anyExtendedKeyUsage: "anyExtendedKeyUsage",
}
EXT_KEY_USAGE_SHORT_NAMES_INV = dict((v, k) for k, v in
EXT_KEY_USAGE_SHORT_NAMES.items())
EXTENSION_NAMES = {
rfc5280.id_ce_policyConstraints: 'policyConstraints',
rfc5280.id_ce_basicConstraints: 'basicConstraints',
rfc5280.id_ce_subjectDirectoryAttributes: 'subjectDirectoryAttributes',
rfc5280.id_ce_deltaCRLIndicator: 'deltaCRLIndicator',
rfc5280.id_ce_cRLDistributionPoints: 'cRLDistributionPoints',
rfc5280.id_ce_issuingDistributionPoint: 'issuingDistributionPoint',
rfc5280.id_ce_nameConstraints: 'nameConstraints',
rfc5280.id_ce_certificatePolicies: 'certificatePolicies',
rfc5280.id_ce_policyMappings: 'policyMappings',
rfc5280.id_ce_privateKeyUsagePeriod: 'privateKeyUsagePeriod',
rfc5280.id_ce_keyUsage: 'keyUsage',
rfc5280.id_ce_authorityKeyIdentifier: 'authorityKeyIdentifier',
rfc5280.id_ce_subjectKeyIdentifier: 'subjectKeyIdentifier',
rfc5280.id_ce_certificateIssuer: 'certificateIssuer',
rfc5280.id_ce_subjectAltName: 'subjectAltName',
rfc5280.id_ce_issuerAltName: 'issuerAltName',
}
LONG_KEY_USAGE_NAMES = {
"Digital Signature": "digitalSignature",
"Non Repudiation": "nonRepudiation",
"Key Encipherment": "keyEncipherment",
"Data Encipherment": "dataEncipherment",
"Key Agreement": "keyAgreement",
"Certificate Sign": "keyCertSign",
"CRL Sign": "cRLSign",
"Encipher Only": "encipherOnly",
"Decipher Only": "decipherOnly",
}
def uses_ext_value(f):
"""Wrapper allowing reading of extension value.
Because the value is normally saved in a (double) serialised way, it's
not easily accessible to the member methods. This is made easier by
unpacking the extension value into an extra argument.
"""
@functools.wraps(f)
def ext_value_filled(self, *args, **kwargs):
kwargs['ext_value'] = self._get_value()
return f(self, *args, **kwargs)
return ext_value_filled
def modifies_ext_value(f):
"""Wrapper allowing modification of extension value.
Because the value is normally saved in a (double) serialised way, it's
not easily accessible to the member methods. This is made easier by
unpacking the extension value into an extra argument.
New value needs to be returned from the method.
"""
@functools.wraps(f)
def ext_value_filled(self, *args, **kwargs):
value = self._get_value()
kwargs['ext_value'] = value
# since some elements like NamedValue are pure value types, there is
# no interface to modify them and new versions have to be returned
value = f(self, *args, **kwargs)
self._set_value(value)
return ext_value_filled
class BasicConstraints(asn1_univ.Sequence):
"""Custom BasicConstraint implementation until pyasn1_modules is fixes."""
componentType = asn1_namedtype.NamedTypes(
asn1_namedtype.DefaultedNamedType('cA', asn1_univ.Boolean(False)),
asn1_namedtype.OptionalNamedType(
'pathLenConstraint',
asn1_univ.Integer().subtype(
subtypeSpec=asn1_constraint.ValueRangeConstraint(0, 64)))
)
class NameConstraints(asn1_univ.Sequence):
"""Custom NameConstraints implementation until pyasn1_modules is fixed."""
componentType = asn1_namedtype.NamedTypes(
asn1_namedtype.OptionalNamedType(
'permittedSubtrees',
rfc5280.GeneralSubtrees().subtype(
implicitTag=asn1_tag.Tag(asn1_tag.tagClassContext,
asn1_tag.tagFormatConstructed, 0))),
asn1_namedtype.OptionalNamedType(
'excludedSubtrees',
rfc5280.GeneralSubtrees().subtype(
implicitTag=asn1_tag.Tag(asn1_tag.tagClassContext,
asn1_tag.tagFormatConstructed, 1)))
)
class X509Extension(object):
"""Abstraction for the pyasn1 Extension structures.
The object should normally be constructed using `construct_extension`,
which will choose the right extension type based on the id.
Each extension has an immutable oid and a spec of the internal value
representation.
Unknown extension types can be still represented by the
X509Extension object and copied/serialised without understanding the
value details. The value will not be displayed properly in the logs
in the case.
"""
_oid = None
spec = None
"""An X509 V3 Certificate extension."""
def __init__(self, ext=None):
if ext is None:
if self.spec is None:
raise errors.X509Error("cannot create generic extension")
self._ext = rfc5280.Extension()
self._ext['extnID'] = self._oid
self._set_value(self._get_default_value())
else:
if not isinstance(ext, rfc5280.Extension):
raise errors.X509Error("extension has incorrect type")
self._ext = ext
@classmethod
def _get_default_value(cls):
# if there are any non-optional fields, this needs to be defined in
# the class
return cls.spec()
def __str__(self):
return "%s: %s" % (self.get_name(), self.get_value_as_str())
def get_value_as_str(self):
return "<unknown>"
def get_oid(self):
return self._ext['extnID']
def get_name(self):
"""Get the extension name as a python string."""
oid = self.get_oid()
return EXTENSION_NAMES.get(oid, oid)
def get_critical(self):
return self._ext['critical']
def set_critical(self, critical):
self._ext['critical'] = critical
def _get_value(self):
return decoder.decode(self._ext['extnValue'].asOctets(),
asn1Spec=self.spec())[0]
def _set_value(self, value):
if not isinstance(value, self.spec):
raise errors.X509Error("extension value has incorrect type")
self._ext['extnValue'] = encoder.encode(value)
def as_der(self):
return encoder.encode(self._ext)
def as_asn1(self):
return self._ext
class X509ExtensionBasicConstraints(X509Extension):
spec = BasicConstraints
_oid = rfc5280.id_ce_basicConstraints
@uses_ext_value
def get_ca(self, ext_value=None):
return bool(ext_value['cA'])
@modifies_ext_value
def set_ca(self, ca, ext_value=None):
ext_value['cA'] = ca
return ext_value
@uses_ext_value
def get_path_len_constraint(self, ext_value=None):
return ext_value['pathLenConstraint']
@modifies_ext_value
def set_path_len_constraint(self, length, ext_value=None):
ext_value['pathLenConstraint'] = length
return ext_value
def __str__(self):
return "basicConstraints: CA: %s, pathLen: %s" % (
str(self.get_ca()).upper(), self.get_path_len_constraint())
class X509ExtensionKeyUsage(X509Extension):
spec = rfc5280.KeyUsage
_oid = rfc5280.id_ce_keyUsage
fields = dict(spec.namedValues.namedValues)
inv_fields = dict((v, k) for k, v in spec.namedValues.namedValues)
@classmethod
def _get_default_value(cls):
# if there are any non-optional fields, this needs to be defined in
# the class
return cls.spec("''B")
@uses_ext_value
def get_usage(self, usage, ext_value=None):
usage = LONG_KEY_USAGE_NAMES.get(usage, usage)
pos = self.fields[usage]
if pos >= len(ext_value):
return False
return bool(ext_value[pos])
@uses_ext_value
def get_all_usages(self, ext_value=None):
return [self.inv_fields[i] for i, enabled in enumerate(ext_value)
if enabled]
@modifies_ext_value
def set_usage(self, usage, state, ext_value=None):
usage = LONG_KEY_USAGE_NAMES.get(usage, usage)
pos = self.fields[usage]
values = [x for x in ext_value]
if state:
while pos >= len(values):
values.append(0)
values[pos] = 1
else:
if pos < len(values):
values[pos] = 0
bits = ''.join(str(x) for x in values)
return self.spec("'%s'B" % bits)
def __str__(self):
return "keyUsage: " + ", ".join(self.get_all_usages())
class X509ExtensionSubjectAltName(X509Extension):
spec = rfc5280.SubjectAltName
_oid = rfc5280.id_ce_subjectAltName
@uses_ext_value
def get_dns_ids(self, ext_value=None):
dns_ids = []
for name in ext_value:
if name.getName() != 'dNSName':
continue
component = name.getComponent()
dns_id = component.asOctets().decode(component.encoding)
dns_ids.append(dns_id)
return dns_ids
@uses_ext_value
def get_ips(self, ext_value=None):
ips = []
for name in ext_value:
if name.getName() != 'iPAddress':
continue
ips.append(utils.asn1_to_netaddr(name.getComponent()))
return ips
@uses_ext_value
def has_unknown_entries(self, ext_value=None):
for name in ext_value:
if name.getName() not in ('dNSName', 'iPAddress'):
return True
return False
@modifies_ext_value
def add_dns_id(self, dns_id, validate=True, ext_value=None):
new_pos = len(ext_value)
ext_value[new_pos] = None
ext_value[new_pos]['dNSName'] = dns_id
return ext_value
@modifies_ext_value
def add_ip(self, ip, ext_value=None):
if not isinstance(ip, netaddr.IPAddress):
raise errors.X509Error("not a real ip address provided")
new_pos = len(ext_value)
ext_value[new_pos] = None
ext_value[new_pos]['iPAddress'] = utils.netaddr_to_asn1(ip)
return ext_value
@uses_ext_value
def __str__(self, ext_value=None):
entries = ["DNS:%s" % (x,) for x in self.get_dns_ids()]
entries += ["IP:%s" % (x,) for x in self.get_ips()]
return "subjectAltName: " + ", ".join(entries)
class X509ExtensionNameConstraints(X509Extension):
spec = NameConstraints
_oid = rfc5280.id_ce_nameConstraints
def _get_permitted(self, ext_value):
return ext_value['permittedSubtrees'] or []
def _get_excluded(self, ext_value):
return ext_value['excludedSubtrees'] or []
@uses_ext_value
def get_permitted_length(self, ext_value=None):
return len(self._get_permitted(ext_value))
@uses_ext_value
def get_permitted_name(self, n, ext_value=None):
name = self._get_permitted(ext_value)[n]['base']
return (name.getName(), name.getComponent())
@uses_ext_value
def get_permitted_range(self, n, ext_value=None):
entry = self._get_permitted(ext_value)[n]
return (entry['minimum'], entry['maximum'])
@uses_ext_value
def get_excluded_length(self, ext_value=None):
return len(self._get_excluded(ext_value))
@uses_ext_value
def get_excluded_name(self, n, ext_value=None):
name = self._get_excluded(ext_value)[n]['base']
return (name.getName(), name.getComponent())
@uses_ext_value
def get_excluded_range(self, n, ext_value=None):
entry = self._get_excluded(ext_value)[n]
return (entry['minimum'], entry['maximum'])
def _add_to_tree(self, ext_value, tree_name, position, name_type, name):
if ext_value[tree_name] is None:
ext_value[tree_name] = None
ext_value[tree_name][position] = None
ext_value[tree_name][position]['base'] = None
ext_value[tree_name][position]['base'][name_type] = name
ext_value[tree_name][position]['minimum'] = 0
# maximum should be missing (RFC5280/4.2.1.10)
@modifies_ext_value
def add_permitted(self, name_type, name, ext_value=None):
last = self.get_permitted_length()
self._add_to_tree(ext_value, 'permittedSubtrees', last,
name_type, name)
return ext_value
@modifies_ext_value
def add_excluded(self, name_type, name, ext_value=None):
last = self.get_excluded_length()
self._add_to_tree(ext_value, 'excludedSubtrees', last, name_type, name)
return ext_value
class X509ExtensionExtendedKeyUsage(X509Extension):
spec = rfc5280.ExtKeyUsageSyntax
_oid = rfc5280.id_ce_extKeyUsage
_valid = list(EXT_KEY_USAGE_NAMES.keys())
@uses_ext_value
def get_all_usages(self, ext_value=None):
return [usage for usage in ext_value]
@uses_ext_value
def get_usage(self, usage, ext_value=None):
if usage not in self._valid:
raise ValueError("usage not valid")
return (usage in ext_value)
@modifies_ext_value
def set_usage(self, usage, state, ext_value=None):
if usage not in self._valid:
raise ValueError("usage not valid")
if state:
if usage not in ext_value:
ext_value[len(ext_value)] = usage
else:
if usage in ext_value:
old = [x for x in ext_value if x != usage]
ext_value.clear()
for i, x in enumerate(old):
ext_value[i] = x
return ext_value
@uses_ext_value
def __str__(self, ext_value=None):
usages = [EXT_KEY_USAGE_NAMES.get(u) for u in ext_value]
return "extKeyUsage: " + ", ".join(usages)
class X509ExtensionAuthorityKeyId(X509Extension):
spec = rfc5280.AuthorityKeyIdentifier
_oid = rfc5280.id_ce_authorityKeyIdentifier
@uses_ext_value
def get_key_id(self, ext_value=None):
ki = ext_value['keyIdentifier']
if ki:
return ki.asOctets()
else:
return None
@uses_ext_value
def get_serial(self, ext_value=None):
return ext_value['authorityCertSerialNumber']
@modifies_ext_value
def set_key_id(self, key, ext_value=None):
# new extension, pyasn1 cannot remove values
new_ext = self.spec()
new_ext['keyIdentifier'] = key
return new_ext
@modifies_ext_value
def set_serial(self, serial, ext_value=None):
# new extension, pyasn1 cannot remove values
new_ext = self.spec()
new_ext['authorityCertSerialNumber'] = int(serial)
return new_ext
class X509ExtensionSubjectKeyId(X509Extension):
spec = rfc5280.SubjectKeyIdentifier
_oid = rfc5280.id_ce_subjectKeyIdentifier
@classmethod
def _get_default_value(cls):
return cls.spec(b"")
@uses_ext_value
def get_key_id(self, ext_value=None):
return ext_value.asOctets()
@modifies_ext_value
def set_key_id(self, key, ext_value=None):
return self.spec(key)
EXTENSION_CLASSES = {
rfc5280.id_ce_basicConstraints: X509ExtensionBasicConstraints,
rfc5280.id_ce_keyUsage: X509ExtensionKeyUsage,
rfc5280.id_ce_extKeyUsage: X509ExtensionExtendedKeyUsage,
rfc5280.id_ce_subjectAltName: X509ExtensionSubjectAltName,
rfc5280.id_ce_nameConstraints: X509ExtensionNameConstraints,
rfc5280.id_ce_authorityKeyIdentifier: X509ExtensionAuthorityKeyId,
rfc5280.id_ce_subjectKeyIdentifier: X509ExtensionSubjectKeyId,
}
def construct_extension(ext):
"""Construct an extension object of the right type.
While X509Extension can provide basic access to the extension elements,
it cannot parse details of extensions. This function detects which type
should be used based on the extension id.
If the type is unknown, generic X509Extension is used instead.
"""
if not isinstance(ext, rfc5280.Extension):
raise errors.X509Error("extension has incorrect type")
ext_class = EXTENSION_CLASSES.get(ext['extnID'], X509Extension)
return ext_class(ext)

View File

@ -1,172 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
from pyasn1.codec.der import decoder
from pyasn1.codec.der import encoder
from pyasn1.type import error as asn1_error
from pyasn1.type import univ as asn1_univ
from anchor.asn1 import rfc5280
from anchor.X509 import errors
OID_commonName = rfc5280.id_at_commonName
OID_localityName = rfc5280.id_at_localityName
OID_stateOrProvinceName = rfc5280.id_at_stateOrProvinceName
OID_organizationName = rfc5280.id_at_organizationName
OID_organizationalUnitName = rfc5280.id_at_organizationalUnitName
OID_countryName = rfc5280.id_at_countryName
OID_pkcs9_emailAddress = rfc5280.id_emailAddress
OID_surname = rfc5280.id_at_surname
OID_givenName = rfc5280.id_at_givenName
name_oids = {
rfc5280.id_at_name: rfc5280.X520name,
rfc5280.id_at_surname: rfc5280.X520name,
rfc5280.id_at_givenName: rfc5280.X520name,
rfc5280.id_at_initials: rfc5280.X520name,
rfc5280.id_at_generationQualifier: rfc5280.X520name,
rfc5280.id_at_commonName: rfc5280.X520CommonName,
rfc5280.id_at_localityName: rfc5280.X520LocalityName,
rfc5280.id_at_stateOrProvinceName: rfc5280.X520StateOrProvinceName,
rfc5280.id_at_organizationName: rfc5280.X520OrganizationName,
rfc5280.id_at_organizationalUnitName: rfc5280.X520OrganizationalUnitName,
rfc5280.id_at_title: rfc5280.X520Title,
rfc5280.id_at_dnQualifier: rfc5280.X520dnQualifier,
rfc5280.id_at_countryName: rfc5280.X520countryName,
rfc5280.id_emailAddress: rfc5280.EmailAddress,
}
code_names = {
rfc5280.id_at_commonName: "CN",
rfc5280.id_at_localityName: "L",
rfc5280.id_at_stateOrProvinceName: "ST",
rfc5280.id_at_organizationName: "O",
rfc5280.id_at_organizationalUnitName: "OU",
rfc5280.id_at_countryName: "C",
rfc5280.id_at_givenName: "GN",
rfc5280.id_at_surname: "SN",
rfc5280.id_emailAddress: "emailAddress",
}
short_names = {
rfc5280.id_at_commonName: "commonName",
rfc5280.id_at_localityName: "localityName",
rfc5280.id_at_stateOrProvinceName: "stateOrProvinceName",
rfc5280.id_at_organizationName: "organizationName",
rfc5280.id_at_organizationalUnitName: "organizationalUnitName",
rfc5280.id_at_countryName: "countryName",
rfc5280.id_at_givenName: "givenName",
rfc5280.id_at_surname: "surname",
rfc5280.id_emailAddress: "emailAddress",
}
class X509Name(object):
"""An X509 Name object."""
class Entry():
"""An X509 Name sub-entry object."""
def __init__(self, obj):
self._obj = obj
def __str__(self):
return "%s: %s" % (self.get_name(), self.get_value())
def get_oid(self):
return self._obj[0]['type']
def get_name(self):
"""Get the name of this entry.
:return: entry name as a python string
"""
oid = self.get_oid()
return short_names.get(oid, str(oid))
def get_code(self):
"""Get the name of this entry.
:return: entry name as a python string
"""
oid = self.get_oid()
return code_names.get(oid, str(oid))
def get_value(self):
"""Get the value of this entry.
:return: entry value as a python string
"""
value = self._obj[0]['value']
der = value.asOctets()
oid = self.get_oid()
if oid not in name_oids:
return 'UNKNOWN'
name_spec = name_oids[oid]()
value = decoder.decode(der, asn1Spec=name_spec)[0]
if hasattr(value, 'getComponent'):
value = value.getComponent()
return value.asOctets().decode(value.encoding)
def __init__(self, name_obj=None):
if name_obj is not None:
if not isinstance(name_obj, rfc5280.RDNSequence):
raise TypeError("name is not an RDNSequence")
self._name_obj = name_obj.clone(cloneValueFlag=True)
else:
self._name_obj = rfc5280.RDNSequence()
def __str__(self):
return '/' + '/'.join("%s=%s" % (e.get_code(), e.get_value())
for e in self)
def __len__(self):
return len(self._name_obj)
def __getitem__(self, idx):
return X509Name.Entry(self._name_obj[idx])
def __iter__(self):
for i in range(len(self)):
yield self[i]
def add_name_entry(self, oid, text):
if not isinstance(oid, asn1_univ.ObjectIdentifier):
raise errors.X509Error("oid '%s' is not valid" % (oid,))
atv = rfc5280.AttributeTypeAndValue()
atv['type'] = oid
name_type = name_oids[oid]
try:
if name_type in (rfc5280.X520countryName, rfc5280.EmailAddress):
val = name_type(text)
else:
val = name_type()
val['utf8String'] = text
except asn1_error.ValueConstraintError:
raise errors.X509Error("Name '%s' is not valid" % text)
atv['value'] = rfc5280.AttributeValue(encoder.encode(val))
entry = rfc5280.RelativeDistinguishedName()
entry[0] = atv
self._name_obj[len(self)] = entry
def get_entries_by_oid(self, oid):
"""Get a name entry corresponding to an NID name.
:param nid: an NID for the new name entry
:return: An X509Name.Entry object
"""
return [entry for entry in self if entry.get_oid() == oid]

View File

@ -1,175 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import logging
from cryptography import exceptions as cio_exceptions
from cryptography.hazmat.primitives.asymmetric import dsa
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from pyasn1.codec.der import encoder
from pyasn1.type import univ as asn1_univ
from anchor.asn1 import rfc5280
from anchor.X509 import errors
LOG = logging.getLogger(__name__)
DEPRECATED_ALGORITHM_NAMES = {
asn1_univ.ObjectIdentifier('1.2.840.113549.1.1.2'): 'MD2 with RSA',
asn1_univ.ObjectIdentifier('1.2.840.113549.1.1.3'): 'MD4 with RSA',
asn1_univ.ObjectIdentifier('1.2.840.113549.1.1.4'): 'MD5 with RSA',
asn1_univ.ObjectIdentifier('1.2.840.113549.1.1.5'): 'SHA1 with RSA',
asn1_univ.ObjectIdentifier('1.2.840.10040.4.3'): 'SHA1 with DSA',
}
# valid algorithms
sha224WithRSAEncryption = asn1_univ.ObjectIdentifier('1.2.840.113549.1.1.14')
sha256WithRSAEncryption = asn1_univ.ObjectIdentifier('1.2.840.113549.1.1.11')
sha384WithRSAEncryption = asn1_univ.ObjectIdentifier('1.2.840.113549.1.1.12')
sha512WithRSAEncryption = asn1_univ.ObjectIdentifier('1.2.840.113549.1.1.13')
id_dsa_with_sha224 = asn1_univ.ObjectIdentifier('2.16.840.1.101.3.4.3.1')
id_dsa_with_sha256 = asn1_univ.ObjectIdentifier('2.16.840.1.101.3.4.3.2')
SIGNING_ALGORITHMS = {
('RSA', 'SHA224'): sha224WithRSAEncryption,
('RSA', 'SHA256'): sha256WithRSAEncryption,
('RSA', 'SHA384'): sha384WithRSAEncryption,
('RSA', 'SHA512'): sha512WithRSAEncryption,
('DSA', 'SHA224'): id_dsa_with_sha224,
('DSA', 'SHA256'): id_dsa_with_sha256,
}
SIGNING_ALGORITHMS_INV = dict((v, k) for k, v in SIGNING_ALGORITHMS.items())
VERIFIER_CONSTRUCTION = {
sha224WithRSAEncryption: (lambda key, signature: key.verifier(
signature, padding.PKCS1v15(), hashes.SHA224())),
sha256WithRSAEncryption: (lambda key, signature: key.verifier(
signature, padding.PKCS1v15(), hashes.SHA256())),
sha384WithRSAEncryption: (lambda key, signature: key.verifier(
signature, padding.PKCS1v15(), hashes.SHA384())),
sha512WithRSAEncryption: (lambda key, signature: key.verifier(
signature, padding.PKCS1v15(), hashes.SHA512())),
id_dsa_with_sha224: (lambda key, signature: key.verifier(
signature, hashes.SHA224())),
id_dsa_with_sha256: (lambda key, signature: key.verifier(
signature, hashes.SHA256())),
}
ALGORITHM_PARAMETERS = {
sha224WithRSAEncryption: encoder.encode(asn1_univ.Null()),
sha256WithRSAEncryption: encoder.encode(asn1_univ.Null()),
sha384WithRSAEncryption: encoder.encode(asn1_univ.Null()),
sha512WithRSAEncryption: encoder.encode(asn1_univ.Null()),
id_dsa_with_sha224: None,
id_dsa_with_sha256: None,
}
class SignatureMixin(object):
"""Provides the sign() and verify() functions.
Both operations rely on the functions provided by the certificate and
csr classes.
"""
def sign(self, encryption, md, signer):
"""Sign the current object."""
md = md.upper()
signature_type = SIGNING_ALGORITHMS.get((encryption, md))
if signature_type is None:
raise errors.X509Error(
"Unknown encryption/hash combination %s/%s" % (encryption, md))
algo_id = rfc5280.AlgorithmIdentifier()
algo_id['algorithm'] = signature_type
algo_params = ALGORITHM_PARAMETERS[signature_type]
if algo_params is not None:
algo_id['parameters'] = algo_params
self._embed_signature_algorithm(algo_id)
to_sign = self._get_bytes_to_sign()
signature = signer(to_sign)
self._embed_signature(algo_id, signature)
def verify(self, key=None):
algo_id = self._get_signing_algorithm()
if algo_id not in SIGNING_ALGORITHMS_INV:
LOG.warning("Signature algorithm %s is unknown, cannot verify",
algo_id)
return False
if key is None:
key = self._get_public_key()
encryption, hash_algo = SIGNING_ALGORITHMS_INV[algo_id]
to_sign = self._get_bytes_to_sign()
signature = self._get_signature()
if ((encryption == 'RSA' and not isinstance(key, rsa.RSAPublicKey)) or
(encryption == 'DSA' and not isinstance(key,
dsa.DSAPublicKey))):
raise errors.X509Error("Key type mismatch: object %s, key %s" %
(encryption, key.__class__))
verifier = VERIFIER_CONSTRUCTION[algo_id](key, signature)
verifier.update(to_sign)
try:
verifier.verify()
return True
except cio_exceptions.InvalidSignature:
return False
def uses_deprecated_algorithm(self):
"""Check for deprecated algorithm in signatures.
Returns the name of the algorithm found, or None if everything's ok.
"""
name = DEPRECATED_ALGORITHM_NAMES.get(self._get_signing_algorithm())
return name
def _get_bytes_to_sign(self):
"""Get bytes which are giong to be hashed and signed."""
raise NotImplementedError()
def _get_public_key(self):
"""Get public key for verifying CSR self-signatures."""
raise NotImplementedError()
def _get_signature(self):
"""Get the current signature value as bytes."""
raise NotImplementedError()
def _get_signing_algorithm(self):
"""Get the description of algorithm used to sign object."""
raise NotImplementedError()
def _embed_signature_algorithm(self, algo_id):
"""Called before the signature is calculated.
Since signature of the certificate depends on the signature algorithm,
it needs to be saved first.
"""
raise NotImplementedError()
def _embed_signature(self, algo_id, signature):
"""Called after the signature is calculated."""
raise NotImplementedError()

View File

@ -1,244 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import binascii
import io
from pyasn1.codec.der import decoder
from pyasn1.codec.der import encoder
from pyasn1.type import univ as asn1_univ
from anchor.asn1 import rfc5280
from anchor.asn1 import rfc6402
from anchor import util
from anchor.X509 import errors
from anchor.X509 import extension
from anchor.X509 import name
from anchor.X509 import signature
from anchor.X509 import utils as x509_utils
OID_extensionRequest = asn1_univ.ObjectIdentifier('1.2.840.113549.1.9.14')
class X509CsrError(errors.X509Error):
def __init__(self, what):
super(X509CsrError, self).__init__(what)
class X509Csr(signature.SignatureMixin):
"""An X509 Certificate Signing Request."""
def __init__(self, csr=None):
if csr is None:
self._csr = rfc6402.CertificationRequest()
else:
self._csr = csr
@staticmethod
def from_open_file(f, encoding='pem'):
if encoding == 'pem':
try:
der_content = util.extract_pem(f.read())
except IOError:
raise X509CsrError("Could not read from source %s" % f)
except Exception:
raise X509CsrError(
"Data source not readable or not in PEM format")
if not der_content:
raise X509CsrError("No PEM data found")
elif encoding == 'der':
der_content = f.read()
else:
raise X509CsrError("Unknown encoding")
try:
csr = decoder.decode(der_content,
asn1Spec=rfc6402.CertificationRequest())[0]
return X509Csr(csr)
except Exception:
raise X509CsrError("Could not read X509 certificate from data.")
@staticmethod
def from_buffer(data, encoding='pem'):
"""Create this CSR from a buffer
:param data: The data buffer
"""
return X509Csr.from_open_file(io.BytesIO(data), encoding)
@staticmethod
def from_file(path, encoding='pem'):
"""Create this CSR from a file on disk
:param path: Path to the file on disk
"""
try:
with open(path, 'r') as f:
return X509Csr.from_open_file(f, encoding)
except IOError:
raise X509CsrError("Could not read file %s" % path)
def get_pubkey(self):
"""Get the public key from the CSR
:return: ASN.1 description of public key
"""
return self._csr['certificationRequestInfo']['subjectPublicKeyInfo']
def get_request_info(self):
if self._csr['certificationRequestInfo'] is None:
self._csr['certificationRequestInfo'] = None
return self._csr['certificationRequestInfo']
def get_subject(self):
"""Get the subject name field from the CSR
:return: an X509Name object
"""
ri = self.get_request_info()
if ri['subject'] is None:
ri['subject'] = None
# setup first RDN sequence
ri['subject'][0] = None
subject = ri['subject'][0]
return name.X509Name(subject)
def set_subject(self, subject):
if not isinstance(subject, name.X509Name):
raise TypeError("subject must be an X509Name")
ri = self.get_request_info()
if ri['subject'] is None:
ri['subject'] = None
ri['subject'][0] = subject._name_obj
def get_attributes(self):
ri = self.get_request_info()
if ri['attributes'] is None:
ri['attributes'] = None
return ri['attributes']
def get_subject_cn(self):
"""Get the CN part of subject.
:return subject's CN
"""
subject = self.get_subject()
cns = subject.get_entries_by_oid(name.OID_commonName)
return [cn.get_value() for cn in cns]
def get_extensions(self, ext_type=None):
"""Get the list of all X509 V3 Extensions on this CSR
:return: a list of X509Extension objects
"""
ext_attrs = [a for a in self.get_attributes()
if a['attrType'] == OID_extensionRequest]
if len(ext_attrs) == 0:
return []
else:
exts_der = ext_attrs[0]['attrValues'][0].asOctets()
exts = decoder.decode(exts_der, asn1Spec=rfc5280.Extensions())[0]
return [extension.construct_extension(e) for e in exts
if ext_type is None or e['extnID'] == ext_type._oid]
def add_extension(self, new_ext):
"""Add a new extension or replace existing one."""
if not isinstance(new_ext, extension.X509Extension):
raise errors.X509Error("ext is not an anchor X509Extension")
attributes = self.get_attributes()
ext_attrs = [a for a in attributes
if a['attrType'] == OID_extensionRequest]
if not ext_attrs:
new_attr_index = len(attributes)
attributes[new_attr_index] = None
ext_attr = attributes[new_attr_index]
ext_attr['attrType'] = OID_extensionRequest
ext_attr['attrValues'] = None
exts = rfc5280.Extensions()
else:
ext_attr = ext_attrs[0]
exts = decoder.decode(ext_attr['attrValues'][0].asOctets(),
asn1Spec=rfc5280.Extensions())[0]
# the end is the default position
new_ext_index = len(exts)
# unless there's an existing extension with the same OID
for i, ext_i in enumerate(exts):
if ext_i['extnID'] == new_ext.get_oid():
new_ext_index = i
break
exts[new_ext_index] = new_ext._ext
ext_attr['attrValues'][0] = encoder.encode(exts)
def get_subject_dns_ids(self):
names = []
for ext in self.get_extensions(extension.X509ExtensionSubjectAltName):
for dns_id in ext.get_dns_ids():
names.append(dns_id)
return names
def get_subject_ip_ids(self):
names = []
for ext in self.get_extensions(extension.X509ExtensionSubjectAltName):
for ip in ext.get_ips():
names.append(ip)
return names
def has_unknown_san_entries(self):
for ext in self.get_extensions(extension.X509ExtensionSubjectAltName):
if ext.has_unknown_entries():
return True
return False
def get_public_key_algo(self):
csr_info = self._csr['certificationRequestInfo']
key_info = csr_info['subjectPublicKeyInfo']
return key_info['algorithm']['algorithm']
def get_public_key_size(self):
return self._get_public_key().key_size
def get_public_key(self):
return self._get_public_key()
def get_signing_algorithm(self):
return self._get_signing_algorithm()
def _get_signature(self):
return x509_utils.bin_to_bytes(self._csr['signature'])
def _get_signing_algorithm(self):
return self._csr['signatureAlgorithm']['algorithm']
def _get_public_key(self):
csr_info = self._csr['certificationRequestInfo']
key_info = csr_info['subjectPublicKeyInfo']
return x509_utils.get_public_key_from_der(encoder.encode(key_info))
def _get_bytes_to_sign(self):
return encoder.encode(self._csr['certificationRequestInfo'])
def _embed_signature_algorithm(self, algo_id):
pass
def _embed_signature(self, algo_id, signature):
self._csr['signatureAlgorithm'] = algo_id
self._csr['signature'] = "'%s'H" % (
str(binascii.hexlify(signature).decode('ascii')),)

View File

@ -1,180 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import calendar
import datetime
import struct
from cryptography.hazmat import backends
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
import netaddr
from pyasn1.type import useful as asn1_useful
from anchor.asn1 import rfc5280
from anchor.X509 import errors
def create_timezone(minute_offset):
"""Create a new timezone with a specified offset.
Since tzinfo is just a base class, and tzinfo subclasses need a
no-arguments __init__(), we need to generate a new class dynamically.
:param minute_offset: total timezone offset in minutes
"""
class SpecificTZ(datetime.tzinfo):
def utcoffset(self, _dt):
return datetime.timedelta(minutes=minute_offset)
def dst(self, _dt):
return datetime.timedelta(0)
def tzname(self, _dt):
return None
def __repr__(self):
sign = "+" if minute_offset > 0 else "-"
hh = minute_offset / 60
mm = minute_offset % 60
return "Timezone %s%02i%02i" % (sign, hh, mm)
return SpecificTZ()
def asn1_time_to_timestamp(t):
"""Convert from ASN1_TIME type to a UTC-based timestamp.
:param t: ASN1_TIME to convert
"""
component = t.getComponent()
timestring = component.asOctets().decode(component.encoding)
if isinstance(component, asn1_useful.UTCTime):
if int(timestring[0]) >= 5:
timestring = "19" + timestring
else:
timestring = "20" + timestring
return asn1_timestring_to_timestamp(timestring)
def asn1_timestring_to_timestamp(timestring):
"""Convert from ASN1_GENERALIZEDTIME to UTC-based timestamp.
:param gt: ASN1_GENERALIZEDTIME to convert
"""
# ASN1_GENERALIZEDTIME is actually a string in known formats,
# so the conversion can be done in this code
before_tz = timestring[:14]
tz_str = timestring[14:]
d = datetime.datetime.strptime(before_tz, "%Y%m%d%H%M%S")
if tz_str == 'Z':
# YYYYMMDDhhmmssZ
d.replace(tzinfo=create_timezone(0))
else:
# YYYYMMDDhhmmss+hhmm
# YYYYMMDDhhmmss-hhmm
sign = -1 if tz_str[0] == '-' else 1
hh = tz_str[1:3]
mm = tz_str[3:5]
minute_offset = sign * (int(mm) + int(hh) * 60)
d.replace(tzinfo=create_timezone(minute_offset))
return calendar.timegm(d.timetuple())
def timestamp_to_asn1_time(t):
"""Convert from UTC-based timestamp to ASN1_TIME
:param t: time in seconds since the epoch
"""
d = datetime.datetime.utcfromtimestamp(t)
asn1time = rfc5280.Time()
if d.year <= 2049:
time_str = d.strftime("%y%m%d%H%M%SZ").encode('ascii')
asn1time['utcTime'] = time_str
else:
time_str = d.strftime("%Y%m%d%H%M%SZ").encode('ascii')
asn1time['generalTime'] = time_str
return asn1time
# chr good for py2 and py3
_chr = chr if str is bytes else lambda x: bytes([x])
# functions needed for converting the pyasn1 signature fields
def bin_to_bytes(bits):
"""Convert bit string to byte string."""
bits = ''.join(str(b) for b in bits)
bits = _pad_byte(bits)
octets = [bits[8*i:8*(i+1)] for i in range(len(bits)//8)]
byte_list = [_chr(int(x, 2)) for x in octets]
return b"".join(byte_list)
# ord good for py2 and py3
_ord = ord if str is bytes else lambda x: x
def _pad_byte(bits):
"""Pad a string of bits with zeros to make its length a multiple of 8."""
r = len(bits) % 8
return ((8-r) % 8)*'0' + bits
def get_hash_class(md):
return getattr(hashes, md.upper(), None)
def get_private_key_from_pem(data):
return serialization.load_pem_private_key(
data, None, backend=backends.default_backend())
def get_public_key_from_der(data):
return serialization.load_der_public_key(
data, backend=backends.default_backend())
def get_private_key_from_file(path):
with open(path, 'rb') as f:
return get_private_key_from_pem(f.read())
def asn1_to_netaddr(octet_string):
"""Translate the ASN1 IP format to netaddr object."""
if not isinstance(octet_string, rfc5280.univ.OctetString):
raise TypeError("not an OctetString")
ip_bytes = octet_string.asOctets()
if len(ip_bytes) == 4:
ip_num = struct.unpack(">I", ip_bytes)[0]
return netaddr.IPAddress(ip_num, 4)
elif len(ip_bytes) == 16:
ip_num_front, ip_num_back = struct.unpack(">QQ", ip_bytes)
ip_num = ip_num_front << 64 | ip_num_back
return netaddr.IPAddress(ip_num, 6)
else:
raise TypeError("ip address is neither v4 nor v6")
def netaddr_to_asn1(ip):
"""Translate the netaddr object to ASN1 IP format."""
if not isinstance(ip, netaddr.IPAddress):
raise errors.X509Error("not a real ip address provided")
return bytes(ip.packed)

View File

View File

@ -1,237 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import logging
import os
import sys
import paste
from paste import translogger # noqa
import pecan
from anchor import audit
from anchor import errors
from anchor import jsonloader
logger = logging.getLogger(__name__)
def config_check_domains(validator_set):
for name, step in validator_set.items():
if 'allowed_domains' in step:
for domain in step['allowed_domains']:
if not domain.startswith('.'):
raise errors.ConfigValidationException(
"Domain that does not start with "
"a '.' <{}>".format(domain))
def validate_config(conf):
for old_name in ['auth', 'ca', 'validators']:
if old_name in conf.config:
raise errors.ConfigValidationException(
"The config seems to be for an old version of Anchor. Please "
"check documentation.")
if not conf.config.get('registration_authority'):
raise errors.ConfigValidationException(
"No registration authorities present")
if not conf.config.get('signing_ca'):
raise errors.ConfigValidationException(
"No signing CA configurations present")
if not conf.config.get('authentication'):
raise errors.ConfigValidationException(
"No authentication methods present")
for name in conf.registration_authority.keys():
logger.info("Checking config for registration authority: %s", name)
validate_registration_authority_config(name, conf)
for name in conf.signing_ca.keys():
logger.info("Checking config for signing ca: %s", name)
validate_signing_ca_config(name, conf)
for name in conf.authentication.keys():
logger.info("Checking config for authentication method: %s", name)
validate_authentication_config(name, conf)
validate_audit_config(conf)
def validate_audit_config(conf):
valid_targets = ('messaging', 'log')
if not conf.config.get('audit'):
# no audit configuration - that's ok
return
audit_conf = conf.audit
if audit_conf.get('target', 'log') not in valid_targets:
raise errors.ConfigValidationException(
"Audit target not known (expected one of %s)" % (
", ".join(valid_targets),))
if audit_conf.get('target') == 'messaging':
if audit_conf.get('url') is None:
raise errors.ConfigValidationException("Audit url required")
def validate_authentication_config(name, conf):
auth_conf = conf.authentication[name]
default_user = "myusername"
default_secret = "simplepassword"
if not auth_conf.get('backend'):
raise errors.ConfigValidationException(
"Authentication method %s doesn't define backend" % name)
if auth_conf['backend'] not in ('static', 'keystone', 'ldap'):
raise errors.ConfigValidationException(
"Authentication backend % unknown" % (auth_conf['backend'],))
# Check for anchor being run with default user/secret
if auth_conf['backend'] == 'static':
if auth_conf['user'] == default_user:
logger.warning("default user for static auth in use")
if auth_conf['secret'] == default_secret:
logger.warning("default secret for static auth in use")
def validate_signing_ca_config(name, conf):
ca_conf = conf.signing_ca[name]
backend_name = ca_conf.get('backend')
if not backend_name:
raise errors.ConfigValidationException(
"Backend type not defined for RA '%s'" % name)
sign_func = jsonloader.conf.get_signing_backend(backend_name)
if not sign_func:
raise errors.ConfigValidationException(
"Backend '%s' could not be found" % backend_name)
if hasattr(sign_func, "_config_validator"):
sign_func._config_validator(name, ca_conf)
def validate_registration_authority_config(ra_name, conf):
ra_conf = conf.registration_authority[ra_name]
auth_name = ra_conf.get('authentication')
if not auth_name:
raise errors.ConfigValidationException(
"No authentication configured for registration authority: %s" %
ra_name)
if not conf.authentication.get(auth_name):
raise errors.ConfigValidationException(
"Authentication method %s configured for registration authority "
"%s doesn't exist" % (auth_name, ra_name))
ca_name = ra_conf.get('signing_ca')
if not ca_name:
raise errors.ConfigValidationException(
"No signing CA configuration present for registration authority: "
"%s" % ra_name)
if not conf.signing_ca.get(ca_name):
raise errors.ConfigValidationException(
"Signing CA %s configured for registration authority %s doesn't "
"exist" % (ca_name, ra_name))
if not ra_conf.get("validators"):
raise errors.ConfigValidationException(
"No validators configured for registration authority: %s" %
ra_name)
ra_validators = ra_conf['validators']
for step in ra_validators.keys():
try:
jsonloader.conf.get_validator(step)
except KeyError:
raise errors.ConfigValidationException(
"Unknown validator <{}> found (for registration "
"authority {})".format(step, ra_name))
config_check_domains(ra_validators)
logger.info("Validators OK for registration authority: %s", ra_name)
ra_fixups = ra_conf.get('fixups', {})
for step in ra_fixups.keys():
try:
jsonloader.conf.get_fixup(step)
except KeyError:
raise errors.ConfigValidationException(
"Unknown fixup <{}> found (for registration "
"authority {})".format(step, ra_name))
logger.info("Fixups OK for registration authority: %s", ra_name)
def load_config():
"""Attempt to find and load a JSON configuration file.
We will search in various locations in order for a valid config file
to use:
- the contents of 'ANCHOR_CONF' environment variable
- a local 'config.json' file in the invocation folder
- a HOME/.config/anchor/config.json file
- a /etc/anchor/config.json file
"""
config_name = 'ANCHOR_CONF'
local_config_path = 'config.json'
user_config_path = os.path.join(
os.environ['HOME'], '.config', 'anchor', 'config.json')
prefix = os.environ.get('VIRTUAL_ENV', os.sep)
sys_config_path = os.path.join(prefix, 'etc', 'anchor', 'config.json')
if 'registration_authority' not in jsonloader.conf.config:
config_path = ""
if config_name in os.environ:
config_path = os.environ[config_name]
elif os.path.isfile(local_config_path):
config_path = local_config_path
elif os.path.isfile(user_config_path):
config_path = user_config_path
elif os.path.isfile(sys_config_path):
config_path = sys_config_path
logger = logging.getLogger("anchor")
logger.info("using config: {}".format(config_path))
jsonloader.conf.load_file_data(config_path)
jsonloader.conf.load_extensions()
def setup_app(config):
# initial logging, will be re-configured later
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
app_conf = dict(config.app)
load_config()
validate_config(jsonloader.conf)
audit.init_audit()
app = pecan.make_app(
app_conf.pop('root'),
logging=config.logging,
**app_conf
)
return paste.translogger.TransLogger(app, setup_console_handler=False)

File diff suppressed because it is too large Load Diff

View File

@ -1,344 +0,0 @@
# Auto-generated by asn1ate on 2015-12-17 15:14:22.594350
from pyasn1.type import univ
from pyasn1.type import char
from pyasn1.type import namedtype
from pyasn1.type import namedval
from pyasn1.type import tag
from pyasn1.type import constraint
from pyasn1.type import useful
from . import rfc3280
MAX=64
def _OID(*components):
output = []
for x in tuple(components):
if isinstance(x, univ.ObjectIdentifier):
output.extend(list(x))
else:
output.append(int(x))
return univ.ObjectIdentifier(output)
class ObjectDigestInfo(univ.Sequence):
pass
ObjectDigestInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('digestedObjectType', univ.Enumerated(namedValues=namedval.NamedValues(('publicKey', 0), ('publicKeyCert', 1), ('otherObjectTypes', 2)))),
namedtype.OptionalNamedType('otherObjectTypeID', univ.ObjectIdentifier()),
namedtype.NamedType('digestAlgorithm', rfc3280.AlgorithmIdentifier()),
namedtype.NamedType('objectDigest', univ.BitString())
)
class IssuerSerial(univ.Sequence):
pass
IssuerSerial.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuer', rfc3280.GeneralNames()),
namedtype.NamedType('serial', rfc3280.CertificateSerialNumber()),
namedtype.OptionalNamedType('issuerUID', rfc3280.UniqueIdentifier())
)
class TargetCert(univ.Sequence):
pass
TargetCert.componentType = namedtype.NamedTypes(
namedtype.NamedType('targetCertificate', IssuerSerial()),
namedtype.OptionalNamedType('targetName', rfc3280.GeneralName()),
namedtype.OptionalNamedType('certDigestInfo', ObjectDigestInfo())
)
class Target(univ.Choice):
pass
Target.componentType = namedtype.NamedTypes(
namedtype.NamedType('targetName', rfc3280.GeneralName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('targetGroup', rfc3280.GeneralName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.NamedType('targetCert', TargetCert().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
)
class Targets(univ.SequenceOf):
pass
Targets.componentType = Target()
class ProxyInfo(univ.SequenceOf):
pass
ProxyInfo.componentType = Targets()
id_at_role = _OID(rfc3280.id_at, 72)
id_pe_aaControls = _OID(rfc3280.id_pe, 6)
id_at_role = _OID(rfc3280.id_at, 72)
id_ce_targetInformation = _OID(rfc3280.id_ce, 55)
id_pe_ac_auditIdentity = _OID(rfc3280.id_pe, 4)
class ClassList(univ.BitString):
pass
ClassList.namedValues = namedval.NamedValues(
('unmarked', 0),
('unclassified', 1),
('restricted', 2),
('confidential', 3),
('secret', 4),
('topSecret', 5)
)
class SecurityCategory(univ.Sequence):
pass
SecurityCategory.componentType = namedtype.NamedTypes(
namedtype.NamedType('type', univ.ObjectIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('value', univ.Any().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
)
class Clearance(univ.Sequence):
pass
Clearance.componentType = namedtype.NamedTypes(
namedtype.NamedType('policyId', univ.ObjectIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.DefaultedNamedType('classList',
ClassList().subtype(implicitTag=tag.Tag(tag.tagClassContext,
tag.tagFormatSimple, 1)).subtype(value="unclassified")),
namedtype.OptionalNamedType('securityCategories', univ.SetOf(componentType=SecurityCategory()).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2)))
)
class AttCertVersion(univ.Integer):
pass
AttCertVersion.namedValues = namedval.NamedValues(
('v2', 1)
)
id_aca = _OID(rfc3280.id_pkix, 10)
id_at_clearance = _OID(2, 5, 1, 5, 55)
class AttrSpec(univ.SequenceOf):
pass
AttrSpec.componentType = univ.ObjectIdentifier()
class AAControls(univ.Sequence):
pass
AAControls.componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('pathLenConstraint', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, MAX))),
namedtype.OptionalNamedType('permittedAttrs', AttrSpec().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.OptionalNamedType('excludedAttrs', AttrSpec().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.DefaultedNamedType('permitUnSpecified', univ.Boolean().subtype(value=1))
)
id_aca = _OID(rfc3280.id_pkix, 10)
class AttCertValidityPeriod(univ.Sequence):
pass
AttCertValidityPeriod.componentType = namedtype.NamedTypes(
namedtype.NamedType('notBeforeTime', useful.GeneralizedTime()),
namedtype.NamedType('notAfterTime', useful.GeneralizedTime())
)
id_pe_ac_auditIdentity = _OID(rfc3280.id_pe, 4)
id_at_clearance = _OID(2, 5, 1, 5, 55)
id_aca_authenticationInfo = _OID(id_aca, 1)
class V2Form(univ.Sequence):
pass
V2Form.componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('issuerName', rfc3280.GeneralNames()),
namedtype.OptionalNamedType('baseCertificateID', IssuerSerial().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.OptionalNamedType('objectDigestInfo', ObjectDigestInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
)
class AttCertIssuer(univ.Choice):
pass
AttCertIssuer.componentType = namedtype.NamedTypes(
namedtype.NamedType('v1Form', rfc3280.GeneralNames()),
namedtype.NamedType('v2Form', V2Form().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
)
class Holder(univ.Sequence):
pass
Holder.componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('baseCertificateID', IssuerSerial().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.OptionalNamedType('entityName', rfc3280.GeneralNames().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.OptionalNamedType('objectDigestInfo', ObjectDigestInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
)
class AttributeCertificateInfo(univ.Sequence):
pass
AttributeCertificateInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', AttCertVersion()),
namedtype.NamedType('holder', Holder()),
namedtype.NamedType('issuer', AttCertIssuer()),
namedtype.NamedType('signature', rfc3280.AlgorithmIdentifier()),
namedtype.NamedType('serialNumber', rfc3280.CertificateSerialNumber()),
namedtype.NamedType('attrCertValidityPeriod', AttCertValidityPeriod()),
namedtype.NamedType('attributes', univ.SequenceOf(componentType=rfc3280.Attribute())),
namedtype.OptionalNamedType('issuerUniqueID', rfc3280.UniqueIdentifier()),
namedtype.OptionalNamedType('extensions', rfc3280.Extensions())
)
class AttributeCertificate(univ.Sequence):
pass
AttributeCertificate.componentType = namedtype.NamedTypes(
namedtype.NamedType('acinfo', AttributeCertificateInfo()),
namedtype.NamedType('signatureAlgorithm', rfc3280.AlgorithmIdentifier()),
namedtype.NamedType('signatureValue', univ.BitString())
)
id_aca_authenticationInfo = _OID(id_aca, 1)
id_mod = _OID(rfc3280.id_pkix, 0)
id_mod_attribute_cert = _OID(id_mod, 12)
id_aca_accessIdentity = _OID(id_aca, 2)
id_aca_accessIdentity = _OID(id_aca, 2)
class RoleSyntax(univ.Sequence):
pass
RoleSyntax.componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('roleAuthority', rfc3280.GeneralNames().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('roleName', rfc3280.GeneralName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
)
id_aca_chargingIdentity = _OID(id_aca, 3)
id_aca_chargingIdentity = _OID(id_aca, 3)
class ACClearAttrs(univ.Sequence):
pass
ACClearAttrs.componentType = namedtype.NamedTypes(
namedtype.NamedType('acIssuer', rfc3280.GeneralName()),
namedtype.NamedType('acSerial', univ.Integer()),
namedtype.NamedType('attrs', univ.SequenceOf(componentType=rfc3280.Attribute()))
)
id_ce_targetInformation = _OID(rfc3280.id_ce, 55)
id_aca_group = _OID(id_aca, 4)
id_aca_group = _OID(id_aca, 4)
id_pe_ac_proxying = _OID(rfc3280.id_pe, 10)
id_pe_aaControls = _OID(rfc3280.id_pe, 6)
class SvceAuthInfo(univ.Sequence):
pass
SvceAuthInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('service', rfc3280.GeneralName()),
namedtype.NamedType('ident', rfc3280.GeneralName()),
namedtype.OptionalNamedType('authInfo', univ.OctetString())
)
class IetfAttrSyntax(univ.Sequence):
pass
IetfAttrSyntax.componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('policyAuthority', rfc3280.GeneralNames().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('values', univ.SequenceOf(componentType=univ.Choice(componentType=namedtype.NamedTypes(
namedtype.NamedType('octets', univ.OctetString()),
namedtype.NamedType('oid', univ.ObjectIdentifier()),
namedtype.NamedType('string', char.UTF8String())
))
))
)
id_aca_encAttrs = _OID(id_aca, 6)
id_aca_encAttrs = _OID(id_aca, 6)
id_pe_ac_proxying = _OID(rfc3280.id_pe, 10)

View File

@ -1,663 +0,0 @@
# Auto-generated by asn1ate on 2015-12-18 17:39:54.470347
from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful
MAX = 64
from . import rfc3280
from . import rfc3281
def _OID(*components):
output = []
for x in tuple(components):
if isinstance(x, univ.ObjectIdentifier):
output.extend(list(x))
else:
output.append(int(x))
return univ.ObjectIdentifier(output)
class AttributeValue(univ.Any):
pass
class Attribute(univ.Sequence):
pass
Attribute.componentType = namedtype.NamedTypes(
namedtype.NamedType('attrType', univ.ObjectIdentifier()),
namedtype.NamedType('attrValues', univ.SetOf(componentType=AttributeValue()))
)
class SignedAttributes(univ.SetOf):
pass
SignedAttributes.componentType = Attribute()
SignedAttributes.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class OtherRevocationInfoFormat(univ.Sequence):
pass
OtherRevocationInfoFormat.componentType = namedtype.NamedTypes(
namedtype.NamedType('otherRevInfoFormat', univ.ObjectIdentifier()),
namedtype.NamedType('otherRevInfo', univ.Any())
)
class RevocationInfoChoice(univ.Choice):
pass
RevocationInfoChoice.componentType = namedtype.NamedTypes(
namedtype.NamedType('crl', rfc3280.CertificateList()),
namedtype.NamedType('other', OtherRevocationInfoFormat().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
)
class RevocationInfoChoices(univ.SetOf):
pass
RevocationInfoChoices.componentType = RevocationInfoChoice()
class OtherKeyAttribute(univ.Sequence):
pass
OtherKeyAttribute.componentType = namedtype.NamedTypes(
namedtype.NamedType('keyAttrId', univ.ObjectIdentifier()),
namedtype.OptionalNamedType('keyAttr', univ.Any())
)
id_signedData = _OID(1, 2, 840, 113549, 1, 7, 2)
class KeyEncryptionAlgorithmIdentifier(rfc3280.AlgorithmIdentifier):
pass
class EncryptedKey(univ.OctetString):
pass
class CMSVersion(univ.Integer):
pass
CMSVersion.namedValues = namedval.NamedValues(
('v0', 0),
('v1', 1),
('v2', 2),
('v3', 3),
('v4', 4),
('v5', 5)
)
class KEKIdentifier(univ.Sequence):
pass
KEKIdentifier.componentType = namedtype.NamedTypes(
namedtype.NamedType('keyIdentifier', univ.OctetString()),
namedtype.OptionalNamedType('date', useful.GeneralizedTime()),
namedtype.OptionalNamedType('other', OtherKeyAttribute())
)
class KEKRecipientInfo(univ.Sequence):
pass
KEKRecipientInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('kekid', KEKIdentifier()),
namedtype.NamedType('keyEncryptionAlgorithm', KeyEncryptionAlgorithmIdentifier()),
namedtype.NamedType('encryptedKey', EncryptedKey())
)
class KeyDerivationAlgorithmIdentifier(rfc3280.AlgorithmIdentifier):
pass
class PasswordRecipientInfo(univ.Sequence):
pass
PasswordRecipientInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.OptionalNamedType('keyDerivationAlgorithm', KeyDerivationAlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('keyEncryptionAlgorithm', KeyEncryptionAlgorithmIdentifier()),
namedtype.NamedType('encryptedKey', EncryptedKey())
)
class OtherRecipientInfo(univ.Sequence):
pass
OtherRecipientInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('oriType', univ.ObjectIdentifier()),
namedtype.NamedType('oriValue', univ.Any())
)
class IssuerAndSerialNumber(univ.Sequence):
pass
IssuerAndSerialNumber.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuer', rfc3280.Name()),
namedtype.NamedType('serialNumber', rfc3280.CertificateSerialNumber())
)
class SubjectKeyIdentifier(univ.OctetString):
pass
class RecipientKeyIdentifier(univ.Sequence):
pass
RecipientKeyIdentifier.componentType = namedtype.NamedTypes(
namedtype.NamedType('subjectKeyIdentifier', SubjectKeyIdentifier()),
namedtype.OptionalNamedType('date', useful.GeneralizedTime()),
namedtype.OptionalNamedType('other', OtherKeyAttribute())
)
class KeyAgreeRecipientIdentifier(univ.Choice):
pass
KeyAgreeRecipientIdentifier.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuerAndSerialNumber', IssuerAndSerialNumber()),
namedtype.NamedType('rKeyId', RecipientKeyIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
)
class RecipientEncryptedKey(univ.Sequence):
pass
RecipientEncryptedKey.componentType = namedtype.NamedTypes(
namedtype.NamedType('rid', KeyAgreeRecipientIdentifier()),
namedtype.NamedType('encryptedKey', EncryptedKey())
)
class RecipientEncryptedKeys(univ.SequenceOf):
pass
RecipientEncryptedKeys.componentType = RecipientEncryptedKey()
class UserKeyingMaterial(univ.OctetString):
pass
class OriginatorPublicKey(univ.Sequence):
pass
OriginatorPublicKey.componentType = namedtype.NamedTypes(
namedtype.NamedType('algorithm', rfc3280.AlgorithmIdentifier()),
namedtype.NamedType('publicKey', univ.BitString())
)
class OriginatorIdentifierOrKey(univ.Choice):
pass
OriginatorIdentifierOrKey.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuerAndSerialNumber', IssuerAndSerialNumber()),
namedtype.NamedType('subjectKeyIdentifier', SubjectKeyIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('originatorKey', OriginatorPublicKey().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
)
class KeyAgreeRecipientInfo(univ.Sequence):
pass
KeyAgreeRecipientInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('originator', OriginatorIdentifierOrKey().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.OptionalNamedType('ukm', UserKeyingMaterial().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.NamedType('keyEncryptionAlgorithm', KeyEncryptionAlgorithmIdentifier()),
namedtype.NamedType('recipientEncryptedKeys', RecipientEncryptedKeys())
)
class RecipientIdentifier(univ.Choice):
pass
RecipientIdentifier.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuerAndSerialNumber', IssuerAndSerialNumber()),
namedtype.NamedType('subjectKeyIdentifier', SubjectKeyIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
class KeyTransRecipientInfo(univ.Sequence):
pass
KeyTransRecipientInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('rid', RecipientIdentifier()),
namedtype.NamedType('keyEncryptionAlgorithm', KeyEncryptionAlgorithmIdentifier()),
namedtype.NamedType('encryptedKey', EncryptedKey())
)
class RecipientInfo(univ.Choice):
pass
RecipientInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('ktri', KeyTransRecipientInfo()),
namedtype.NamedType('kari', KeyAgreeRecipientInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
namedtype.NamedType('kekri', KEKRecipientInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
namedtype.NamedType('pwri', PasswordRecipientInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))),
namedtype.NamedType('ori', OtherRecipientInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4)))
)
class RecipientInfos(univ.SetOf):
pass
RecipientInfos.componentType = RecipientInfo()
RecipientInfos.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class DigestAlgorithmIdentifier(rfc3280.AlgorithmIdentifier):
pass
class Signature(univ.BitString):
pass
class SignerIdentifier(univ.Choice):
pass
SignerIdentifier.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuerAndSerialNumber', IssuerAndSerialNumber()),
namedtype.NamedType('subjectKeyIdentifier', SubjectKeyIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
class UnprotectedAttributes(univ.SetOf):
pass
UnprotectedAttributes.componentType = Attribute()
UnprotectedAttributes.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class ContentType(univ.ObjectIdentifier):
pass
class EncryptedContent(univ.OctetString):
pass
class ContentEncryptionAlgorithmIdentifier(rfc3280.AlgorithmIdentifier):
pass
class EncryptedContentInfo(univ.Sequence):
pass
EncryptedContentInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('contentType', ContentType()),
namedtype.NamedType('contentEncryptionAlgorithm', ContentEncryptionAlgorithmIdentifier()),
namedtype.OptionalNamedType('encryptedContent', EncryptedContent().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
class EncryptedData(univ.Sequence):
pass
EncryptedData.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('encryptedContentInfo', EncryptedContentInfo()),
namedtype.OptionalNamedType('unprotectedAttrs', UnprotectedAttributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
)
id_contentType = _OID(1, 2, 840, 113549, 1, 9, 3)
id_data = _OID(1, 2, 840, 113549, 1, 7, 1)
id_messageDigest = _OID(1, 2, 840, 113549, 1, 9, 4)
class DigestAlgorithmIdentifiers(univ.SetOf):
pass
DigestAlgorithmIdentifiers.componentType = DigestAlgorithmIdentifier()
class EncapsulatedContentInfo(univ.Sequence):
pass
EncapsulatedContentInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('eContentType', ContentType()),
namedtype.OptionalNamedType('eContent', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
class Digest(univ.OctetString):
pass
class DigestedData(univ.Sequence):
pass
DigestedData.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('digestAlgorithm', DigestAlgorithmIdentifier()),
namedtype.NamedType('encapContentInfo', EncapsulatedContentInfo()),
namedtype.NamedType('digest', Digest())
)
class ContentInfo(univ.Sequence):
pass
ContentInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('contentType', ContentType()),
namedtype.NamedType('content', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
class UnauthAttributes(univ.SetOf):
pass
UnauthAttributes.componentType = Attribute()
UnauthAttributes.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class ExtendedCertificateInfo(univ.Sequence):
pass
ExtendedCertificateInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('certificate', rfc3280.Certificate()),
namedtype.NamedType('attributes', UnauthAttributes())
)
class SignatureAlgorithmIdentifier(rfc3280.AlgorithmIdentifier):
pass
class ExtendedCertificate(univ.Sequence):
pass
ExtendedCertificate.componentType = namedtype.NamedTypes(
namedtype.NamedType('extendedCertificateInfo', ExtendedCertificateInfo()),
namedtype.NamedType('signatureAlgorithm', SignatureAlgorithmIdentifier()),
namedtype.NamedType('signature', Signature())
)
class OtherCertificateFormat(univ.Sequence):
pass
OtherCertificateFormat.componentType = namedtype.NamedTypes(
namedtype.NamedType('otherCertFormat', univ.ObjectIdentifier()),
namedtype.NamedType('otherCert', univ.Any())
)
class AttributeCertificateV2(rfc3281.AttributeCertificate):
pass
class AttCertVersionV1(univ.Integer):
pass
AttCertVersionV1.namedValues = namedval.NamedValues(
('v1', 0)
)
class AttributeCertificateInfoV1(univ.Sequence):
pass
AttributeCertificateInfoV1.componentType = namedtype.NamedTypes(
namedtype.DefaultedNamedType('version', AttCertVersionV1().subtype(value="v1")),
namedtype.NamedType('subject', univ.Choice(componentType=namedtype.NamedTypes(
namedtype.NamedType('baseCertificateID', rfc3281.IssuerSerial().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('subjectName', rfc3280.GeneralNames().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
))
),
namedtype.NamedType('issuer', rfc3280.GeneralNames()),
namedtype.NamedType('signature', rfc3280.AlgorithmIdentifier()),
namedtype.NamedType('serialNumber', rfc3280.CertificateSerialNumber()),
namedtype.NamedType('attCertValidityPeriod', rfc3281.AttCertValidityPeriod()),
namedtype.NamedType('attributes', univ.SequenceOf(componentType=rfc3280.Attribute())),
namedtype.OptionalNamedType('issuerUniqueID', rfc3280.UniqueIdentifier()),
namedtype.OptionalNamedType('extensions', rfc3280.Extensions())
)
class AttributeCertificateV1(univ.Sequence):
pass
AttributeCertificateV1.componentType = namedtype.NamedTypes(
namedtype.NamedType('acInfo', AttributeCertificateInfoV1()),
namedtype.NamedType('signatureAlgorithm', rfc3280.AlgorithmIdentifier()),
namedtype.NamedType('signature', univ.BitString())
)
class CertificateChoices(univ.Choice):
pass
CertificateChoices.componentType = namedtype.NamedTypes(
namedtype.NamedType('certificate', rfc3280.Certificate()),
namedtype.NamedType('extendedCertificate', ExtendedCertificate().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.NamedType('v1AttrCert', AttributeCertificateV1().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.NamedType('v2AttrCert', AttributeCertificateV2().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
namedtype.NamedType('other', OtherCertificateFormat().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
)
class CertificateSet(univ.SetOf):
pass
CertificateSet.componentType = CertificateChoices()
class MessageAuthenticationCode(univ.OctetString):
pass
class UnsignedAttributes(univ.SetOf):
pass
UnsignedAttributes.componentType = Attribute()
UnsignedAttributes.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class SignatureValue(univ.OctetString):
pass
class SignerInfo(univ.Sequence):
pass
SignerInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('sid', SignerIdentifier()),
namedtype.NamedType('digestAlgorithm', DigestAlgorithmIdentifier()),
namedtype.OptionalNamedType('signedAttrs', SignedAttributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('signatureAlgorithm', SignatureAlgorithmIdentifier()),
namedtype.NamedType('signature', SignatureValue()),
namedtype.OptionalNamedType('unsignedAttrs', UnsignedAttributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
)
class SignerInfos(univ.SetOf):
pass
SignerInfos.componentType = SignerInfo()
class SignedData(univ.Sequence):
pass
SignedData.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('digestAlgorithms', DigestAlgorithmIdentifiers()),
namedtype.NamedType('encapContentInfo', EncapsulatedContentInfo()),
namedtype.OptionalNamedType('certificates', CertificateSet().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.OptionalNamedType('crls', RevocationInfoChoices().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.NamedType('signerInfos', SignerInfos())
)
class MessageAuthenticationCodeAlgorithm(rfc3280.AlgorithmIdentifier):
pass
class MessageDigest(univ.OctetString):
pass
class Time(univ.Choice):
pass
Time.componentType = namedtype.NamedTypes(
namedtype.NamedType('utcTime', useful.UTCTime()),
namedtype.NamedType('generalTime', useful.GeneralizedTime())
)
class OriginatorInfo(univ.Sequence):
pass
OriginatorInfo.componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('certs', CertificateSet().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.OptionalNamedType('crls', RevocationInfoChoices().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
)
class AuthAttributes(univ.SetOf):
pass
AuthAttributes.componentType = Attribute()
AuthAttributes.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class AuthenticatedData(univ.Sequence):
pass
AuthenticatedData.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.OptionalNamedType('originatorInfo', OriginatorInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.NamedType('recipientInfos', RecipientInfos()),
namedtype.NamedType('macAlgorithm', MessageAuthenticationCodeAlgorithm()),
namedtype.OptionalNamedType('digestAlgorithm', DigestAlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.NamedType('encapContentInfo', EncapsulatedContentInfo()),
namedtype.OptionalNamedType('authAttrs', AuthAttributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
namedtype.NamedType('mac', MessageAuthenticationCode()),
namedtype.OptionalNamedType('unauthAttrs', UnauthAttributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)))
)
id_ct_contentInfo = _OID(1, 2, 840, 113549, 1, 9, 16, 1, 6)
id_envelopedData = _OID(1, 2, 840, 113549, 1, 7, 3)
class EnvelopedData(univ.Sequence):
pass
EnvelopedData.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.OptionalNamedType('originatorInfo', OriginatorInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.NamedType('recipientInfos', RecipientInfos()),
namedtype.NamedType('encryptedContentInfo', EncryptedContentInfo()),
namedtype.OptionalNamedType('unprotectedAttrs', UnprotectedAttributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
)
class Countersignature(SignerInfo):
pass
id_digestedData = _OID(1, 2, 840, 113549, 1, 7, 5)
id_signingTime = _OID(1, 2, 840, 113549, 1, 9, 5)
class ExtendedCertificateOrCertificate(univ.Choice):
pass
ExtendedCertificateOrCertificate.componentType = namedtype.NamedTypes(
namedtype.NamedType('certificate', rfc3280.Certificate()),
namedtype.NamedType('extendedCertificate', ExtendedCertificate().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
)
id_encryptedData = _OID(1, 2, 840, 113549, 1, 7, 6)
id_ct_authData = _OID(1, 2, 840, 113549, 1, 9, 16, 1, 2)
class SigningTime(Time):
pass
id_countersignature = _OID(1, 2, 840, 113549, 1, 9, 6)

View File

@ -1,349 +0,0 @@
# Auto-generated by asn1ate on 2015-12-21 15:05:42.666261
from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful
from . import rfc3280
from . import rfc3852
MAX = 64
def _OID(*components):
output = []
for x in tuple(components):
if isinstance(x, univ.ObjectIdentifier):
output.extend(list(x))
else:
output.append(int(x))
return univ.ObjectIdentifier(output)
id_pkix = _OID(1, 3, 6, 1, 5, 5, 7)
id_pkip = _OID(id_pkix, 5)
id_regCtrl = _OID(id_pkip, 1)
class SinglePubInfo(univ.Sequence):
pass
SinglePubInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('pubMethod', univ.Integer(namedValues=namedval.NamedValues(('dontCare', 0), ('x500', 1), ('web', 2), ('ldap', 3)))),
namedtype.OptionalNamedType('pubLocation', rfc3280.GeneralName())
)
class UTF8Pairs(char.UTF8String):
pass
class PKMACValue(univ.Sequence):
pass
PKMACValue.componentType = namedtype.NamedTypes(
namedtype.NamedType('algId', rfc3280.AlgorithmIdentifier()),
namedtype.NamedType('value', univ.BitString())
)
class POPOSigningKeyInput(univ.Sequence):
pass
POPOSigningKeyInput.componentType = namedtype.NamedTypes(
namedtype.NamedType('authInfo', univ.Choice(componentType=namedtype.NamedTypes(
namedtype.NamedType('sender', rfc3280.GeneralName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.NamedType('publicKeyMAC', PKMACValue())
))
),
namedtype.NamedType('publicKey', rfc3280.SubjectPublicKeyInfo())
)
class POPOSigningKey(univ.Sequence):
pass
POPOSigningKey.componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('poposkInput', POPOSigningKeyInput().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.NamedType('algorithmIdentifier', rfc3280.AlgorithmIdentifier()),
namedtype.NamedType('signature', univ.BitString())
)
class Attributes(univ.SetOf):
pass
Attributes.componentType = rfc3280.Attribute()
class PrivateKeyInfo(univ.Sequence):
pass
PrivateKeyInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', univ.Integer()),
namedtype.NamedType('privateKeyAlgorithm', rfc3280.AlgorithmIdentifier()),
namedtype.NamedType('privateKey', univ.OctetString()),
namedtype.OptionalNamedType('attributes', Attributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
class EncryptedValue(univ.Sequence):
pass
EncryptedValue.componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('intendedAlg', rfc3280.AlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.OptionalNamedType('symmAlg', rfc3280.AlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.OptionalNamedType('encSymmKey', univ.BitString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
namedtype.OptionalNamedType('keyAlg', rfc3280.AlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))),
namedtype.OptionalNamedType('valueHint', univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))),
namedtype.NamedType('encValue', univ.BitString())
)
class EncryptedKey(univ.Choice):
pass
EncryptedKey.componentType = namedtype.NamedTypes(
namedtype.NamedType('encryptedValue', EncryptedValue()),
namedtype.NamedType('envelopedData', rfc3852.EnvelopedData().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
class KeyGenParameters(univ.OctetString):
pass
class PKIArchiveOptions(univ.Choice):
pass
PKIArchiveOptions.componentType = namedtype.NamedTypes(
namedtype.NamedType('encryptedPrivKey', EncryptedKey().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.NamedType('keyGenParameters', KeyGenParameters().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.NamedType('archiveRemGenPrivKey', univ.Boolean().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2)))
)
id_regCtrl_authenticator = _OID(id_regCtrl, 2)
id_regInfo = _OID(id_pkip, 2)
id_regInfo_certReq = _OID(id_regInfo, 2)
class ProtocolEncrKey(rfc3280.SubjectPublicKeyInfo):
pass
class Authenticator(char.UTF8String):
pass
class SubsequentMessage(univ.Integer):
pass
SubsequentMessage.namedValues = namedval.NamedValues(
('encrCert', 0),
('challengeResp', 1)
)
class AttributeTypeAndValue(univ.Sequence):
pass
AttributeTypeAndValue.componentType = namedtype.NamedTypes(
namedtype.NamedType('type', univ.ObjectIdentifier()),
namedtype.NamedType('value', univ.Any())
)
class POPOPrivKey(univ.Choice):
pass
POPOPrivKey.componentType = namedtype.NamedTypes(
namedtype.NamedType('thisMessage', univ.BitString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('subsequentMessage', SubsequentMessage().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.NamedType('dhMAC', univ.BitString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
namedtype.NamedType('agreeMAC', PKMACValue().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))),
namedtype.NamedType('encryptedKey', rfc3852.EnvelopedData().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)))
)
class ProofOfPossession(univ.Choice):
pass
ProofOfPossession.componentType = namedtype.NamedTypes(
namedtype.NamedType('raVerified', univ.Null().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('signature', POPOSigningKey().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
namedtype.NamedType('keyEncipherment', POPOPrivKey().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
namedtype.NamedType('keyAgreement', POPOPrivKey().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
)
class OptionalValidity(univ.Sequence):
pass
OptionalValidity.componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('notBefore', rfc3280.Time().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.OptionalNamedType('notAfter', rfc3280.Time().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
)
class CertTemplate(univ.Sequence):
pass
CertTemplate.componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('version', rfc3280.Version().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.OptionalNamedType('serialNumber', univ.Integer().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.OptionalNamedType('signingAlg', rfc3280.AlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
namedtype.OptionalNamedType('issuer', rfc3280.Name().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))),
namedtype.OptionalNamedType('validity', OptionalValidity().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4))),
namedtype.OptionalNamedType('subject', rfc3280.Name().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 5))),
namedtype.OptionalNamedType('publicKey', rfc3280.SubjectPublicKeyInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 6))),
namedtype.OptionalNamedType('issuerUID', rfc3280.UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 7))),
namedtype.OptionalNamedType('subjectUID', rfc3280.UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 8))),
namedtype.OptionalNamedType('extensions', rfc3280.Extensions().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 9)))
)
class Controls(univ.SequenceOf):
pass
Controls.componentType = AttributeTypeAndValue()
Controls.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class CertRequest(univ.Sequence):
pass
CertRequest.componentType = namedtype.NamedTypes(
namedtype.NamedType('certReqId', univ.Integer()),
namedtype.NamedType('certTemplate', CertTemplate()),
namedtype.OptionalNamedType('controls', Controls())
)
class CertReqMsg(univ.Sequence):
pass
CertReqMsg.componentType = namedtype.NamedTypes(
namedtype.NamedType('certReq', CertRequest()),
namedtype.OptionalNamedType('popo', ProofOfPossession()),
namedtype.OptionalNamedType('regInfo', univ.SequenceOf(componentType=AttributeTypeAndValue()))
)
class CertReqMessages(univ.SequenceOf):
pass
CertReqMessages.componentType = CertReqMsg()
CertReqMessages.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class CertReq(CertRequest):
pass
id_regCtrl_pkiPublicationInfo = _OID(id_regCtrl, 3)
class CertId(univ.Sequence):
pass
CertId.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuer', rfc3280.GeneralName()),
namedtype.NamedType('serialNumber', univ.Integer())
)
class OldCertId(CertId):
pass
class PKIPublicationInfo(univ.Sequence):
pass
PKIPublicationInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('action', univ.Integer(namedValues=namedval.NamedValues(('dontPublish', 0), ('pleasePublish', 1)))),
namedtype.OptionalNamedType('pubInfos', univ.SequenceOf(componentType=SinglePubInfo()))
)
class EncKeyWithID(univ.Sequence):
pass
EncKeyWithID.componentType = namedtype.NamedTypes(
namedtype.NamedType('privateKey', PrivateKeyInfo()),
namedtype.OptionalNamedType('identifier', univ.Choice(componentType=namedtype.NamedTypes(
namedtype.NamedType('string', char.UTF8String()),
namedtype.NamedType('generalName', rfc3280.GeneralName())
))
)
)
id_regCtrl_protocolEncrKey = _OID(id_regCtrl, 6)
id_regCtrl_oldCertID = _OID(id_regCtrl, 5)
id_smime = _OID(1, 2, 840, 113549, 1, 9, 16)
class PBMParameter(univ.Sequence):
pass
PBMParameter.componentType = namedtype.NamedTypes(
namedtype.NamedType('salt', univ.OctetString()),
namedtype.NamedType('owf', rfc3280.AlgorithmIdentifier()),
namedtype.NamedType('iterationCount', univ.Integer()),
namedtype.NamedType('mac', rfc3280.AlgorithmIdentifier())
)
id_regCtrl_regToken = _OID(id_regCtrl, 1)
id_regCtrl_pkiArchiveOptions = _OID(id_regCtrl, 4)
id_regInfo_utf8Pairs = _OID(id_regInfo, 1)
id_ct = _OID(id_smime, 1)
id_ct_encKeyWithID = _OID(id_ct, 21)
class RegToken(char.UTF8String):
pass

File diff suppressed because it is too large Load Diff

View File

@ -1,666 +0,0 @@
from pyasn1.type import char
from pyasn1.type import constraint
from pyasn1.type import namedtype
from pyasn1.type import namedval
from pyasn1.type import tag
from pyasn1.type import univ
from pyasn1.type import useful
from . import rfc3281
from . import rfc5280
MAX = 64
def _OID(*components):
output = []
for x in tuple(components):
if isinstance(x, univ.ObjectIdentifier):
output.extend(list(x))
else:
output.append(int(x))
return univ.ObjectIdentifier(output)
class AttCertVersionV1(univ.Integer):
pass
AttCertVersionV1.namedValues = namedval.NamedValues(
('v1', 0)
)
class AttributeCertificateInfoV1(univ.Sequence):
pass
AttributeCertificateInfoV1.componentType = namedtype.NamedTypes(
namedtype.DefaultedNamedType('version', AttCertVersionV1().subtype(value="v1")),
namedtype.NamedType('subject', univ.Choice(componentType=namedtype.NamedTypes(
namedtype.NamedType('baseCertificateID', rfc3281.IssuerSerial().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('subjectName', rfc5280.GeneralNames().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
))
),
namedtype.NamedType('issuer', rfc5280.GeneralNames()),
namedtype.NamedType('signature', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('serialNumber', rfc5280.CertificateSerialNumber()),
namedtype.NamedType('attCertValidityPeriod', rfc3281.AttCertValidityPeriod()),
namedtype.NamedType('attributes', univ.SequenceOf(componentType=rfc5280.Attribute())),
namedtype.OptionalNamedType('issuerUniqueID', rfc5280.UniqueIdentifier()),
namedtype.OptionalNamedType('extensions', rfc5280.Extensions())
)
class AttributeCertificateV1(univ.Sequence):
pass
AttributeCertificateV1.componentType = namedtype.NamedTypes(
namedtype.NamedType('acInfo', AttributeCertificateInfoV1()),
namedtype.NamedType('signatureAlgorithm', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('signature', univ.BitString())
)
class AttributeValue(univ.Any):
pass
class Attribute(univ.Sequence):
pass
Attribute.componentType = namedtype.NamedTypes(
namedtype.NamedType('attrType', univ.ObjectIdentifier()),
namedtype.NamedType('attrValues', univ.SetOf(componentType=AttributeValue()))
)
class SignedAttributes(univ.SetOf):
pass
SignedAttributes.componentType = Attribute()
SignedAttributes.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class AttributeCertificateV2(rfc3281.AttributeCertificate):
pass
class OtherKeyAttribute(univ.Sequence):
pass
OtherKeyAttribute.componentType = namedtype.NamedTypes(
namedtype.NamedType('keyAttrId', univ.ObjectIdentifier()),
namedtype.OptionalNamedType('keyAttr', univ.Any())
)
class UnauthAttributes(univ.SetOf):
pass
UnauthAttributes.componentType = Attribute()
UnauthAttributes.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
id_encryptedData = _OID(1, 2, 840, 113549, 1, 7, 6)
class SignatureValue(univ.OctetString):
pass
class IssuerAndSerialNumber(univ.Sequence):
pass
IssuerAndSerialNumber.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuer', rfc5280.Name()),
namedtype.NamedType('serialNumber', rfc5280.CertificateSerialNumber())
)
class SubjectKeyIdentifier(univ.OctetString):
pass
class RecipientKeyIdentifier(univ.Sequence):
pass
RecipientKeyIdentifier.componentType = namedtype.NamedTypes(
namedtype.NamedType('subjectKeyIdentifier', SubjectKeyIdentifier()),
namedtype.OptionalNamedType('date', useful.GeneralizedTime()),
namedtype.OptionalNamedType('other', OtherKeyAttribute())
)
class KeyAgreeRecipientIdentifier(univ.Choice):
pass
KeyAgreeRecipientIdentifier.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuerAndSerialNumber', IssuerAndSerialNumber()),
namedtype.NamedType('rKeyId', RecipientKeyIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
)
class EncryptedKey(univ.OctetString):
pass
class RecipientEncryptedKey(univ.Sequence):
pass
RecipientEncryptedKey.componentType = namedtype.NamedTypes(
namedtype.NamedType('rid', KeyAgreeRecipientIdentifier()),
namedtype.NamedType('encryptedKey', EncryptedKey())
)
class RecipientEncryptedKeys(univ.SequenceOf):
pass
RecipientEncryptedKeys.componentType = RecipientEncryptedKey()
class MessageAuthenticationCode(univ.OctetString):
pass
class CMSVersion(univ.Integer):
pass
CMSVersion.namedValues = namedval.NamedValues(
('v0', 0),
('v1', 1),
('v2', 2),
('v3', 3),
('v4', 4),
('v5', 5)
)
class OtherCertificateFormat(univ.Sequence):
pass
OtherCertificateFormat.componentType = namedtype.NamedTypes(
namedtype.NamedType('otherCertFormat', univ.ObjectIdentifier()),
namedtype.NamedType('otherCert', univ.Any())
)
class ExtendedCertificateInfo(univ.Sequence):
pass
ExtendedCertificateInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('certificate', rfc5280.Certificate()),
namedtype.NamedType('attributes', UnauthAttributes())
)
class Signature(univ.BitString):
pass
class SignatureAlgorithmIdentifier(rfc5280.AlgorithmIdentifier):
pass
class ExtendedCertificate(univ.Sequence):
pass
ExtendedCertificate.componentType = namedtype.NamedTypes(
namedtype.NamedType('extendedCertificateInfo', ExtendedCertificateInfo()),
namedtype.NamedType('signatureAlgorithm', SignatureAlgorithmIdentifier()),
namedtype.NamedType('signature', Signature())
)
class CertificateChoices(univ.Choice):
pass
CertificateChoices.componentType = namedtype.NamedTypes(
namedtype.NamedType('certificate', rfc5280.Certificate()),
namedtype.NamedType('extendedCertificate', ExtendedCertificate().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.NamedType('v1AttrCert', AttributeCertificateV1().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.NamedType('v2AttrCert', AttributeCertificateV2().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
namedtype.NamedType('other', OtherCertificateFormat().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
)
class CertificateSet(univ.SetOf):
pass
CertificateSet.componentType = CertificateChoices()
class OtherRevocationInfoFormat(univ.Sequence):
pass
OtherRevocationInfoFormat.componentType = namedtype.NamedTypes(
namedtype.NamedType('otherRevInfoFormat', univ.ObjectIdentifier()),
namedtype.NamedType('otherRevInfo', univ.Any())
)
class RevocationInfoChoice(univ.Choice):
pass
RevocationInfoChoice.componentType = namedtype.NamedTypes(
namedtype.NamedType('crl', rfc5280.CertificateList()),
namedtype.NamedType('other', OtherRevocationInfoFormat().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
)
class RevocationInfoChoices(univ.SetOf):
pass
RevocationInfoChoices.componentType = RevocationInfoChoice()
class OriginatorInfo(univ.Sequence):
pass
OriginatorInfo.componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('certs', CertificateSet().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.OptionalNamedType('crls', RevocationInfoChoices().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
)
class ContentType(univ.ObjectIdentifier):
pass
class EncryptedContent(univ.OctetString):
pass
class ContentEncryptionAlgorithmIdentifier(rfc5280.AlgorithmIdentifier):
pass
class EncryptedContentInfo(univ.Sequence):
pass
EncryptedContentInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('contentType', ContentType()),
namedtype.NamedType('contentEncryptionAlgorithm', ContentEncryptionAlgorithmIdentifier()),
namedtype.OptionalNamedType('encryptedContent', EncryptedContent().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
class UnprotectedAttributes(univ.SetOf):
pass
UnprotectedAttributes.componentType = Attribute()
UnprotectedAttributes.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class KeyEncryptionAlgorithmIdentifier(rfc5280.AlgorithmIdentifier):
pass
class KEKIdentifier(univ.Sequence):
pass
KEKIdentifier.componentType = namedtype.NamedTypes(
namedtype.NamedType('keyIdentifier', univ.OctetString()),
namedtype.OptionalNamedType('date', useful.GeneralizedTime()),
namedtype.OptionalNamedType('other', OtherKeyAttribute())
)
class KEKRecipientInfo(univ.Sequence):
pass
KEKRecipientInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('kekid', KEKIdentifier()),
namedtype.NamedType('keyEncryptionAlgorithm', KeyEncryptionAlgorithmIdentifier()),
namedtype.NamedType('encryptedKey', EncryptedKey())
)
class KeyDerivationAlgorithmIdentifier(rfc5280.AlgorithmIdentifier):
pass
class PasswordRecipientInfo(univ.Sequence):
pass
PasswordRecipientInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.OptionalNamedType('keyDerivationAlgorithm', KeyDerivationAlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('keyEncryptionAlgorithm', KeyEncryptionAlgorithmIdentifier()),
namedtype.NamedType('encryptedKey', EncryptedKey())
)
class RecipientIdentifier(univ.Choice):
pass
RecipientIdentifier.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuerAndSerialNumber', IssuerAndSerialNumber()),
namedtype.NamedType('subjectKeyIdentifier', SubjectKeyIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
class KeyTransRecipientInfo(univ.Sequence):
pass
KeyTransRecipientInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('rid', RecipientIdentifier()),
namedtype.NamedType('keyEncryptionAlgorithm', KeyEncryptionAlgorithmIdentifier()),
namedtype.NamedType('encryptedKey', EncryptedKey())
)
class UserKeyingMaterial(univ.OctetString):
pass
class OriginatorPublicKey(univ.Sequence):
pass
OriginatorPublicKey.componentType = namedtype.NamedTypes(
namedtype.NamedType('algorithm', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('publicKey', univ.BitString())
)
class OriginatorIdentifierOrKey(univ.Choice):
pass
OriginatorIdentifierOrKey.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuerAndSerialNumber', IssuerAndSerialNumber()),
namedtype.NamedType('subjectKeyIdentifier', SubjectKeyIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('originatorKey', OriginatorPublicKey().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
)
class KeyAgreeRecipientInfo(univ.Sequence):
pass
KeyAgreeRecipientInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('originator', OriginatorIdentifierOrKey().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.OptionalNamedType('ukm', UserKeyingMaterial().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.NamedType('keyEncryptionAlgorithm', KeyEncryptionAlgorithmIdentifier()),
namedtype.NamedType('recipientEncryptedKeys', RecipientEncryptedKeys())
)
class OtherRecipientInfo(univ.Sequence):
pass
OtherRecipientInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('oriType', univ.ObjectIdentifier()),
namedtype.NamedType('oriValue', univ.Any())
)
class RecipientInfo(univ.Choice):
pass
RecipientInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('ktri', KeyTransRecipientInfo()),
namedtype.NamedType('kari', KeyAgreeRecipientInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
namedtype.NamedType('kekri', KEKRecipientInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
namedtype.NamedType('pwri', PasswordRecipientInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))),
namedtype.NamedType('ori', OtherRecipientInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4)))
)
class RecipientInfos(univ.SetOf):
pass
RecipientInfos.componentType = RecipientInfo()
RecipientInfos.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class EnvelopedData(univ.Sequence):
pass
EnvelopedData.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.OptionalNamedType('originatorInfo', OriginatorInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.NamedType('recipientInfos', RecipientInfos()),
namedtype.NamedType('encryptedContentInfo', EncryptedContentInfo()),
namedtype.OptionalNamedType('unprotectedAttrs', UnprotectedAttributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
)
class DigestAlgorithmIdentifier(rfc5280.AlgorithmIdentifier):
pass
id_ct_contentInfo = _OID(1, 2, 840, 113549, 1, 9, 16, 1, 6)
id_digestedData = _OID(1, 2, 840, 113549, 1, 7, 5)
class EncryptedData(univ.Sequence):
pass
EncryptedData.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('encryptedContentInfo', EncryptedContentInfo()),
namedtype.OptionalNamedType('unprotectedAttrs', UnprotectedAttributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
)
id_messageDigest = _OID(1, 2, 840, 113549, 1, 9, 4)
id_signedData = _OID(1, 2, 840, 113549, 1, 7, 2)
class MessageAuthenticationCodeAlgorithm(rfc5280.AlgorithmIdentifier):
pass
class UnsignedAttributes(univ.SetOf):
pass
UnsignedAttributes.componentType = Attribute()
UnsignedAttributes.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class SignerIdentifier(univ.Choice):
pass
SignerIdentifier.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuerAndSerialNumber', IssuerAndSerialNumber()),
namedtype.NamedType('subjectKeyIdentifier', SubjectKeyIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
class SignerInfo(univ.Sequence):
pass
SignerInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('sid', SignerIdentifier()),
namedtype.NamedType('digestAlgorithm', DigestAlgorithmIdentifier()),
namedtype.OptionalNamedType('signedAttrs', SignedAttributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.NamedType('signatureAlgorithm', SignatureAlgorithmIdentifier()),
namedtype.NamedType('signature', SignatureValue()),
namedtype.OptionalNamedType('unsignedAttrs', UnsignedAttributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
)
class SignerInfos(univ.SetOf):
pass
SignerInfos.componentType = SignerInfo()
class Countersignature(SignerInfo):
pass
class ContentInfo(univ.Sequence):
pass
ContentInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('contentType', ContentType()),
namedtype.NamedType('content', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
class EncapsulatedContentInfo(univ.Sequence):
pass
EncapsulatedContentInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('eContentType', ContentType()),
namedtype.OptionalNamedType('eContent', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
id_countersignature = _OID(1, 2, 840, 113549, 1, 9, 6)
id_data = _OID(1, 2, 840, 113549, 1, 7, 1)
class MessageDigest(univ.OctetString):
pass
class AuthAttributes(univ.SetOf):
pass
AuthAttributes.componentType = Attribute()
AuthAttributes.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class Time(univ.Choice):
pass
Time.componentType = namedtype.NamedTypes(
namedtype.NamedType('utcTime', useful.UTCTime()),
namedtype.NamedType('generalTime', useful.GeneralizedTime())
)
class AuthenticatedData(univ.Sequence):
pass
AuthenticatedData.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.OptionalNamedType('originatorInfo', OriginatorInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.NamedType('recipientInfos', RecipientInfos()),
namedtype.NamedType('macAlgorithm', MessageAuthenticationCodeAlgorithm()),
namedtype.OptionalNamedType('digestAlgorithm', DigestAlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.NamedType('encapContentInfo', EncapsulatedContentInfo()),
namedtype.OptionalNamedType('authAttrs', AuthAttributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
namedtype.NamedType('mac', MessageAuthenticationCode()),
namedtype.OptionalNamedType('unauthAttrs', UnauthAttributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)))
)
id_contentType = _OID(1, 2, 840, 113549, 1, 9, 3)
class ExtendedCertificateOrCertificate(univ.Choice):
pass
ExtendedCertificateOrCertificate.componentType = namedtype.NamedTypes(
namedtype.NamedType('certificate', rfc5280.Certificate()),
namedtype.NamedType('extendedCertificate', ExtendedCertificate().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
)
class Digest(univ.OctetString):
pass
class DigestedData(univ.Sequence):
pass
DigestedData.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('digestAlgorithm', DigestAlgorithmIdentifier()),
namedtype.NamedType('encapContentInfo', EncapsulatedContentInfo()),
namedtype.NamedType('digest', Digest())
)
id_envelopedData = _OID(1, 2, 840, 113549, 1, 7, 3)
class DigestAlgorithmIdentifiers(univ.SetOf):
pass
DigestAlgorithmIdentifiers.componentType = DigestAlgorithmIdentifier()
class SignedData(univ.Sequence):
pass
SignedData.componentType = namedtype.NamedTypes(
namedtype.NamedType('version', CMSVersion()),
namedtype.NamedType('digestAlgorithms', DigestAlgorithmIdentifiers()),
namedtype.NamedType('encapContentInfo', EncapsulatedContentInfo()),
namedtype.OptionalNamedType('certificates', CertificateSet().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
namedtype.OptionalNamedType('crls', RevocationInfoChoices().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.NamedType('signerInfos', SignerInfos())
)
id_signingTime = _OID(1, 2, 840, 113549, 1, 9, 5)
class SigningTime(Time):
pass
id_ct_authData = _OID(1, 2, 840, 113549, 1, 9, 16, 1, 2)

View File

@ -1,576 +0,0 @@
# Auto-generated by asn1ate on 2015-12-21 15:40:08.299576
from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful
from . import rfc4211
from . import rfc5280
from . import rfc5652
MAX = 64
def _OID(*components):
output = []
for x in tuple(components):
if isinstance(x, univ.ObjectIdentifier):
output.extend(list(x))
else:
output.append(int(x))
return univ.ObjectIdentifier(output)
class ChangeSubjectName(univ.Sequence):
pass
ChangeSubjectName.componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('subject', rfc5280.Name()),
namedtype.OptionalNamedType('subjectAlt', rfc5280.GeneralNames())
)
class AttributeValue(univ.Any):
pass
class CMCStatus(univ.Integer):
pass
CMCStatus.namedValues = namedval.NamedValues(
('success', 0),
('failed', 2),
('pending', 3),
('noSupport', 4),
('confirmRequired', 5),
('popRequired', 6),
('partial', 7)
)
class PendInfo(univ.Sequence):
pass
PendInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('pendToken', univ.OctetString()),
namedtype.NamedType('pendTime', useful.GeneralizedTime())
)
bodyIdMax = univ.Integer(4294967295)
class BodyPartID(univ.Integer):
pass
BodyPartID.subtypeSpec = constraint.ValueRangeConstraint(0, bodyIdMax)
class BodyPartPath(univ.SequenceOf):
pass
BodyPartPath.componentType = BodyPartID()
BodyPartPath.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
class BodyPartReference(univ.Choice):
pass
BodyPartReference.componentType = namedtype.NamedTypes(
namedtype.NamedType('bodyPartID', BodyPartID()),
namedtype.NamedType('bodyPartPath', BodyPartPath())
)
class CMCFailInfo(univ.Integer):
pass
CMCFailInfo.namedValues = namedval.NamedValues(
('badAlg', 0),
('badMessageCheck', 1),
('badRequest', 2),
('badTime', 3),
('badCertId', 4),
('unsupportedExt', 5),
('mustArchiveKeys', 6),
('badIdentity', 7),
('popRequired', 8),
('popFailed', 9),
('noKeyReuse', 10),
('internalCAError', 11),
('tryLater', 12),
('authDataFail', 13)
)
class CMCStatusInfoV2(univ.Sequence):
pass
CMCStatusInfoV2.componentType = namedtype.NamedTypes(
namedtype.NamedType('cMCStatus', CMCStatus()),
namedtype.NamedType('bodyList', univ.SequenceOf(componentType=BodyPartReference())),
namedtype.OptionalNamedType('statusString', char.UTF8String()),
namedtype.OptionalNamedType('otherInfo', univ.Choice(componentType=namedtype.NamedTypes(
namedtype.NamedType('failInfo', CMCFailInfo()),
namedtype.NamedType('pendInfo', PendInfo()),
namedtype.NamedType('extendedFailInfo', univ.Sequence(componentType=namedtype.NamedTypes(
namedtype.NamedType('failInfoOID', univ.ObjectIdentifier()),
namedtype.NamedType('failInfoValue', AttributeValue())
))
)
))
)
)
class GetCRL(univ.Sequence):
pass
GetCRL.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuerName', rfc5280.Name()),
namedtype.OptionalNamedType('cRLName', rfc5280.GeneralName()),
namedtype.OptionalNamedType('time', useful.GeneralizedTime()),
namedtype.OptionalNamedType('reasons', rfc5280.ReasonFlags())
)
id_pkix = _OID(1, 3, 6, 1, 5, 5, 7)
id_cmc = _OID(id_pkix, 7)
id_cmc_batchResponses = _OID(id_cmc, 29)
id_cmc_popLinkWitness = _OID(id_cmc, 23)
class PopLinkWitnessV2(univ.Sequence):
pass
PopLinkWitnessV2.componentType = namedtype.NamedTypes(
namedtype.NamedType('keyGenAlgorithm', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('macAlgorithm', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('witness', univ.OctetString())
)
id_cmc_popLinkWitnessV2 = _OID(id_cmc, 33)
id_cmc_identityProofV2 = _OID(id_cmc, 34)
id_cmc_revokeRequest = _OID(id_cmc, 17)
id_cmc_recipientNonce = _OID(id_cmc, 7)
class ControlsProcessed(univ.Sequence):
pass
ControlsProcessed.componentType = namedtype.NamedTypes(
namedtype.NamedType('bodyList', univ.SequenceOf(componentType=BodyPartReference()))
)
class CertificationRequest(univ.Sequence):
pass
CertificationRequest.componentType = namedtype.NamedTypes(
namedtype.NamedType('certificationRequestInfo', univ.Sequence(componentType=namedtype.NamedTypes(
namedtype.NamedType('version', univ.Integer()),
namedtype.NamedType('subject', rfc5280.Name()),
namedtype.NamedType('subjectPublicKeyInfo', univ.Sequence(componentType=namedtype.NamedTypes(
namedtype.NamedType('algorithm', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('subjectPublicKey', univ.BitString())
))
),
namedtype.NamedType('attributes', univ.SetOf(componentType=rfc5652.Attribute()).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
))
),
namedtype.NamedType('signatureAlgorithm', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('signature', univ.BitString())
)
class TaggedCertificationRequest(univ.Sequence):
pass
TaggedCertificationRequest.componentType = namedtype.NamedTypes(
namedtype.NamedType('bodyPartID', BodyPartID()),
namedtype.NamedType('certificationRequest', CertificationRequest())
)
class TaggedRequest(univ.Choice):
pass
TaggedRequest.componentType = namedtype.NamedTypes(
namedtype.NamedType('tcr', TaggedCertificationRequest().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
namedtype.NamedType('crm', rfc4211.CertReqMsg().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
namedtype.NamedType('orm', univ.Sequence(componentType=namedtype.NamedTypes(
namedtype.NamedType('bodyPartID', BodyPartID()),
namedtype.NamedType('requestMessageType', univ.ObjectIdentifier()),
namedtype.NamedType('requestMessageValue', univ.Any())
))
.subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
)
id_cmc_popLinkRandom = _OID(id_cmc, 22)
id_cmc_statusInfo = _OID(id_cmc, 1)
id_cmc_trustedAnchors = _OID(id_cmc, 26)
id_cmc_transactionId = _OID(id_cmc, 5)
id_cmc_encryptedPOP = _OID(id_cmc, 9)
class PublishTrustAnchors(univ.Sequence):
pass
PublishTrustAnchors.componentType = namedtype.NamedTypes(
namedtype.NamedType('seqNumber', univ.Integer()),
namedtype.NamedType('hashAlgorithm', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('anchorHashes', univ.SequenceOf(componentType=univ.OctetString()))
)
class RevokeRequest(univ.Sequence):
pass
RevokeRequest.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuerName', rfc5280.Name()),
namedtype.NamedType('serialNumber', univ.Integer()),
namedtype.NamedType('reason', rfc5280.CRLReason()),
namedtype.OptionalNamedType('invalidityDate', useful.GeneralizedTime()),
namedtype.OptionalNamedType('passphrase', univ.OctetString()),
namedtype.OptionalNamedType('comment', char.UTF8String())
)
id_cmc_senderNonce = _OID(id_cmc, 6)
id_cmc_authData = _OID(id_cmc, 27)
class TaggedContentInfo(univ.Sequence):
pass
TaggedContentInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('bodyPartID', BodyPartID()),
namedtype.NamedType('contentInfo', rfc5652.ContentInfo())
)
class IdentifyProofV2(univ.Sequence):
pass
IdentifyProofV2.componentType = namedtype.NamedTypes(
namedtype.NamedType('proofAlgID', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('macAlgId', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('witness', univ.OctetString())
)
class CMCPublicationInfo(univ.Sequence):
pass
CMCPublicationInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('hashAlg', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('certHashes', univ.SequenceOf(componentType=univ.OctetString())),
namedtype.NamedType('pubInfo', rfc4211.PKIPublicationInfo())
)
id_kp_cmcCA = _OID(rfc5280.id_kp, 27)
id_cmc_confirmCertAcceptance = _OID(id_cmc, 24)
id_cmc_raIdentityWitness = _OID(id_cmc, 35)
id_ExtensionReq = _OID(1, 2, 840, 113549, 1, 9, 14)
id_cct = _OID(id_pkix, 12)
id_cct_PKIData = _OID(id_cct, 2)
id_kp_cmcRA = _OID(rfc5280.id_kp, 28)
class CMCStatusInfo(univ.Sequence):
pass
CMCStatusInfo.componentType = namedtype.NamedTypes(
namedtype.NamedType('cMCStatus', CMCStatus()),
namedtype.NamedType('bodyList', univ.SequenceOf(componentType=BodyPartID())),
namedtype.OptionalNamedType('statusString', char.UTF8String()),
namedtype.OptionalNamedType('otherInfo', univ.Choice(componentType=namedtype.NamedTypes(
namedtype.NamedType('failInfo', CMCFailInfo()),
namedtype.NamedType('pendInfo', PendInfo())
))
)
)
class DecryptedPOP(univ.Sequence):
pass
DecryptedPOP.componentType = namedtype.NamedTypes(
namedtype.NamedType('bodyPartID', BodyPartID()),
namedtype.NamedType('thePOPAlgID', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('thePOP', univ.OctetString())
)
id_cmc_addExtensions = _OID(id_cmc, 8)
id_cmc_modCertTemplate = _OID(id_cmc, 31)
class TaggedAttribute(univ.Sequence):
pass
TaggedAttribute.componentType = namedtype.NamedTypes(
namedtype.NamedType('bodyPartID', BodyPartID()),
namedtype.NamedType('attrType', univ.ObjectIdentifier()),
namedtype.NamedType('attrValues', univ.SetOf(componentType=AttributeValue()))
)
class OtherMsg(univ.Sequence):
pass
OtherMsg.componentType = namedtype.NamedTypes(
namedtype.NamedType('bodyPartID', BodyPartID()),
namedtype.NamedType('otherMsgType', univ.ObjectIdentifier()),
namedtype.NamedType('otherMsgValue', univ.Any())
)
class PKIData(univ.Sequence):
pass
PKIData.componentType = namedtype.NamedTypes(
namedtype.NamedType('controlSequence', univ.SequenceOf(componentType=TaggedAttribute())),
namedtype.NamedType('reqSequence', univ.SequenceOf(componentType=TaggedRequest())),
namedtype.NamedType('cmsSequence', univ.SequenceOf(componentType=TaggedContentInfo())),
namedtype.NamedType('otherMsgSequence', univ.SequenceOf(componentType=OtherMsg()))
)
class BodyPartList(univ.SequenceOf):
pass
BodyPartList.componentType = BodyPartID()
BodyPartList.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
id_cmc_responseBody = _OID(id_cmc, 37)
class AuthPublish(BodyPartID):
pass
class CMCUnsignedData(univ.Sequence):
pass
CMCUnsignedData.componentType = namedtype.NamedTypes(
namedtype.NamedType('bodyPartPath', BodyPartPath()),
namedtype.NamedType('identifier', univ.ObjectIdentifier()),
namedtype.NamedType('content', univ.Any())
)
class CMCCertId(rfc5652.IssuerAndSerialNumber):
pass
class PKIResponse(univ.Sequence):
pass
PKIResponse.componentType = namedtype.NamedTypes(
namedtype.NamedType('controlSequence', univ.SequenceOf(componentType=TaggedAttribute())),
namedtype.NamedType('cmsSequence', univ.SequenceOf(componentType=TaggedContentInfo())),
namedtype.NamedType('otherMsgSequence', univ.SequenceOf(componentType=OtherMsg()))
)
class ResponseBody(PKIResponse):
pass
id_cmc_statusInfoV2 = _OID(id_cmc, 25)
id_cmc_lraPOPWitness = _OID(id_cmc, 11)
class ModCertTemplate(univ.Sequence):
pass
ModCertTemplate.componentType = namedtype.NamedTypes(
namedtype.NamedType('pkiDataReference', BodyPartPath()),
namedtype.NamedType('certReferences', BodyPartList()),
namedtype.DefaultedNamedType('replace', univ.Boolean().subtype(value=1)),
namedtype.NamedType('certTemplate', rfc4211.CertTemplate())
)
id_cmc_regInfo = _OID(id_cmc, 18)
id_cmc_identityProof = _OID(id_cmc, 3)
class ExtensionReq(univ.SequenceOf):
pass
ExtensionReq.componentType = rfc5280.Extension()
ExtensionReq.subtypeSpec=constraint.ValueSizeConstraint(1, MAX)
id_kp_cmcArchive = _OID(rfc5280.id_kp, 28)
id_cmc_publishCert = _OID(id_cmc, 30)
id_cmc_dataReturn = _OID(id_cmc, 4)
class LraPopWitness(univ.Sequence):
pass
LraPopWitness.componentType = namedtype.NamedTypes(
namedtype.NamedType('pkiDataBodyid', BodyPartID()),
namedtype.NamedType('bodyIds', univ.SequenceOf(componentType=BodyPartID()))
)
id_aa = _OID(1, 2, 840, 113549, 1, 9, 16, 2)
id_aa_cmc_unsignedData = _OID(id_aa, 34)
id_cmc_getCert = _OID(id_cmc, 15)
id_cmc_batchRequests = _OID(id_cmc, 28)
id_cmc_decryptedPOP = _OID(id_cmc, 10)
id_cmc_responseInfo = _OID(id_cmc, 19)
id_cmc_changeSubjectName = _OID(id_cmc, 36)
class GetCert(univ.Sequence):
pass
GetCert.componentType = namedtype.NamedTypes(
namedtype.NamedType('issuerName', rfc5280.GeneralName()),
namedtype.NamedType('serialNumber', univ.Integer())
)
id_cmc_identification = _OID(id_cmc, 2)
id_cmc_queryPending = _OID(id_cmc, 21)
class AddExtensions(univ.Sequence):
pass
AddExtensions.componentType = namedtype.NamedTypes(
namedtype.NamedType('pkiDataReference', BodyPartID()),
namedtype.NamedType('certReferences', univ.SequenceOf(componentType=BodyPartID())),
namedtype.NamedType('extensions', univ.SequenceOf(componentType=rfc5280.Extension()))
)
class EncryptedPOP(univ.Sequence):
pass
EncryptedPOP.componentType = namedtype.NamedTypes(
namedtype.NamedType('request', TaggedRequest()),
namedtype.NamedType('cms', rfc5652.ContentInfo()),
namedtype.NamedType('thePOPAlgID', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('witnessAlgID', rfc5280.AlgorithmIdentifier()),
namedtype.NamedType('witness', univ.OctetString())
)
id_cmc_getCRL = _OID(id_cmc, 16)
id_cct_PKIResponse = _OID(id_cct, 3)
id_cmc_controlProcessed = _OID(id_cmc, 32)
class NoSignatureValue(univ.OctetString):
pass
id_ad_cmc = _OID(rfc5280.id_ad, 12)
id_alg_noSignature = _OID(id_pkix, 6, 2)

View File

@ -1,132 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
import uuid
from anchor import jsonloader
import oslo_config
import oslo_messaging
from pycadf import cadftaxonomy
from pycadf import event
from pycadf import identifier
from pycadf import resource
logger = logging.getLogger(__name__)
target = None
notifier = None
ANCHOR_UUID_NS = uuid.UUID('0ff9c8c5-f57e-47aa-bd3d-5407eb907c74')
def _emit_event(event_type, payload):
if not payload.is_valid():
logger.error("created invalid audit event: %s", payload)
return
if notifier is not None:
notifier.info({}, event_type, payload.as_dict())
def _event_defaults(result):
# eventType, id, eventTime are filled in automatically by pyCADF
return {
'outcome': (cadftaxonomy.OUTCOME_SUCCESS if result else
cadftaxonomy.OUTCOME_FAILURE),
}
def _user_resource(username, result):
if result:
res_id = uuid.uuid5(ANCHOR_UUID_NS, result.username)
user = result.username
else:
if username:
res_id = uuid.uuid5(ANCHOR_UUID_NS, username.encode('utf-8',
'replace'))
user = username
else:
# Authentication was a failure, but there was no username
# provided either. This can happen with failed token authentication
# for example.
res_id = uuid.uuid4()
user = None
return resource.Resource(
id=str(res_id),
typeURI=cadftaxonomy.ACCOUNT_USER,
name=user)
def _auth_resource(ra_name):
return resource.Resource(
id='anchor://authentication',
typeURI=cadftaxonomy.SERVICE_SECURITY,
domain=ra_name)
def _policy_resource(ra_name):
return resource.Resource(
id='anchor://certificates/policy',
typeURI=cadftaxonomy.SECURITY_POLICY,
domain=ra_name)
def _certificate_resource(fingerprint):
if fingerprint is None:
res_id = identifier.generate_uuid()
else:
res_id = "certificate:%s" % (fingerprint,)
return resource.Resource(
id=res_id,
typeURI=cadftaxonomy.SECURITY_KEY,
)
def emit_auth_event(ra_name, username, result):
success = result is not None
params = _event_defaults(success)
params['action'] = 'authenticate'
params['initiator'] = _user_resource(username, result)
auth_res = _auth_resource(ra_name)
params['observer'] = auth_res
params['target'] = auth_res
_emit_event('audit.auth', event.Event(**params))
def emit_signing_event(ra_name, username, result, fingerprint=None):
params = _event_defaults(result)
params['action'] = 'evaluate'
params['initiator'] = _user_resource(username, result)
params['observer'] = _policy_resource(ra_name)
params['target'] = _certificate_resource(fingerprint)
# add when pycadf merges event names
# params['name'] = "certificate signing"
_emit_event('audit.sign', event.Event(**params))
def init_audit():
global target
global notifier
audit_conf = jsonloader.config_for_audit()
if audit_conf is None:
return
target = audit_conf.get('target', 'log')
cfg = oslo_config.cfg.ConfigOpts()
if target == 'messaging':
transport = oslo_messaging.get_transport(cfg, url=audit_conf['url'])
else:
transport = oslo_messaging.get_transport(cfg)
notifier = oslo_messaging.Notifier(transport, 'anchor', driver=target)

View File

@ -1,44 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import pecan
from anchor.auth import keystone # noqa
from anchor.auth import ldap # noqa
from anchor.auth import static # noqa
from anchor import jsonloader
def validate(ra_name, user, secret):
"""Top-level authN entry point.
This will return an AuthDetails object or abort. This will only
check that a single auth method. That method will either succeed
or fail.
:param ra_name: name of the registration authority
:param user: user provided user name
:param secret: user provided secret (password or token)
:return: AuthDetails if authenticated or aborts
"""
auth_conf = jsonloader.authentication_for_registration_authority(ra_name)
backend_name = auth_conf['backend']
backend = jsonloader.conf.get_authentication(backend_name)
res = backend(ra_name, user, secret)
if res:
return res
# we should only get here if a module failed to abort
pecan.abort(401, "authentication failure")

View File

@ -1,55 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import logging
import requests
from anchor.auth import results
from anchor import jsonloader
logger = logging.getLogger(__name__)
def login(_, token):
"""Authenticate with the keystone endpoint from configuration file
:param token: A Keystone Token
:returns: AuthDetails -- Class used for authentication information
"""
req = requests.get(jsonloader.conf.auth['keystone']['url'] +
'/v3/auth/tokens',
headers={'X-Auth-Token': token,
'X-Subject-Token': token})
if req.status_code != 200:
logger.info("Authentication failed for token <%s>, status %s",
token, req.status_code)
return None
try:
res = req.json()
user = res['token']['user']
user_name = user['name']
user_id = user['id']
project_id = res['token']['project']['id']
roles = [role['name'] for role in res['token']['roles']]
except Exception:
logger.exception("Keystone response was not in the expected format")
return None
return results.AuthDetails(username=user_name, groups=roles,
user_id=user_id, project_id=project_id)

View File

@ -1,75 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import logging
import ldap3
from ldap3.core import exceptions as ldap3_exc
from ldap3.utils import dn
from anchor.auth import results
from anchor import jsonloader
logger = logging.getLogger(__name__)
def user_get_groups(attributes):
"""Retrieve the group membership
:param attributes: LDAP attributes for user
:returns: List -- A list of groups that the user is a member of
"""
groups = attributes.get('memberOf', [])
group_dns = [dn.parse_dn(g) for g in groups]
return [x[0][1] for x in group_dns if x[1] == ('OU', 'Groups', ',')]
def login(ra_name, user, secret):
"""Attempt to Authenitcate user using LDAP
:param ra_name: name of registration authority
:param user: Username
:param secret: Secret/Passphrase
:returns: AuthDetails -- Class used for authentication information
"""
conf = jsonloader.authentication_for_registration_authority(ra_name)
ldap_port = int(conf.get('port', 389))
use_ssl = conf.get('ssl', ldap_port == 636)
lds = ldap3.Server(conf['host'], port=ldap_port,
get_info=ldap3.ALL, use_ssl=use_ssl)
try:
ldap_user = "%s@%s" % (user, conf['domain'])
ldc = ldap3.Connection(lds, auto_bind=True, client_strategy=ldap3.SYNC,
user=ldap_user, password=secret,
authentication=ldap3.SIMPLE, check_names=True)
filter_str = ('(sAMAccountName=%s)' %
ldap3.utils.conv.escape_bytes(user))
ldc.search(conf['base'], filter_str,
ldap3.SUBTREE, attributes=['memberOf'])
if ldc.result['result'] != 0:
return None
user_attrs = ldc.response[0]['attributes']
user_groups = user_get_groups(user_attrs)
return results.AuthDetails(username=user, groups=user_groups)
except ldap3_exc.LDAPSocketOpenError:
logger.error("cannot connect to LDAP host '%s' (authority '%s')",
conf['host'], ra_name)
return None
except ldap3_exc.LDAPBindError:
logger.info("failed ldap auth for user %s", user)
return None

View File

@ -1,32 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
class AuthDetails(object):
def __init__(self, username=None, groups=None, user_id=None,
project_id=None):
self.username = username
self.groups = groups or []
self.user_id = user_id
self.project_id = project_id
def __eq__(self, other):
return (self.username == other.username and
self.groups == other.groups and
self.user_id == other.user_id and
self.project_id == other.project_id)
def __ne__(self, other):
return not self.__eq__(other)

View File

@ -1,69 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import logging
from anchor.auth import results
from anchor import jsonloader
from oslo_utils import secretutils as util
logger = logging.getLogger(__name__)
def login(ra_name, user, secret):
"""Validates a user supplied user/password against an expected value.
The expected value is pulled from the pecan config. Note that this
value is currently stored in the clear inside that config, so we
are assuming that the config is protected using file perms, etc.
This function provides some resistance to timing attacks, but
information on the expected user/password lengths can still be
leaked. It may also be possible to use a timing attack to see
which input failed validation. See comments below for details.
:param ra_name: name of the registration authority
:param user: The user supplied username (unicode or string)
:param secret: The user supplied password (unicode or string)
:return: None on failure or an AuthDetails object on success
"""
auth_conf = jsonloader.authentication_for_registration_authority(ra_name)
# convert input to strings
user = str(user)
secret = str(secret)
# expected values
try:
expected_user = str(auth_conf['user'])
expected_secret = str(auth_conf['secret'])
except (KeyError, TypeError):
logger.warning("auth conf missing static user or secret")
return None
# This technique is used to provide a constant time string compare
# between the user input and the expected values.
valid_user = util.constant_time_compare(user, expected_user)
valid_secret = util.constant_time_compare(secret, expected_secret)
# This if statement results in a potential timing attack where the
# statement could return more quickly if valid_secret=False. We
# do not see an obvious solution to this problem, but also believe
# that leaking which input was valid isn't as big of a concern.
if valid_user and valid_secret:
return results.AuthDetails(username=expected_user, groups=[])
logger.info("failed static auth for user {}".format(user))

View File

@ -1,201 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import logging
import os
import pecan
from webob import exc as http_status
from anchor import cmc
from anchor import jsonloader
from anchor import util
from anchor import validation
from anchor.X509 import certificate
from anchor.X509 import signing_request
logger = logging.getLogger(__name__)
# we only support the PEM encoding for now, but this may grow
# to support things like DER in the future
VALID_ENCODINGS = ['pem']
def parse_csr(data, encoding):
"""Loads the user provided CSR into the backend X509 library.
:param data: CSR as provided by the API user
:param encoding: encoding for the CSR (must be PEM today)
:return: CSR object from backend X509 library or aborts
"""
# validate untrusted input
if str(encoding).lower() not in VALID_ENCODINGS:
logger.error("parse_csr failed: bad encoding ({})".format(encoding))
pecan.abort(400, "invalid CSR")
if data is None:
logger.error("parse_csr failed: missing CSR")
pecan.abort(400, "invalid CSR")
# get DER version
der = util.extract_pem(data.encode('ascii'))
if der is None:
logger.error("parse_csr failed: PEM contents not found")
pecan.abort(400, "PEM contents not found")
# try to unpack the certificate from CMC wrappers
try:
csr = cmc.parse_request(der)
return signing_request.X509Csr(csr)
except cmc.CMCParsingError:
# it's not CMC data, that's fine, it's likely the CSR itself
try:
return signing_request.X509Csr.from_buffer(der, 'der')
except Exception as e:
logger.exception("Exception while parsing the CSR: %s", e)
pecan.abort(400, "CSR cannot be parsed")
def validate_csr(ra_name, auth_result, csr, request):
"""Validates various aspects of the CSR based on the loaded config.
The arguments of this method are passed to the underlying validate
methods. Therefore, some may be optional, depending on which
validation routines are specified in the configuration.
:param ra_name: name of the registration authority
:param auth_result: AuthDetails value from auth.validate
:param csr: CSR value from certificate_ops.parse_csr
:param request: pecan request object associated with this action
"""
try:
valid = validation.validate_csr(ra_name, auth_result, csr, request)
except Exception as e:
logger.exception("Error running validators: %s", e)
pecan.abort(500, "Internal Validation Error")
if not all(list(valid.values())):
pecan.abort(400, "CSR failed validation")
def certificate_fingerprint(cert_pem, hash_name):
"""Get certificate fingerprint."""
cert = certificate.X509Certificate.from_buffer(cert_pem)
return cert.get_fingerprint(hash_name)
def get_ca(ra_name):
ca_conf = jsonloader.signing_ca_for_registration_authority(ra_name)
ca_path = ca_conf.get('cert_path')
if not ca_path:
pecan.abort(404, "CA certificate not available")
try:
with open(ca_path) as f:
return f.read()
except IOError:
pecan.abort(500, "CA certificate not available")
def dispatch_sign(ra_name, csr):
"""Dispatch the sign call to the configured backend.
:param csr: X509 certificate signing request
:return: signed certificate in PEM format
"""
ca_conf = jsonloader.signing_ca_for_registration_authority(ra_name)
backend_name = ca_conf.get('backend', 'anchor')
sign_func = jsonloader.conf.get_signing_backend(backend_name)
try:
cert_pem = sign_func(csr, ca_conf)
except http_status.HTTPException:
logger.exception("Failed to sign certificate")
raise
except Exception:
logger.exception("Failed to sign the certificate")
pecan.abort(500, "certificate signing error")
fingerprint = certificate_fingerprint(cert_pem, 'sha256')
if ca_conf.get('output_path') is not None:
path = os.path.join(
ca_conf['output_path'],
'%s.crt' % fingerprint)
logger.info("Saving certificate to: %s", path)
with open(path, "w") as f:
f.write(cert_pem)
return cert_pem, fingerprint
def _run_fixup(name, body, args):
"""Parse the fixup tuple, call the fixup, and return the new csr.
:param name: the fixup name
:param body: fixup body, directly from config
:param args: additional arguments to pass to the fixup function
:return: the fixed csr
"""
# careful to not modify the master copy of args with local params
new_kwargs = args.copy()
new_kwargs.update(body)
# perform the actual check
logger.debug("_run_fixup: fixup <%s> with arguments: %s", name, body)
try:
fixup = jsonloader.conf.get_fixup(name)
new_csr = fixup(**new_kwargs)
logger.debug("_run_fixup: success: <%s> ", name)
return new_csr
except Exception:
logger.exception("_run_fixup: failed: <%s>", name)
return None
def fixup_csr(ra_name, csr, request):
"""Apply configured changes to the certificate.
:param ra_name: registration authority name
:param csr: X509 certificate signing request
:param request: pecan request
"""
ra_conf = jsonloader.config_for_registration_authority(ra_name)
args = {'csr': csr,
'conf': ra_conf,
'request': request}
fixups = ra_conf.get('fixups', {})
try:
for fixup_name, fixup in fixups.items():
new_csr = _run_fixup(fixup_name, fixup, args)
if new_csr is None:
pecan.abort(500, "Could not finish all required modifications")
if not isinstance(new_csr, signing_request.X509Csr):
logger.error("Fixup %s returned incorrect object", fixup_name)
pecan.abort(500, "Could not finish all required modifications")
args['csr'] = new_csr
except http_status.HTTPInternalServerError:
raise
except Exception:
logger.exception("Failed to execute fixups")
pecan.abort(500, "Could not finish all required modifications")
return args['csr']

View File

@ -1,80 +0,0 @@
from anchor.asn1 import rfc5652
from anchor.asn1 import rfc6402
from pyasn1.codec.der import decoder
from pyasn1 import error
class CMCParsingError(Exception):
pass
class UnexpectedContentType(CMCParsingError):
def __init__(self, content_type):
self.content_type = content_type
def __str__(self):
return "Unexpected content type, got %s" % self.content_type
def _unwrap_signed_data(data):
# Since we don't have trust with anyone signing the requests, this
# signature is not relevant. The request itself is self-signed which
# stops accidents.
result = decoder.decode(data, rfc5652.SignedData())[0]
return _unwrap_generic(
result['encapContentInfo']['eContentType'],
result['encapContentInfo']['eContent'])
def _unwrap_content_info(data):
result = decoder.decode(data, rfc5652.ContentInfo())[0]
return _unwrap_generic(result['contentType'], result['content'])
def _unwrap_generic(content_type, data):
unwrapper = CONTENT_TYPES.get(content_type)
if unwrapper is None:
return (content_type, data)
return unwrapper(data)
def strip_wrappers(data):
# assume the outer wrapper is contentinfo
return _unwrap_content_info(data)
CONTENT_TYPES = {
rfc5652.id_ct_contentInfo: _unwrap_content_info,
rfc5652.id_signedData: _unwrap_signed_data,
}
def parse_request(data):
try:
content_type, data = strip_wrappers(data)
except error.PyAsn1Error:
raise CMCParsingError("Cannot find valid CMC wrapper")
if content_type != rfc6402.id_cct_PKIData:
raise UnexpectedContentType(content_type)
pd = decoder.decode(data, rfc6402.PKIData())[0]
if len(pd['reqSequence']) == 0:
raise CMCParsingError("No certificate requests")
if len(pd['reqSequence']) > 1:
raise CMCParsingError("Can't handle multiple certificates")
req = pd['reqSequence'][0]
if req.getName() != 'tcr':
raise CMCParsingError("Can handle only tagged cert requests")
return req['tcr']['certificationRequest']
if __name__ == "__main__":
import sys
with open(sys.argv[1], 'rb') as f:
data = f.read()
cert_req = parse_request(data)
print(cert_req.prettyPrint())

View File

@ -1,48 +0,0 @@
server = {
'port': '5016',
'host': '0.0.0.0' # nosec
}
# Pecan Application Configurations
app = {
'root': 'anchor.controllers.RootController',
'modules': ['anchor'],
# 'static_root': '%(confdir)s/public',
# 'template_path': '%(confdir)s/${package}/templates',
'debug': True,
'errors': {
'404': '/error/404',
'__force_dict__': True
},
}
logging = {
"formatters": {
"simple": {
"format": ("%(asctime)s %(levelname)-5.5s [%(name)s][%(process)d/"
"%(threadName)s] %(message)s")
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "simple",
"level": "DEBUG"
},
},
"loggers": {
"anchor": {
"level": "DEBUG"
},
"wsgi": {
"level": "INFO"
},
"oslo_messaging": {
"level": "DEBUG"
},
},
"root": {
"handlers": ["console"],
"level": "INFO"
},
}

View File

@ -1,106 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
import pecan
from pecan import rest
from webob import exc as http_status
from anchor import audit
from anchor import auth
from anchor import certificate_ops
from anchor import jsonloader
logger = logging.getLogger(__name__)
class RobotsController(rest.RestController):
"""Serves /robots.txt that disallows search bots."""
@pecan.expose(content_type="text/plain")
def get(self):
return "User-agent: *\nDisallow: /\n"
class GenericInstanceController(rest.RestController):
"""Handles requests to /xxx/ra_name."""
def __init__(self, ra_name):
self.ra_name = ra_name
class SignInstanceController(GenericInstanceController):
"""Handles POST requests to /sign/instance."""
@pecan.expose(content_type="text/plain")
def post(self):
ra_name = self.ra_name
logger.debug("processing signing request in registration authority %s",
ra_name)
try:
auth_result = auth.validate(ra_name,
pecan.request.POST.get('user'),
pecan.request.POST.get('secret'))
audit.emit_auth_event(ra_name, pecan.request.POST.get('user'),
auth_result)
except http_status.HTTPUnauthorized:
audit.emit_auth_event(ra_name, pecan.request.POST.get('user'),
None)
raise
try:
csr = certificate_ops.parse_csr(pecan.request.POST.get('csr'),
pecan.request.POST.get('encoding'))
certificate_ops.validate_csr(ra_name, auth_result, csr,
pecan.request)
csr = certificate_ops.fixup_csr(ra_name, csr, pecan.request)
cert, fingerprint = certificate_ops.dispatch_sign(ra_name, csr)
audit.emit_signing_event(ra_name, pecan.request.POST.get('user'),
auth_result, fingerprint=fingerprint)
except Exception:
audit.emit_signing_event(ra_name, pecan.request.POST.get('user'),
auth_result)
raise
return cert
class CAInstanceController(GenericInstanceController):
"""Handles POST requests to /ca/ra_name."""
@pecan.expose(content_type="text/plain")
def get(self):
ra_name = self.ra_name
return certificate_ops.get_ca(ra_name)
class RAController(rest.RestController):
def __init__(self, subcontroller):
self._subcontroller = subcontroller
@pecan.expose()
def _lookup(self, ra_name, *remaining):
if ra_name in jsonloader.registration_authority_names():
return self._subcontroller(ra_name), remaining
pecan.abort(404)
class V1Controller(rest.RestController):
sign = RAController(SignInstanceController)
ca = RAController(CAInstanceController)
class RootController(object):
robots = RobotsController()
v1 = V1Controller()

View File

@ -1,2 +0,0 @@
class ConfigValidationException(Exception):
pass

View File

@ -1,44 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import netaddr
from anchor.X509 import extension
def enforce_alternative_names_present(csr=None, **kwargs):
"""Make sure that if CN is set, it's also present in SAN extension."""
sans = csr.get_extensions(extension.X509ExtensionSubjectAltName)
if sans:
san = sans[0]
else:
san = extension.X509ExtensionSubjectAltName()
san_updated = False
for cn in csr.get_subject_cn():
try:
ip = netaddr.IPAddress(cn)
if ip not in san.get_ips():
san.add_ip(ip)
san_updated = True
except netaddr.AddrFormatError:
if cn not in san.get_dns_ids():
san.add_dns_id(cn)
san_updated = True
if san_updated:
csr.add_extension(san)
return csr

View File

@ -1,135 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import json
import logging
import stevedore
logger = logging.getLogger(__name__)
class AnchorConf():
def __init__(self, logger):
'''Attempt to initialize a config dictionary from a JSON file.
Error out if loading the yaml file fails for any reason.
:param logger: Logger to be used in the case of errors
:param config_file: The Anchor JSON config file
:return: -
'''
self._logger = logger
self._config = {}
def _load_json_file(self, config_file):
try:
with open(config_file, 'r') as f:
return json.load(f)
except IOError:
logger.error("could not open config file: %s" % config_file)
raise
except ValueError:
logger.error("error parsing config file: %s" % config_file)
raise
def load_file_data(self, config_file):
'''Load a config from a file.'''
self._config = self._load_json_file(config_file)
def load_str_data(self, data):
'''Load a config from string data.'''
self._config = json.loads(data)
def load_extensions(self):
self._signing_backends = stevedore.ExtensionManager(
"anchor.signing_backends")
self._validators = stevedore.ExtensionManager("anchor.validators")
self._authentication = stevedore.ExtensionManager(
"anchor.authentication")
self._fixups = stevedore.ExtensionManager("anchor.fixups")
def get_signing_backend(self, name):
return self._signing_backends[name].plugin
def get_validator(self, name):
return self._validators[name].plugin
def get_authentication(self, name):
return self._authentication[name].plugin
def get_fixup(self, name):
return self._fixups[name].plugin
@property
def config(self):
'''Property to return the config dictionary
:return: Config dictionary
'''
return self._config
def __getattr__(self, name):
try:
return self._config[name]
except KeyError:
raise AttributeError("'AnchorConf' object has no attribute '%s'" %
name)
conf = AnchorConf(logger)
def config_for_audit():
"""Get configuration for a given name."""
try:
return conf.audit
except AttributeError:
# it's ok not to configure audit
return None
def config_for_registration_authority(ra_name):
"""Get configuration for a given name."""
return conf.registration_authority[ra_name]
def authentication_for_registration_authority(ra_name):
"""Get authentication config for a given name.
This is only supposed to be called after config validation. All the right
elements are expected to be in place.
"""
auth_name = conf.registration_authority[ra_name]['authentication']
return conf.authentication[auth_name]
def signing_ca_for_registration_authority(ra_name):
"""Get signing ca config for a given name.
This is only supposed to be called after config validation. All the right
elements are expected to be in place.
"""
ca_name = conf.registration_authority[ra_name]['signing_ca']
return conf.signing_ca[ca_name]
def registration_authority_names():
"""List the names of supported registration authorities."""
return conf.registration_authority.keys()

View File

@ -1,91 +0,0 @@
import logging
import time
import uuid
from anchor.X509 import certificate
from anchor.X509 import extension
logger = logging.getLogger(__name__)
def config_validator(val):
def patcher(f):
setattr(f, "_config_validator", val)
return f
return patcher
class SigningError(Exception):
pass
def sign_generic(csr, ca_conf, encryption, signer):
"""Generate an X.509 certificate and sign it.
:param csr: X509 certificate signing request
:param ca_conf: signing CA configuration
:return: signed certificate in PEM format
"""
try:
ca = certificate.X509Certificate.from_file(
ca_conf['cert_path'])
except Exception as e:
raise SigningError("Cannot load the signing CA: %s" % (e,))
new_cert = certificate.X509Certificate()
new_cert.set_version(2)
start_time = int(time.time())
end_time = start_time + (ca_conf['valid_hours'] * 60 * 60)
new_cert.set_not_before(start_time)
new_cert.set_not_after(end_time)
new_cert.set_pubkey(pkey=csr.get_pubkey())
new_cert.set_subject(csr.get_subject())
new_cert.set_issuer(ca.get_subject())
serial = int(uuid.uuid4().hex, 16)
new_cert.set_serial_number(serial)
exts = csr.get_extensions()
ext_i = 0
for ext in exts:
# this check is separate from standards validator - the signing backend
# may know about more/fewer extensions than we do
if ext.get_oid() not in extension.EXTENSION_CLASSES.keys():
if ext.get_critical():
logger.warning("CSR submitted with unknown extension oid %s, "
"refusing to sign", ext.get_oid())
raise SigningError("Unknown critical extension %s" % (
ext.get_oid(),))
else:
logger.info("CSR submitted with non-critical unknown oid %s, "
"not including extension", (ext.get_oid(),))
else:
logger.info("Adding certificate extension: %i %s", ext_i, str(ext))
# authority id will be replaced with current signer
# this cannot be a fixup, because they don't get access to the CA
if isinstance(ext, extension.X509ExtensionAuthorityKeyId):
continue
new_cert.add_extension(ext, ext_i)
ext_i += 1
ca_exts = ca.get_extensions(extension.X509ExtensionSubjectKeyId)
auth_key_id = extension.X509ExtensionAuthorityKeyId()
if ca_exts:
auth_key_id.set_key_id(ca_exts[0].get_key_id())
else:
auth_key_id.set_key_id(ca.get_key_id())
new_cert.add_extension(auth_key_id, ext_i)
logger.info("Signing certificate for <%s> with serial <%s>",
csr.get_subject(), serial)
new_cert.sign(encryption, ca_conf['signing_hash'], signer)
cert_pem = new_cert.as_pem()
return cert_pem

View File

@ -1,74 +0,0 @@
from cryptography.hazmat.primitives.asymmetric import dsa
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from anchor import errors
from anchor import signers
from anchor import util
from anchor.X509 import utils as x509_utils
SIGNER_CONSTRUCTION = {
('RSA', 'SHA224'): (lambda key: key.signer(padding.PKCS1v15(),
hashes.SHA224())),
('RSA', 'SHA256'): (lambda key: key.signer(padding.PKCS1v15(),
hashes.SHA256())),
('RSA', 'SHA384'): (lambda key: key.signer(padding.PKCS1v15(),
hashes.SHA384())),
('RSA', 'SHA512'): (lambda key: key.signer(padding.PKCS1v15(),
hashes.SHA512())),
('DSA', 'SHA224'): (lambda key: key.signer(hashes.SHA224())),
('DSA', 'SHA256'): (lambda key: key.signer(hashes.SHA256())),
}
def conf_validator(name, ca_conf):
# mandatory CA settings
ca_config_requirements = ["cert_path", "key_path", "output_path",
"signing_hash", "valid_hours"]
for requirement in ca_config_requirements:
if requirement not in ca_conf.keys():
raise errors.ConfigValidationException(
"CA config missing: %s (for signing CA %s)" % (requirement,
name))
# all are specified, check the CA certificate and key are readable with
# sane permissions
util.check_file_exists(ca_conf['cert_path'])
util.check_file_exists(ca_conf['key_path'])
util.check_file_permissions(ca_conf['key_path'])
def make_signer(key, encryption, md):
signer = SIGNER_CONSTRUCTION.get((encryption, md.upper()))
if signer is None:
raise signers.SigningError(
"Unknown hash/encryption combination (%s/%s)" % (md, encryption))
signer = signer(key)
def cryptography_io_signer(to_sign):
signer.update(to_sign)
return signer.finalize()
return cryptography_io_signer
@signers.config_validator(conf_validator)
def sign(csr, ca_conf):
try:
key = x509_utils.get_private_key_from_file(ca_conf['key_path'])
except Exception as e:
raise signers.SigningError("Cannot load the signing CA key: %s" % (e,))
if isinstance(key, rsa.RSAPrivateKey):
encryption = 'RSA'
elif isinstance(key, dsa.DSAPrivateKey):
encryption = 'DSA'
else:
raise signers.SigningError("Unknown key type: %s" % (key.__class__,))
signer = make_signer(key, encryption, ca_conf['signing_hash'])
return signers.sign_generic(csr, ca_conf, encryption, signer)

View File

@ -1,120 +0,0 @@
from cryptography.hazmat import backends as cio_backends
from cryptography.hazmat.primitives import hashes
from pyasn1.codec.der import encoder
from pyasn1.type import univ as asn1_univ
from pyasn1_modules import rfc2315
from anchor import errors
from anchor import signers
from anchor import util
def import_pkcs():
# separate function for mocking the import failure
return __import__("PyKCS11")
def conf_validator(name, ca_conf):
# mandatory CA settings
ca_config_requirements = ["cert_path", "output_path", "signing_hash",
"valid_hours", "slot", "pin", "key_id",
"pkcs11_path"]
for requirement in ca_config_requirements:
if requirement not in ca_conf.keys():
raise errors.ConfigValidationException(
"CA config missing: %s (for signing CA %s)" % (requirement,
name))
# all are specified, check the CA certificate and key are readable with
# sane permissions
util.check_file_exists(ca_conf['cert_path'])
util.check_file_exists(ca_conf['pkcs11_path'])
# PyKCS11 is an optional dependency
try:
PyKCS11 = import_pkcs()
except ImportError:
raise errors.ConfigValidationException(
"PyKCS11 library cannot be imported")
# library at the selected path should be possible to load
try:
pkcslib = PyKCS11.PyKCS11Lib()
pkcslib.load(ca_conf['pkcs11_path'])
except PyKCS11.PyKCS11Error:
raise errors.ConfigValidationException(
"Selected pkcs11 library failed to load")
slot = ca_conf['slot']
slots = pkcslib.getSlotList()
if slot not in slots:
raise errors.ConfigValidationException(
"Slot %s cannot be found in the pkcs11 store" % slot)
try:
session = pkcslib.openSession(slot)
session.login(ca_conf['pin'])
except PyKCS11.PyKCS11Error:
raise errors.ConfigValidationException(
"Cannot login to the selected slot")
def make_signer(key_id, slot, pin, pkcs11_path, md):
HASH_OIDS = {
'SHA256': asn1_univ.ObjectIdentifier('2.16.840.1.101.3.4.2.1'),
'SHA384': asn1_univ.ObjectIdentifier('2.16.840.1.101.3.4.2.2'),
'SHA512': asn1_univ.ObjectIdentifier('2.16.840.1.101.3.4.2.3'),
'SHA224': asn1_univ.ObjectIdentifier('2.16.840.1.101.3.4.2.4'),
}
PyKCS11 = import_pkcs()
try:
pkcslib = PyKCS11.PyKCS11Lib()
pkcslib.load(pkcs11_path)
session = pkcslib.openSession(slot)
session.login(pin)
except PyKCS11.PyKCS11Error:
raise signers.SigningError("Could not setup the pkcs11 session")
keys = session.findObjects((
(PyKCS11.CKA_CLASS, PyKCS11.CKO_PRIVATE_KEY),
(PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_RSA),
(PyKCS11.CKA_SIGN, True),
(PyKCS11.CKA_ID, key_id),
))
if not keys:
raise signers.SigningError("Cannot find the requested key")
key = keys[0]
cio_hash = getattr(hashes, md, None)
if not cio_hash:
raise signers.SigningError("Requested hash is not supported")
h = hashes.Hash(cio_hash(), cio_backends.default_backend())
def pkcs11_signer(to_sign):
pkcslib.getInfo # just to keep pkcslib in scope, it's a NOOP
h.update(to_sign)
di = rfc2315.DigestInfo()
di['digestAlgorithm'] = None
di['digestAlgorithm'][0] = HASH_OIDS[md]
di['digest'] = h.finalize()
signature = bytes(session.sign(key, encoder.encode(di),
PyKCS11.MechanismRSAPKCS1))
session.logout()
return signature
return pkcs11_signer
@signers.config_validator(conf_validator)
def sign(csr, ca_conf):
slot = ca_conf['slot']
pin = ca_conf['pin']
pkcs11_path = ca_conf['pkcs11_path']
key_id = [int(ca_conf['key_id'][i:i+2], 16) for
i in range(0, len(ca_conf['key_id']), 2)]
signing_hash = ca_conf['signing_hash'].upper()
signer = make_signer(key_id, slot, pin, pkcs11_path, signing_hash)
return signers.sign_generic(csr, ca_conf, 'RSA', signer)

View File

@ -1,86 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import base64
import os
import stat
from anchor import errors
def verify_domain(domain, label_re_comp, allow_wildcards=False):
labels = domain.split('.')
if labels[-1] == "":
# single trailing . is ok, ignore
labels.pop(-1)
for i, label in enumerate(labels):
if len(label) > 63:
raise ValueError(
"domain <%s> it too long (RFC5280/4.2.1.6)" % (domain,))
# check for wildcard labels, ignore partial-wildcard labels
if '*' == label and allow_wildcards:
if i != 0:
raise ValueError(
"domain <%s> has wildcard that's not in the "
"left-most label (RFC6125/6.4.3)" % (domain,))
else:
if label_re_comp.match(label) is None:
raise ValueError(
"domain <%s> contains invalid characters "
"(RFC1034/3.5)" % (domain,))
def extract_pem(data, use_markers=True):
"""Extract and unpack PEM data
Anything between the BEGIN and END lines will be unpacked using base64. The
specific BEGIN/END content name is ignored since it's not standard anyway.
"""
if not isinstance(data, bytes):
raise TypeError("data must be bytes")
lines = data.splitlines()
seen_start = not use_markers
b64_content = b""
for line in lines:
if line.startswith(b"-----END ") and line.endswith(b"-----"):
break
if seen_start:
b64_content += line
if line.startswith(b"-----BEGIN ") and line.endswith(b"-----"):
seen_start = True
if not b64_content:
return None
decoder = getattr(base64, 'decodebytes', base64.decodestring)
return decoder(b64_content)
def check_file_permissions(path):
# checks that file is owner readable only
expected_permissions = (stat.S_IRUSR | stat.S_IFREG) # 0o100400
st = os.stat(path)
if st.st_mode != expected_permissions:
raise errors.ConfigValidationException("CA file: %s has incorrect "
"permissions set, expected "
"owner readable only" % path)
def check_file_exists(path):
if not (os.path.isfile(path) and
os.access(path, os.R_OK)):
raise errors.ConfigValidationException("could not read file: %s" %
path)

View File

@ -1,84 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import logging
from anchor import jsonloader
from anchor.validators import errors
from anchor.validators import internal
logger = logging.getLogger(__name__)
# some validators will be always active because they enforce Anchor design
# ideas rather than user configuration
ENFORCED_VALIDATORS = [
internal.ca_status
]
def _run_validator(name, validator, body, args):
"""Parse the validator tuple, call the validator, and return result.
:param name: the validator name
:param validator: the validator callable
:param body: validator body, directly from config
:param args: additional arguments to pass to the validator function
:return: True on success, else False
"""
# careful to not modify the master copy of args with local params
new_kwargs = args.copy()
new_kwargs.update(body)
# perform the actual check
logger.debug("_run_validator: checking <%s> with rules: %s", name, body)
try:
validator(**new_kwargs)
logger.debug("_run_validator: success: <%s> ", name)
return True # validator passed b/c no exceptions
except errors.ValidationError as e:
logger.exception("_run_validator: FAILED: <%s> - %s", name, e)
return False
def validate_csr(ra_name, auth_result, csr, request):
"""Validates various aspects of the CSR based on the loaded config.
The arguments of this method are passed to the underlying validate
methods. Therefore, some may be optional, depending on which
validation routines are specified in the configuration.
:param ra_name: name of the registration authority
:param auth_result: AuthDetails value from auth.validate
:param csr: CSR value from certificate_ops.parse_csr
:param request: pecan request object associated with this action
"""
ra_conf = jsonloader.config_for_registration_authority(ra_name)
args = {'auth_result': auth_result,
'csr': csr,
'conf': ra_conf,
'request': request}
# It is ok if the config doesn't have any validators listed
valid = {}
for validator in ENFORCED_VALIDATORS:
vname = validator.__name__
valid[vname] = _run_validator(vname, validator, {}, args)
for vname, options in ra_conf['validators'].items():
validator = jsonloader.conf.get_validator(vname)
valid[vname] = _run_validator(vname, validator, options, args)
return valid

View File

@ -1,313 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import logging
import netaddr
from pyasn1.type import univ as pyasn1_univ
from pyasn1_modules import rfc2437 # PKCS#1
from pyasn1_modules import rfc2459
from anchor.validators import errors as v_errors
from anchor.validators import utils
from anchor.X509 import extension
from anchor.X509 import name as x509_name
logger = logging.getLogger(__name__)
def common_name(csr, allowed_domains=[], allowed_networks=[], **kwargs):
"""Check the CN entry is a known domain.
Refuse requests for certificates if they contain multiple CN
entries, or the domain does not match the list of known suffixes.
"""
alt_present = any(ext.get_name() == "subjectAltName"
for ext in csr.get_extensions())
CNs = csr.get_subject().get_entries_by_oid(x509_name.OID_commonName)
if len(CNs) > 1:
raise v_errors.ValidationError("Too many CNs in the request")
# rfc2459#section-4.2.1.6 says so
if len(CNs) == 0 and not alt_present:
raise v_errors.ValidationError("Alt subjects have to exist if the main"
" subject doesn't")
if len(CNs) > 0:
cn = utils.csr_require_cn(csr)
try:
# is it an IP rather than domain?
ip = netaddr.IPAddress(cn)
if not (utils.check_networks(ip, allowed_networks)):
raise v_errors.ValidationError(
"Address '%s' not allowed (does not match known networks)"
% cn)
except netaddr.AddrFormatError:
if not (utils.check_domains(cn, allowed_domains)):
raise v_errors.ValidationError(
"Domain '%s' not allowed (does not match known domains)"
% cn)
def alternative_names(csr, allowed_domains=[], **kwargs):
"""Check known domain alternative names.
Refuse requests for certificates if the domain does not match
the list of known suffixes, or network ranges.
"""
for _, name in utils.iter_alternative_names(csr, ['DNS']):
if not utils.check_domains(name, allowed_domains):
raise v_errors.ValidationError("Domain '%s' not allowed (doesn't"
" match known domains)" % name)
def alternative_names_ip(csr, allowed_domains=[], allowed_networks=[],
**kwargs):
"""Check known domain and ip alternative names.
Refuse requests for certificates if the domain does not match
the list of known suffixes, or network ranges.
"""
for name_type, name in utils.iter_alternative_names(csr,
['DNS', 'IP Address']):
if name_type == 'DNS' and not utils.check_domains(name,
allowed_domains):
raise v_errors.ValidationError("Domain '%s' not allowed (doesn't"
" match known domains)" % name)
if name_type == 'IP Address':
if not utils.check_networks(name, allowed_networks):
raise v_errors.ValidationError("IP '%s' not allowed (doesn't"
" match known networks)" % name)
def blacklist_names(csr, domains=[], **kwargs):
"""Check for blacklisted names in CN and altNames."""
if not domains:
logger.warning("No domains were configured for the blacklist filter, "
"consider disabling the step or providing a list")
return
CNs = csr.get_subject().get_entries_by_oid(x509_name.OID_commonName)
if len(CNs) > 0:
cn = utils.csr_require_cn(csr)
if utils.check_domains(cn, domains):
raise v_errors.ValidationError("Domain '%s' not allowed "
"(CN blacklisted)" % cn)
for _, name in utils.iter_alternative_names(csr, ['DNS'],
fail_other_types=False):
if utils.check_domains(name, domains):
raise v_errors.ValidationError("Domain '%s' not allowed "
"(alt blacklisted)" % name)
def server_group(auth_result=None, csr=None, group_prefixes={}, **kwargs):
"""Check Team prefix.
Make sure that for server names containing a team prefix, the team is
verified against the groups the user is a member of.
"""
cn = utils.csr_require_cn(csr)
parts = cn.split('-')
if len(parts) == 1 or '.' in parts[0]:
return # no prefix
if parts[0] in group_prefixes:
if group_prefixes[parts[0]] not in auth_result.groups:
raise v_errors.ValidationError(
"Server prefix doesn't match user groups")
def extensions(csr=None, allowed_extensions=[], **kwargs):
"""Ensure only accepted extensions are used."""
exts = csr.get_extensions() or []
for ext in exts:
if (ext.get_name() not in allowed_extensions and
str(ext.get_oid()) not in allowed_extensions):
raise v_errors.ValidationError("Extension '%s' not allowed"
% ext.get_name())
def key_usage(csr=None, allowed_usage=None, **kwargs):
"""Ensure only accepted key usages are specified."""
allowed = set(extension.LONG_KEY_USAGE_NAMES.get(x, x) for x in
allowed_usage)
denied = set()
for ext in (csr.get_extensions() or []):
if isinstance(ext, extension.X509ExtensionKeyUsage):
usages = set(ext.get_all_usages())
denied = denied | (usages - allowed)
if denied:
raise v_errors.ValidationError("Found some prohibited key usages: %s"
% ', '.join(denied))
def ext_key_usage(csr=None, allowed_usage=None, **kwargs):
"""Ensure only accepted extended key usages are specified."""
# transform all possible names into oids we actually check
for i, usage in enumerate(allowed_usage):
if usage in extension.EXT_KEY_USAGE_NAMES_INV:
allowed_usage[i] = extension.EXT_KEY_USAGE_NAMES_INV[usage]
elif usage in extension.EXT_KEY_USAGE_SHORT_NAMES_INV:
allowed_usage[i] = extension.EXT_KEY_USAGE_SHORT_NAMES_INV[usage]
else:
try:
oid = pyasn1_univ.ObjectIdentifier(usage)
allowed_usage[i] = oid
except Exception:
raise v_errors.ValidationError("Unknown usage: %s" % (usage,))
allowed = set(allowed_usage)
denied = set()
for ext in csr.get_extensions(extension.X509ExtensionExtendedKeyUsage):
usages = set(ext.get_all_usages())
denied = denied | (usages - allowed)
if denied:
text_denied = [extension.EXT_KEY_USAGE_SHORT_NAMES.get(x)
for x in denied]
raise v_errors.ValidationError("Found some prohibited key usages: %s"
% ', '.join(text_denied))
def source_cidrs(request=None, cidrs=None, **kwargs):
"""Ensure that the request comes from a known source."""
for cidr in cidrs:
try:
r = netaddr.IPNetwork(cidr)
if request.client_addr in r:
return
except netaddr.AddrFormatError:
raise v_errors.ValidationError(
"Cidr '%s' does not describe a valid network" % cidr)
raise v_errors.ValidationError(
"No network matched the request source '%s'" %
request.client_addr)
def public_key(csr=None, allowed_keys=None, **kwargs):
"""Ensure the public key has the known type and size.
Configuration provides a dictionary of key types and minimum sizes.
"""
if allowed_keys is None or not isinstance(allowed_keys, dict):
raise v_errors.ValidationError("Allowed keys configuration missing")
algo = csr.get_public_key_algo()
algo_names = {
rfc2437.rsaEncryption: 'RSA',
rfc2459.id_dsa: 'DSA',
}
algo_name = algo_names.get(algo)
if algo_name is None:
raise v_errors.ValidationError("Unknown public key type")
min_size = allowed_keys.get(algo_name)
if min_size is None:
raise v_errors.ValidationError(
"Key type not allowed (%s)" % (algo_name,))
if min_size == 0:
# key size is not enforced
return
if csr.get_public_key_size() < min_size:
raise v_errors.ValidationError("Key size too small")
def _split_names_by_type(names):
"""Identify ips and network ranges in a list of strings."""
allowed_domains = []
allowed_ips = []
allowed_ranges = []
for name in names:
ip = utils.maybe_ip(name)
if ip:
allowed_ips.append(ip)
continue
net = utils.maybe_range(name)
if net:
allowed_ranges.append(net)
continue
allowed_domains.append(name)
return (allowed_domains, allowed_ips, allowed_ranges)
def whitelist_names(csr=None, names=[], allow_cn_id=False, allow_dns_id=False,
allow_ip_id=False, allow_wildcard=False, **kwargs):
"""Ensure names match the whitelist in the allowed name slots."""
allowed_domains, allowed_ips, allowed_ranges = _split_names_by_type(names)
for dns_id in csr.get_subject_dns_ids():
if not allow_dns_id:
raise v_errors.ValidationError("IP-ID not allowed")
valid = False
for allowed_domain in allowed_domains:
if utils.compare_name_pattern(dns_id, allowed_domain,
allow_wildcard):
valid = True
break
if not valid:
raise v_errors.ValidationError(
"Value `%s` not allowed in DNS-ID" % (dns_id,))
for ip_id in csr.get_subject_ip_ids():
if not allow_ip_id:
raise v_errors.ValidationError("IP-ID not allowed")
if ip_id in allowed_ips:
continue
for net in allowed_ranges:
if ip_id in net:
continue
raise v_errors.ValidationError(
"Value `%s` not allowed in IP-ID" % (ip_id,))
for cn_id in csr.get_subject_cn():
if not allow_cn_id:
raise v_errors.ValidationError("CN-ID not allowed")
ip = utils.maybe_ip(cn_id)
if ip:
# current CN is an ip address
if ip in allowed_ips:
continue
if any((ip in net) for net in allowed_ranges):
continue
raise v_errors.ValidationError(
"Value `%s` not allowed in CN-ID" % (cn_id,))
else:
# current CN is a domain
valid = False
for allowed_domain in allowed_domains:
if utils.compare_name_pattern(cn_id, allowed_domain,
allow_wildcard):
valid = True
break
if valid:
continue
raise v_errors.ValidationError(
"Value `%s` not allowed in CN-ID" % (cn_id,))
if csr.has_unknown_san_entries():
raise v_errors.ValidationError("Request contains unknown SAN entries")

View File

@ -1,16 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
class ValidationError(Exception):
pass

View File

@ -1,42 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Anchor internally used validators. They should not be exposed to the
users.
"""
from anchor.validators import errors as v_errors
from anchor.X509 import extension
def ca_status(csr=None, **kwargs):
"""Ensure the request hasn't got the CA or cert signing flag.
This validation applies both to the BasicConstraints extension and to the
KeyUsage extension.
"""
basic_constraint = csr.get_extensions(
extension.X509ExtensionBasicConstraints)
if basic_constraint:
if basic_constraint[0].get_ca():
raise v_errors.ValidationError(
"Request is for a CA certificate")
key_usage = csr.get_extensions(extension.X509ExtensionKeyUsage)
if key_usage:
if key_usage[0].get_usage('keyCertSign'):
raise v_errors.ValidationError(
"Request contains certificates signing usage flag")
if key_usage[0].get_usage('cRLSign'):
raise v_errors.ValidationError(
"Request contains CRL signing usage flag")

View File

@ -1,111 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Standards based validator.
This module provides validators which should be included in all deployments and
which are based directly on the standards documents. All exceptions must have a
comment referencing the document / section they're based on.
All the rules are pulled into a single validator: ``standards_compliance``.
"""
from __future__ import absolute_import
from anchor import util
from anchor.validators import errors
from anchor.X509 import errors as x509_errors
from anchor.X509 import extension
import re
# RFC1034 allows a simple " " too, but it's not allowed in certificates, so it
# will not match
#
# This pattern is RFC1034 compatible otherwise, but since newer RFCs actually
# allow any binary value as the domain label, some operators may want to relax
# the pattern in the configuration, for example to allow leading digits or
# hyphens.
def standards_compliance(csr=None, label_re="^[a-z](?:[-a-z0-9]*[a-z0-9])?$",
**kwargs):
"""Collection of separate cases of standards validation."""
_no_extension_duplicates(csr)
_critical_flags(csr)
_valid_domains(csr, label_re)
_csr_signature(csr)
# TODO(stan): validate srv/uri, distinct DNs, email format, identity keys
def _no_extension_duplicates(csr):
"""Only one extension with a given oid is allowed.
See RFC5280 section 4.2
"""
seen_oids = set()
for ext in csr.get_extensions():
oid = ext.get_oid()
if oid in seen_oids:
raise errors.ValidationError(
"Duplicate extension with oid %s (RFC5280/4.2)" % oid)
seen_oids.add(oid)
def _critical_flags(csr):
"""Various rules define whether critical flag is required."""
for ext in csr.get_extensions():
if isinstance(ext, extension.X509ExtensionSubjectAltName):
if len(csr.get_subject()) == 0 and not ext.get_critical():
raise errors.ValidationError(
"SAN must be critical if subject is empty "
"(RFC5280/4.1.2.6)")
if isinstance(ext, extension.X509ExtensionBasicConstraints):
if not ext.get_critical():
raise errors.ValidationError(
"Basic constraints has to be marked critical "
"(RFC5280/4.1.2.9)")
def _valid_domains(csr, label_re="^[a-z](?:[-a-z0-9]*[a-z0-9])?$"):
"""Format of the domin names
See RFC5280 section 4.2.1.6 / RFC6125 / RFC1034
"""
sans = csr.get_extensions(extension.X509ExtensionSubjectAltName)
if not sans:
return
label_re_comp = re.compile(label_re, re.IGNORECASE)
ext = sans[0]
for domain in ext.get_dns_ids():
try:
util.verify_domain(domain, label_re_comp, allow_wildcards=True)
except ValueError as e:
raise errors.ValidationError(str(e))
def _csr_signature(csr):
"""Ensure that the CSR has a valid self-signature."""
# first check for deprecated signatures - verification on those will fail
algo = csr.uses_deprecated_algorithm()
if algo:
raise errors.ValidationError("CSR rejected for using a known broken, "
"or deprecated algorithm: %s" % algo)
try:
if not csr.verify():
raise errors.ValidationError("Signature on the CSR is not valid")
except x509_errors.X509Error:
raise errors.ValidationError("Signature on the CSR is not valid")

View File

@ -1,137 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import logging
import netaddr
from anchor.validators import errors
from anchor.X509 import extension
logger = logging.getLogger(__name__)
def csr_require_cn(csr):
cns = csr.get_subject_cn()
if not cns:
raise errors.ValidationError("CSR is lacking a CN in the Subject")
if len(cns) > 1:
raise errors.ValidationError("CSR has too many CN entries")
return cns[0]
def check_domains(domain, allowed_domains):
if allowed_domains:
if not any(domain.endswith(suffix) for suffix in allowed_domains):
# no domain matched
return False
else:
# no valid domains were provided, so we can't make any assertions
logger.warning("No domains were configured for validation. Anchor "
"will issue certificates for any domain, this is not a "
"recommended configuration for production environments")
return True
def iter_alternative_names(csr, types, fail_other_types=True):
for ext in csr.get_extensions():
if isinstance(ext, extension.X509ExtensionSubjectAltName):
# TODO(stan): fail on other types
if 'DNS' in types:
for dns_id in ext.get_dns_ids():
yield ('DNS', dns_id)
if 'IP Address' in types:
for ip in ext.get_ips():
yield ('IP Address', ip)
def check_networks(ip, allowed_networks):
"""Check the IP is within an allowed network."""
if not isinstance(ip, netaddr.IPAddress):
raise TypeError("ip must be a netaddr ip address")
if not allowed_networks:
# no valid networks were provided, so we can't make any assertions
logger.warning("No valid network IP ranges were given, skipping")
return True
if any(ip in netaddr.IPNetwork(net) for net in allowed_networks):
return True
return False
def maybe_ip(name):
try:
return netaddr.IPAddress(name)
except ValueError:
# happens when trying to pass a subnet prefix
return None
except netaddr.AddrFormatError:
return None
def maybe_range(name):
try:
return netaddr.IPNetwork(name)
except netaddr.AddrFormatError:
return None
def compare_name_pattern(name, pattern, allow_wildcard):
"""Compare domain names including wildcards.
Wilcard means local Anchor wildcard which is '%'. This allows the pattern
to match an actual wildcard entry (*) or name which can be expanded.
Partial matches using % are allowed, but % matches only in one label.
In practice that means:
name: pattern: wildard: result:
example.com example.com - match
*.example.com *.example.com - match
*.example.com %.example.com true match
*.example.com %.example.com false fail
abc.example.com %.example.com - match
abc.def.example.com %.example.com - fail
abc.def.example.com %.%.example.com - match
host-123.example.com host-%.example.com - match
"""
name_labels = name.split('.')
patt_labels = pattern.split('.')
if len(name_labels) != len(patt_labels):
return False
for nl, pl in zip(name_labels, patt_labels):
if '%' in pl:
pre, _, post = pl.partition('%')
if not nl.startswith(pre):
return False
nl = nl[len(pre):] # strip the pre part of pattern
if not nl.endswith(post):
return False
if len(post) > 0:
nl = nl[:-len(post)] # strip the post part of pattern
if '*' in nl and not allow_wildcard:
return False
else:
if nl != pl:
return False
return True

View File

@ -1,10 +0,0 @@
Files in "anchor/asn1" have been generated from the asn1 modules in "asn"
directory using asn1ate (https://github.com/kimgr/asn1ate/)
They can be regenerated by:
- running asn1ate on each module
- putting .i and .e results together (implicit / explicit modules)
- removing faked imports
- linking missing classes to other rfcXXXX files
There's currently no fully automatic way to do it.

View File

@ -1,628 +0,0 @@
PKIX1Explicit88 { iso(1) identified-organization(3) dod(6) internet(1)
security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-explicit(18) }
DEFINITIONS EXPLICIT TAGS ::=
BEGIN
-- EXPORTS ALL --
-- IMPORTS NONE --
-- UNIVERSAL Types defined in 1993 and 1998 ASN.1
-- and required by this specification
--UniversalString ::= [UNIVERSAL 28] IMPLICIT OCTET STRING
-- UniversalString is defined in ASN.1:1993
--BMPString ::= [UNIVERSAL 30] IMPLICIT OCTET STRING
-- BMPString is the subtype of UniversalString and models
-- the Basic Multilingual Plane of ISO/IEC/ITU 10646-1
--UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING
-- The content of this type conforms to RFC 2279.
-- PKIX specific OIDs
id-pkix OBJECT IDENTIFIER ::=
{ iso(1) identified-organization(3) dod(6) internet(1)
security(5) mechanisms(5) pkix(7) }
-- PKIX arcs
id-pe OBJECT IDENTIFIER ::= { id-pkix 1 }
-- arc for private certificate extensions
id-qt OBJECT IDENTIFIER ::= { id-pkix 2 }
-- arc for policy qualifier types
id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
-- arc for extended key purpose OIDS
id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
-- arc for access descriptors
-- policyQualifierIds for Internet policy qualifiers
id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 }
-- OID for CPS qualifier
id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 }
-- OID for user notice qualifier
-- access descriptor definitions
id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
id-ad-timeStamping OBJECT IDENTIFIER ::= { id-ad 3 }
id-ad-caRepository OBJECT IDENTIFIER ::= { id-ad 5 }
-- attribute data types
Attribute ::= SEQUENCE {
type AttributeType,
values SET OF AttributeValue }
-- at least one value is required
AttributeType ::= OBJECT IDENTIFIER
AttributeValue ::= ANY
AttributeTypeAndValue ::= SEQUENCE {
type AttributeType,
value AttributeValue }
-- suggested naming attributes: Definition of the following
-- information object set may be augmented to meet local
-- requirements. Note that deleting members of the set may
-- prevent interoperability with conforming implementations.
-- presented in pairs: the AttributeType followed by the
-- type definition for the corresponding AttributeValue
--Arc for standard naming attributes
id-at OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 4 }
-- Naming attributes of type X520name
id-at-name AttributeType ::= { id-at 41 }
id-at-surname AttributeType ::= { id-at 4 }
id-at-givenName AttributeType ::= { id-at 42 }
id-at-initials AttributeType ::= { id-at 43 }
id-at-generationQualifier AttributeType ::= { id-at 44 }
X520name ::= CHOICE {
teletexString TeletexString (SIZE (1..ub-name)),
printableString PrintableString (SIZE (1..ub-name)),
universalString UniversalString (SIZE (1..ub-name)),
utf8String UTF8String (SIZE (1..ub-name)),
bmpString BMPString (SIZE (1..ub-name)) }
-- Naming attributes of type X520CommonName
id-at-commonName AttributeType ::= { id-at 3 }
X520CommonName ::= CHOICE {
teletexString TeletexString (SIZE (1..ub-common-name)),
printableString PrintableString (SIZE (1..ub-common-name)),
universalString UniversalString (SIZE (1..ub-common-name)),
utf8String UTF8String (SIZE (1..ub-common-name)),
bmpString BMPString (SIZE (1..ub-common-name)) }
-- Naming attributes of type X520LocalityName
id-at-localityName AttributeType ::= { id-at 7 }
X520LocalityName ::= CHOICE {
teletexString TeletexString (SIZE (1..ub-locality-name)),
printableString PrintableString (SIZE (1..ub-locality-name)),
universalString UniversalString (SIZE (1..ub-locality-name)),
utf8String UTF8String (SIZE (1..ub-locality-name)),
bmpString BMPString (SIZE (1..ub-locality-name)) }
-- Naming attributes of type X520StateOrProvinceName
id-at-stateOrProvinceName AttributeType ::= { id-at 8 }
X520StateOrProvinceName ::= CHOICE {
teletexString TeletexString (SIZE (1..ub-state-name)),
printableString PrintableString (SIZE (1..ub-state-name)),
universalString UniversalString (SIZE (1..ub-state-name)),
utf8String UTF8String (SIZE (1..ub-state-name)),
bmpString BMPString (SIZE(1..ub-state-name)) }
-- Naming attributes of type X520OrganizationName
id-at-organizationName AttributeType ::= { id-at 10 }
X520OrganizationName ::= CHOICE {
teletexString TeletexString
(SIZE (1..ub-organization-name)),
printableString PrintableString
(SIZE (1..ub-organization-name)),
universalString UniversalString
(SIZE (1..ub-organization-name)),
utf8String UTF8String
(SIZE (1..ub-organization-name)),
bmpString BMPString
(SIZE (1..ub-organization-name)) }
-- Naming attributes of type X520OrganizationalUnitName
id-at-organizationalUnitName AttributeType ::= { id-at 11 }
X520OrganizationalUnitName ::= CHOICE {
teletexString TeletexString
(SIZE (1..ub-organizational-unit-name)),
printableString PrintableString
(SIZE (1..ub-organizational-unit-name)),
universalString UniversalString
(SIZE (1..ub-organizational-unit-name)),
utf8String UTF8String
(SIZE (1..ub-organizational-unit-name)),
bmpString BMPString
(SIZE (1..ub-organizational-unit-name)) }
-- Naming attributes of type X520Title
id-at-title AttributeType ::= { id-at 12 }
X520Title ::= CHOICE {
teletexString TeletexString (SIZE (1..ub-title)),
printableString PrintableString (SIZE (1..ub-title)),
universalString UniversalString (SIZE (1..ub-title)),
utf8String UTF8String (SIZE (1..ub-title)),
bmpString BMPString (SIZE (1..ub-title)) }
-- Naming attributes of type X520dnQualifier
id-at-dnQualifier AttributeType ::= { id-at 46 }
X520dnQualifier ::= PrintableString
-- Naming attributes of type X520countryName (digraph from IS 3166)
id-at-countryName AttributeType ::= { id-at 6 }
X520countryName ::= PrintableString (SIZE (2))
-- Naming attributes of type X520SerialNumber
id-at-serialNumber AttributeType ::= { id-at 5 }
X520SerialNumber ::= PrintableString (SIZE (1..ub-serial-number))
-- Naming attributes of type X520Pseudonym
id-at-pseudonym AttributeType ::= { id-at 65 }
X520Pseudonym ::= CHOICE {
teletexString TeletexString (SIZE (1..ub-pseudonym)),
printableString PrintableString (SIZE (1..ub-pseudonym)),
universalString UniversalString (SIZE (1..ub-pseudonym)),
utf8String UTF8String (SIZE (1..ub-pseudonym)),
bmpString BMPString (SIZE (1..ub-pseudonym)) }
-- Naming attributes of type DomainComponent (from RFC 2247)
id-domainComponent AttributeType ::=
{ 0 9 2342 19200300 100 1 25 }
DomainComponent ::= IA5String
-- Legacy attributes
pkcs-9 OBJECT IDENTIFIER ::=
{ iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 }
id-emailAddress AttributeType ::= { pkcs-9 1 }
EmailAddress ::= IA5String (SIZE (1..ub-emailaddress-length))
-- naming data types --
Name ::= CHOICE { -- only one possibility for now --
rdnSequence RDNSequence }
RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
DistinguishedName ::= RDNSequence
RelativeDistinguishedName ::=
SET SIZE (1 .. MAX) OF AttributeTypeAndValue
-- Directory string type --
DirectoryString ::= CHOICE {
teletexString TeletexString (SIZE (1..MAX)),
printableString PrintableString (SIZE (1..MAX)),
universalString UniversalString (SIZE (1..MAX)),
utf8String UTF8String (SIZE (1..MAX)),
bmpString BMPString (SIZE (1..MAX)) }
-- certificate and CRL specific structures begin here
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signature BIT STRING }
TBSCertificate ::= SEQUENCE {
version [0] Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
-- If present, version MUST be v2 or v3
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
-- If present, version MUST be v2 or v3
extensions [3] Extensions OPTIONAL
-- If present, version MUST be v3 -- }
Version ::= INTEGER { v1(0), v2(1), v3(2) }
CertificateSerialNumber ::= INTEGER
Validity ::= SEQUENCE {
notBefore Time,
notAfter Time }
Time ::= CHOICE {
utcTime UTCTime,
generalTime GeneralizedTime }
UniqueIdentifier ::= BIT STRING
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING }
Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
Extension ::= SEQUENCE {
extnID OBJECT IDENTIFIER,
critical BOOLEAN DEFAULT FALSE,
extnValue OCTET STRING }
-- CRL structures
CertificateList ::= SEQUENCE {
tbsCertList TBSCertList,
signatureAlgorithm AlgorithmIdentifier,
signature BIT STRING }
TBSCertList ::= SEQUENCE {
version Version OPTIONAL,
-- if present, MUST be v2
signature AlgorithmIdentifier,
issuer Name,
thisUpdate Time,
nextUpdate Time OPTIONAL,
revokedCertificates SEQUENCE OF SEQUENCE {
userCertificate CertificateSerialNumber,
revocationDate Time,
crlEntryExtensions Extensions OPTIONAL
-- if present, MUST be v2
} OPTIONAL,
crlExtensions [0] Extensions OPTIONAL }
-- if present, MUST be v2
-- Version, Time, CertificateSerialNumber, and Extensions were
-- defined earlier for use in the certificate structure
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL }
-- contains a value of the type
-- registered for use with the
-- algorithm object identifier value
-- X.400 address syntax starts here
ORAddress ::= SEQUENCE {
built-in-standard-attributes BuiltInStandardAttributes,
built-in-domain-defined-attributes
BuiltInDomainDefinedAttributes OPTIONAL,
-- see also teletex-domain-defined-attributes
extension-attributes ExtensionAttributes OPTIONAL }
-- Built-in Standard Attributes
BuiltInStandardAttributes ::= SEQUENCE {
country-name CountryName OPTIONAL,
administration-domain-name AdministrationDomainName OPTIONAL,
network-address [0] IMPLICIT NetworkAddress OPTIONAL,
-- see also extended-network-address
terminal-identifier [1] IMPLICIT TerminalIdentifier OPTIONAL,
private-domain-name [2] PrivateDomainName OPTIONAL,
organization-name [3] IMPLICIT OrganizationName OPTIONAL,
-- see also teletex-organization-name
numeric-user-identifier [4] IMPLICIT NumericUserIdentifier
OPTIONAL,
personal-name [5] IMPLICIT PersonalName OPTIONAL,
-- see also teletex-personal-name
organizational-unit-names [6] IMPLICIT OrganizationalUnitNames
OPTIONAL }
-- see also teletex-organizational-unit-names
CountryName ::= [APPLICATION 1] CHOICE {
x121-dcc-code NumericString
(SIZE (ub-country-name-numeric-length)),
iso-3166-alpha2-code PrintableString
(SIZE (ub-country-name-alpha-length)) }
AdministrationDomainName ::= [APPLICATION 2] CHOICE {
numeric NumericString (SIZE (0..ub-domain-name-length)),
printable PrintableString (SIZE (0..ub-domain-name-length)) }
NetworkAddress ::= X121Address -- see also extended-network-address
X121Address ::= NumericString (SIZE (1..ub-x121-address-length))
TerminalIdentifier ::= PrintableString (SIZE
(1..ub-terminal-id-length))
PrivateDomainName ::= CHOICE {
numeric NumericString (SIZE (1..ub-domain-name-length)),
printable PrintableString (SIZE (1..ub-domain-name-length)) }
OrganizationName ::= PrintableString
(SIZE (1..ub-organization-name-length))
-- see also teletex-organization-name
NumericUserIdentifier ::= NumericString
(SIZE (1..ub-numeric-user-id-length))
PersonalName ::= SET {
surname [0] IMPLICIT PrintableString
(SIZE (1..ub-surname-length)),
given-name [1] IMPLICIT PrintableString
(SIZE (1..ub-given-name-length)) OPTIONAL,
initials [2] IMPLICIT PrintableString
(SIZE (1..ub-initials-length)) OPTIONAL,
generation-qualifier [3] IMPLICIT PrintableString
(SIZE (1..ub-generation-qualifier-length))
OPTIONAL }
-- see also teletex-personal-name
OrganizationalUnitNames ::= SEQUENCE SIZE (1..ub-organizational-units)
OF OrganizationalUnitName
-- see also teletex-organizational-unit-names
OrganizationalUnitName ::= PrintableString (SIZE
(1..ub-organizational-unit-name-length))
-- Built-in Domain-defined Attributes
BuiltInDomainDefinedAttributes ::= SEQUENCE SIZE
(1..ub-domain-defined-attributes) OF
BuiltInDomainDefinedAttribute
BuiltInDomainDefinedAttribute ::= SEQUENCE {
type PrintableString (SIZE
(1..ub-domain-defined-attribute-type-length)),
value PrintableString (SIZE
(1..ub-domain-defined-attribute-value-length)) }
-- Extension Attributes
ExtensionAttributes ::= SET SIZE (1..ub-extension-attributes) OF
ExtensionAttribute
ExtensionAttribute ::= SEQUENCE {
extension-attribute-type [0] IMPLICIT INTEGER
(0..ub-extension-attributes),
extension-attribute-value [1]
ANY DEFINED BY extension-attribute-type }
-- Extension types and attribute values
common-name INTEGER ::= 1
CommonName ::= PrintableString (SIZE (1..ub-common-name-length))
teletex-common-name INTEGER ::= 2
TeletexCommonName ::= TeletexString (SIZE (1..ub-common-name-length))
teletex-organization-name INTEGER ::= 3
TeletexOrganizationName ::=
TeletexString (SIZE (1..ub-organization-name-length))
teletex-personal-name INTEGER ::= 4
TeletexPersonalName ::= SET {
surname [0] IMPLICIT TeletexString
(SIZE (1..ub-surname-length)),
given-name [1] IMPLICIT TeletexString
(SIZE (1..ub-given-name-length)) OPTIONAL,
initials [2] IMPLICIT TeletexString
(SIZE (1..ub-initials-length)) OPTIONAL,
generation-qualifier [3] IMPLICIT TeletexString
(SIZE (1..ub-generation-qualifier-length))
OPTIONAL }
teletex-organizational-unit-names INTEGER ::= 5
TeletexOrganizationalUnitNames ::= SEQUENCE SIZE
(1..ub-organizational-units) OF TeletexOrganizationalUnitName
TeletexOrganizationalUnitName ::= TeletexString
(SIZE (1..ub-organizational-unit-name-length))
pds-name INTEGER ::= 7
PDSName ::= PrintableString (SIZE (1..ub-pds-name-length))
physical-delivery-country-name INTEGER ::= 8
PhysicalDeliveryCountryName ::= CHOICE {
x121-dcc-code NumericString (SIZE
(ub-country-name-numeric-length)),
iso-3166-alpha2-code PrintableString
(SIZE (ub-country-name-alpha-length)) }
postal-code INTEGER ::= 9
PostalCode ::= CHOICE {
numeric-code NumericString (SIZE (1..ub-postal-code-length)),
printable-code PrintableString (SIZE (1..ub-postal-code-length)) }
physical-delivery-office-name INTEGER ::= 10
PhysicalDeliveryOfficeName ::= PDSParameter
physical-delivery-office-number INTEGER ::= 11
PhysicalDeliveryOfficeNumber ::= PDSParameter
extension-OR-address-components INTEGER ::= 12
ExtensionORAddressComponents ::= PDSParameter
physical-delivery-personal-name INTEGER ::= 13
PhysicalDeliveryPersonalName ::= PDSParameter
physical-delivery-organization-name INTEGER ::= 14
PhysicalDeliveryOrganizationName ::= PDSParameter
extension-physical-delivery-address-components INTEGER ::= 15
ExtensionPhysicalDeliveryAddressComponents ::= PDSParameter
unformatted-postal-address INTEGER ::= 16
UnformattedPostalAddress ::= SET {
printable-address SEQUENCE SIZE (1..ub-pds-physical-address-lines)
OF PrintableString (SIZE (1..ub-pds-parameter-length))
OPTIONAL,
teletex-string TeletexString
(SIZE (1..ub-unformatted-address-length)) OPTIONAL }
street-address INTEGER ::= 17
StreetAddress ::= PDSParameter
post-office-box-address INTEGER ::= 18
PostOfficeBoxAddress ::= PDSParameter
poste-restante-address INTEGER ::= 19
PosteRestanteAddress ::= PDSParameter
unique-postal-name INTEGER ::= 20
UniquePostalName ::= PDSParameter
local-postal-attributes INTEGER ::= 21
LocalPostalAttributes ::= PDSParameter
PDSParameter ::= SET {
printable-string PrintableString
(SIZE(1..ub-pds-parameter-length)) OPTIONAL,
teletex-string TeletexString
(SIZE(1..ub-pds-parameter-length)) OPTIONAL }
extended-network-address INTEGER ::= 22
ExtendedNetworkAddress ::= CHOICE {
e163-4-address SEQUENCE {
number [0] IMPLICIT NumericString
(SIZE (1..ub-e163-4-number-length)),
sub-address [1] IMPLICIT NumericString
(SIZE (1..ub-e163-4-sub-address-length))
OPTIONAL },
psap-address [0] IMPLICIT PresentationAddress }
PresentationAddress ::= SEQUENCE {
pSelector [0] EXPLICIT OCTET STRING OPTIONAL,
sSelector [1] EXPLICIT OCTET STRING OPTIONAL,
tSelector [2] EXPLICIT OCTET STRING OPTIONAL,
nAddresses [3] EXPLICIT SET SIZE (1..MAX) OF OCTET STRING }
terminal-type INTEGER ::= 23
TerminalType ::= INTEGER {
telex (3),
teletex (4),
g3-facsimile (5),
g4-facsimile (6),
ia5-terminal (7),
videotex (8) } --(0..ub-integer-options)
-- Extension Domain-defined Attributes
teletex-domain-defined-attributes INTEGER ::= 6
TeletexDomainDefinedAttributes ::= SEQUENCE SIZE
(1..ub-domain-defined-attributes) OF TeletexDomainDefinedAttribute
TeletexDomainDefinedAttribute ::= SEQUENCE {
type TeletexString
(SIZE (1..ub-domain-defined-attribute-type-length)),
value TeletexString
(SIZE (1..ub-domain-defined-attribute-value-length)) }
-- specifications of Upper Bounds MUST be regarded as mandatory
-- from Annex B of ITU-T X.411 Reference Definition of MTS Parameter
-- Upper Bounds
-- Upper Bounds
ub-name INTEGER ::= 32768
ub-common-name INTEGER ::= 64
ub-locality-name INTEGER ::= 128
ub-state-name INTEGER ::= 128
ub-organization-name INTEGER ::= 64
ub-organizational-unit-name INTEGER ::= 64
ub-title INTEGER ::= 64
ub-serial-number INTEGER ::= 64
ub-match INTEGER ::= 128
ub-emailaddress-length INTEGER ::= 128
ub-common-name-length INTEGER ::= 64
ub-country-name-alpha-length INTEGER ::= 2
ub-country-name-numeric-length INTEGER ::= 3
ub-domain-defined-attributes INTEGER ::= 4
ub-domain-defined-attribute-type-length INTEGER ::= 8
ub-domain-defined-attribute-value-length INTEGER ::= 128
ub-domain-name-length INTEGER ::= 16
ub-extension-attributes INTEGER ::= 256
ub-e163-4-number-length INTEGER ::= 15
ub-e163-4-sub-address-length INTEGER ::= 40
ub-generation-qualifier-length INTEGER ::= 3
ub-given-name-length INTEGER ::= 16
ub-initials-length INTEGER ::= 5
ub-integer-options INTEGER ::= 256
ub-numeric-user-id-length INTEGER ::= 32
ub-organization-name-length INTEGER ::= 64
ub-organizational-unit-name-length INTEGER ::= 32
ub-organizational-units INTEGER ::= 4
ub-pds-name-length INTEGER ::= 16
ub-pds-parameter-length INTEGER ::= 30
ub-pds-physical-address-lines INTEGER ::= 6
ub-postal-code-length INTEGER ::= 16
ub-pseudonym INTEGER ::= 128
ub-surname-length INTEGER ::= 40
ub-terminal-id-length INTEGER ::= 24
ub-unformatted-address-length INTEGER ::= 180
ub-x121-address-length INTEGER ::= 16
-- Note - upper bounds on string types, such as TeletexString, are
-- measured in characters. Excepting PrintableString or IA5String, a
-- significantly greater number of octets will be required to hold
-- such a value. As a minimum, 16 octets, or twice the specified
-- upper bound, whichever is the larger, should be allowed for
-- TeletexString. For UTF8String or UniversalString at least four
-- times the upper bound should be allowed.
END

View File

@ -1,347 +0,0 @@
PKIX1Implicit88 { iso(1) identified-organization(3) dod(6) internet(1)
security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-implicit(19) }
DEFINITIONS IMPLICIT TAGS ::=
BEGIN
-- EXPORTS ALL --
-- fake imports
id-pe OBJECT IDENTIFIER ::= { id-pkix 1 }
id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
ORAddress ::= ANY
Name ::= CHOICE { any ANY }
RelativeDistinguishedName ::= ANY
CertificateSerialNumber ::= INTEGER
Attribute ::= ANY
DirectoryString ::= CHOICE { any ANY }
-- ISO arc for standard certificate and CRL extensions
id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29}
-- authority key identifier OID and syntax
id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 }
AuthorityKeyIdentifier ::= SEQUENCE {
keyIdentifier [0] KeyIdentifier OPTIONAL,
authorityCertIssuer [1] GeneralNames OPTIONAL,
authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
-- authorityCertIssuer and authorityCertSerialNumber MUST both
-- be present or both be absent
KeyIdentifier ::= OCTET STRING
-- subject key identifier OID and syntax
id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 }
SubjectKeyIdentifier ::= KeyIdentifier
-- key usage extension OID and syntax
id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
KeyUsage ::= BIT STRING {
digitalSignature (0),
nonRepudiation (1),
keyEncipherment (2),
dataEncipherment (3),
keyAgreement (4),
keyCertSign (5),
cRLSign (6),
encipherOnly (7),
decipherOnly (8) }
-- private key usage period extension OID and syntax
id-ce-privateKeyUsagePeriod OBJECT IDENTIFIER ::= { id-ce 16 }
PrivateKeyUsagePeriod ::= SEQUENCE {
notBefore [0] GeneralizedTime OPTIONAL,
notAfter [1] GeneralizedTime OPTIONAL }
-- either notBefore or notAfter MUST be present
-- certificate policies extension OID and syntax
id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 }
anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 }
CertificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
PolicyInformation ::= SEQUENCE {
policyIdentifier CertPolicyId,
policyQualifiers SEQUENCE SIZE (1..MAX) OF
PolicyQualifierInfo OPTIONAL }
CertPolicyId ::= OBJECT IDENTIFIER
PolicyQualifierInfo ::= SEQUENCE {
policyQualifierId PolicyQualifierId,
qualifier ANY DEFINED BY policyQualifierId }
-- Implementations that recognize additional policy qualifiers MUST
-- augment the following definition for PolicyQualifierId
PolicyQualifierId ::=
OBJECT IDENTIFIER --( id-qt-cps | id-qt-unotice )
-- CPS pointer qualifier
CPSuri ::= IA5String
-- user notice qualifier
UserNotice ::= SEQUENCE {
noticeRef NoticeReference OPTIONAL,
explicitText DisplayText OPTIONAL}
NoticeReference ::= SEQUENCE {
organization DisplayText,
noticeNumbers SEQUENCE OF INTEGER }
DisplayText ::= CHOICE {
ia5String IA5String (SIZE (1..200)),
visibleString VisibleString (SIZE (1..200)),
bmpString BMPString (SIZE (1..200)),
utf8String UTF8String (SIZE (1..200)) }
-- policy mapping extension OID and syntax
id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
issuerDomainPolicy CertPolicyId,
subjectDomainPolicy CertPolicyId }
-- subject alternative name extension OID and syntax
id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 }
SubjectAltName ::= GeneralNames
GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
GeneralName ::= CHOICE {
otherName [0] AnotherName,
rfc822Name [1] IA5String,
dNSName [2] IA5String,
x400Address [3] ORAddress,
directoryName [4] Name,
ediPartyName [5] EDIPartyName,
uniformResourceIdentifier [6] IA5String,
iPAddress [7] OCTET STRING,
registeredID [8] OBJECT IDENTIFIER }
-- AnotherName replaces OTHER-NAME ::= TYPE-IDENTIFIER, as
-- TYPE-IDENTIFIER is not supported in the '88 ASN.1 syntax
AnotherName ::= SEQUENCE {
type-id OBJECT IDENTIFIER,
value [0] EXPLICIT ANY DEFINED BY type-id }
EDIPartyName ::= SEQUENCE {
nameAssigner [0] DirectoryString OPTIONAL,
partyName [1] DirectoryString }
-- issuer alternative name extension OID and syntax
id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 }
IssuerAltName ::= GeneralNames
id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 }
SubjectDirectoryAttributes ::= SEQUENCE SIZE (1..MAX) OF Attribute
-- basic constraints extension OID and syntax
id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
BasicConstraints ::= SEQUENCE {
cA BOOLEAN DEFAULT FALSE,
pathLenConstraint INTEGER (0..MAX) OPTIONAL }
-- name constraints extension OID and syntax
id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
NameConstraints ::= SEQUENCE {
permittedSubtrees [0] GeneralSubtrees OPTIONAL,
excludedSubtrees [1] GeneralSubtrees OPTIONAL }
GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
GeneralSubtree ::= SEQUENCE {
base GeneralName,
minimum [0] BaseDistance DEFAULT 0,
maximum [1] BaseDistance OPTIONAL }
BaseDistance ::= INTEGER (0..MAX)
-- policy constraints extension OID and syntax
id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 }
PolicyConstraints ::= SEQUENCE {
requireExplicitPolicy [0] SkipCerts OPTIONAL,
inhibitPolicyMapping [1] SkipCerts OPTIONAL }
SkipCerts ::= INTEGER (0..MAX)
-- CRL distribution points extension OID and syntax
id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= {id-ce 31}
CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
DistributionPoint ::= SEQUENCE {
distributionPoint [0] DistributionPointName OPTIONAL,
reasons [1] ReasonFlags OPTIONAL,
cRLIssuer [2] GeneralNames OPTIONAL }
DistributionPointName ::= CHOICE {
fullName [0] GeneralNames,
nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
ReasonFlags ::= BIT STRING {
unused (0),
keyCompromise (1),
cACompromise (2),
affiliationChanged (3),
superseded (4),
cessationOfOperation (5),
certificateHold (6),
privilegeWithdrawn (7),
aACompromise (8) }
-- extended key usage extension OID and syntax
id-ce-extKeyUsage OBJECT IDENTIFIER ::= {id-ce 37}
ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
KeyPurposeId ::= OBJECT IDENTIFIER
-- permit unspecified key uses
anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 }
-- extended key purpose OIDs
id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 }
id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 }
id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 }
id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
-- inhibit any policy OID and syntax
id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::= { id-ce 54 }
InhibitAnyPolicy ::= SkipCerts
-- freshest (delta)CRL extension OID and syntax
id-ce-freshestCRL OBJECT IDENTIFIER ::= { id-ce 46 }
FreshestCRL ::= CRLDistributionPoints
-- authority info access
id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
AuthorityInfoAccessSyntax ::=
SEQUENCE SIZE (1..MAX) OF AccessDescription
AccessDescription ::= SEQUENCE {
accessMethod OBJECT IDENTIFIER,
accessLocation GeneralName }
-- subject info access
id-pe-subjectInfoAccess OBJECT IDENTIFIER ::= { id-pe 11 }
SubjectInfoAccessSyntax ::=
SEQUENCE SIZE (1..MAX) OF AccessDescription
-- CRL number extension OID and syntax
id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 }
CRLNumber ::= INTEGER (0..MAX)
-- issuing distribution point extension OID and syntax
id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 }
IssuingDistributionPoint ::= SEQUENCE {
distributionPoint [0] DistributionPointName OPTIONAL,
onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE,
onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE,
onlySomeReasons [3] ReasonFlags OPTIONAL,
indirectCRL [4] BOOLEAN DEFAULT FALSE,
onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 }
BaseCRLNumber ::= CRLNumber
-- CRL reasons extension OID and syntax
id-ce-cRLReasons OBJECT IDENTIFIER ::= { id-ce 21 }
CRLReason ::= ENUMERATED {
unspecified (0),
keyCompromise (1),
cACompromise (2),
affiliationChanged (3),
superseded (4),
cessationOfOperation (5),
certificateHold (6),
removeFromCRL (8),
privilegeWithdrawn (9),
aACompromise (10) }
-- certificate issuer CRL entry extension OID and syntax
id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 }
CertificateIssuer ::= GeneralNames
-- hold instruction extension OID and syntax
id-ce-holdInstructionCode OBJECT IDENTIFIER ::= { id-ce 23 }
HoldInstructionCode ::= OBJECT IDENTIFIER
-- ANSI x9 holdinstructions
-- ANSI x9 arc holdinstruction arc
holdInstruction OBJECT IDENTIFIER ::=
{joint-iso-itu-t(2) member-body(2) us(840) x9cm(10040) 2}
-- ANSI X9 holdinstructions referenced by this standard
id-holdinstruction-none OBJECT IDENTIFIER ::=
{holdInstruction 1} -- deprecated
id-holdinstruction-callissuer OBJECT IDENTIFIER ::=
{holdInstruction 2}
id-holdinstruction-reject OBJECT IDENTIFIER ::=
{holdInstruction 3}
-- invalidity date CRL entry extension OID and syntax
id-ce-invalidityDate OBJECT IDENTIFIER ::= { id-ce 24 }
InvalidityDate ::= GeneralizedTime
END

View File

@ -1,51 +0,0 @@
AttributeCertificateVersion1
{ iso(1) member-body(2) us(840) rsadsi(113549)
pkcs(1) pkcs-9(9) smime(16) modules(0) v1AttrCert(15) }
DEFINITIONS EXPLICIT TAGS ::=
BEGIN
-- EXPORTS All
-- fake imports
-- Imports from RFC 3280 [PROFILE], Appendix A.1
AlgorithmIdentifier ::= ANY
Attribute ::= ANY
CertificateSerialNumber ::= INTEGER
Extensions ::= ANY
UniqueIdentifier ::= BIT STRING
-- Imports from RFC 3280 [PROFILE], Appendix A.2
GeneralNames ::= ANY
-- Imports from RFC 3281 [ACPROFILE], Appendix B
AttCertValidityPeriod ::= ANY
IssuerSerial ::= ANY
-- Definition extracted from X.509-1997 [X.509-97], but
-- different type names are used to avoid collisions.
AttributeCertificateV1 ::= SEQUENCE {
acInfo AttributeCertificateInfoV1,
signatureAlgorithm AlgorithmIdentifier,
signature BIT STRING }
AttributeCertificateInfoV1 ::= SEQUENCE {
version AttCertVersionV1 DEFAULT v1,
subject CHOICE {
baseCertificateID [0] IssuerSerial,
-- associated with a Public Key Certificate
subjectName [1] GeneralNames },
-- associated with a name
issuer GeneralNames,
signature AlgorithmIdentifier,
serialNumber CertificateSerialNumber,
attCertValidityPeriod AttCertValidityPeriod,
attributes SEQUENCE OF Attribute,
issuerUniqueID UniqueIdentifier OPTIONAL,
extensions Extensions OPTIONAL }
AttCertVersionV1 ::= INTEGER { v1(0) }
END -- of AttributeCertificateVersion1

View File

@ -1,333 +0,0 @@
CryptographicMessageSyntax2004
{ iso(1) member-body(2) us(840) rsadsi(113549)
pkcs(1) pkcs-9(9) smime(16) modules(0) cms-2004(24) }
DEFINITIONS IMPLICIT TAGS ::=
BEGIN
-- EXPORTS All
-- The types and values defined in this module are exported for use
-- in the other ASN.1 modules. Other applications may use them for
-- their own purposes.
-- fake imports
-- Imports from RFC 3280 [PROFILE], Appendix A.1
AlgorithmIdentifier ::= ANY
Certificate ::= ANY
CertificateList ::= ANY
CertificateSerialNumber ::= INTEGER
Name ::= CHOICE { any ANY }
-- Imports from RFC 3281 [ACPROFILE], Appendix B
AttributeCertificate ::= ANY
-- Imports from Appendix B of this document
AttributeCertificateV1 ::= ANY
-- Cryptographic Message Syntax
ContentInfo ::= SEQUENCE {
contentType ContentType,
content [0] EXPLICIT ANY DEFINED BY contentType }
ContentType ::= OBJECT IDENTIFIER
SignedData ::= SEQUENCE {
version CMSVersion,
digestAlgorithms DigestAlgorithmIdentifiers,
encapContentInfo EncapsulatedContentInfo,
certificates [0] IMPLICIT CertificateSet OPTIONAL,
crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
signerInfos SignerInfos }
DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
SignerInfos ::= SET OF SignerInfo
EncapsulatedContentInfo ::= SEQUENCE {
eContentType ContentType,
eContent [0] EXPLICIT OCTET STRING OPTIONAL }
SignerInfo ::= SEQUENCE {
version CMSVersion,
sid SignerIdentifier,
digestAlgorithm DigestAlgorithmIdentifier,
signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
signatureAlgorithm SignatureAlgorithmIdentifier,
signature SignatureValue,
unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
SignerIdentifier ::= CHOICE {
issuerAndSerialNumber IssuerAndSerialNumber,
subjectKeyIdentifier [0] SubjectKeyIdentifier }
SignedAttributes ::= SET SIZE (1..MAX) OF Attribute
UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute
Attribute ::= SEQUENCE {
attrType OBJECT IDENTIFIER,
attrValues SET OF AttributeValue }
AttributeValue ::= ANY
SignatureValue ::= OCTET STRING
EnvelopedData ::= SEQUENCE {
version CMSVersion,
originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
recipientInfos RecipientInfos,
encryptedContentInfo EncryptedContentInfo,
unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }
OriginatorInfo ::= SEQUENCE {
certs [0] IMPLICIT CertificateSet OPTIONAL,
crls [1] IMPLICIT RevocationInfoChoices OPTIONAL }
RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo
EncryptedContentInfo ::= SEQUENCE {
contentType ContentType,
contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL }
EncryptedContent ::= OCTET STRING
UnprotectedAttributes ::= SET SIZE (1..MAX) OF Attribute
RecipientInfo ::= CHOICE {
ktri KeyTransRecipientInfo,
kari [1] KeyAgreeRecipientInfo,
kekri [2] KEKRecipientInfo,
pwri [3] PasswordRecipientInfo,
ori [4] OtherRecipientInfo }
EncryptedKey ::= OCTET STRING
KeyTransRecipientInfo ::= SEQUENCE {
version CMSVersion, -- always set to 0 or 2
rid RecipientIdentifier,
keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
encryptedKey EncryptedKey }
RecipientIdentifier ::= CHOICE {
issuerAndSerialNumber IssuerAndSerialNumber,
subjectKeyIdentifier [0] SubjectKeyIdentifier }
KeyAgreeRecipientInfo ::= SEQUENCE {
version CMSVersion, -- always set to 3
originator [0] EXPLICIT OriginatorIdentifierOrKey,
ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL,
keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
recipientEncryptedKeys RecipientEncryptedKeys }
OriginatorIdentifierOrKey ::= CHOICE {
issuerAndSerialNumber IssuerAndSerialNumber,
subjectKeyIdentifier [0] SubjectKeyIdentifier,
originatorKey [1] OriginatorPublicKey }
OriginatorPublicKey ::= SEQUENCE {
algorithm AlgorithmIdentifier,
publicKey BIT STRING }
RecipientEncryptedKeys ::= SEQUENCE OF RecipientEncryptedKey
RecipientEncryptedKey ::= SEQUENCE {
rid KeyAgreeRecipientIdentifier,
encryptedKey EncryptedKey }
KeyAgreeRecipientIdentifier ::= CHOICE {
issuerAndSerialNumber IssuerAndSerialNumber,
rKeyId [0] IMPLICIT RecipientKeyIdentifier }
RecipientKeyIdentifier ::= SEQUENCE {
subjectKeyIdentifier SubjectKeyIdentifier,
date GeneralizedTime OPTIONAL,
other OtherKeyAttribute OPTIONAL }
SubjectKeyIdentifier ::= OCTET STRING
KEKRecipientInfo ::= SEQUENCE {
version CMSVersion, -- always set to 4
kekid KEKIdentifier,
keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
encryptedKey EncryptedKey }
KEKIdentifier ::= SEQUENCE {
keyIdentifier OCTET STRING,
date GeneralizedTime OPTIONAL,
other OtherKeyAttribute OPTIONAL }
PasswordRecipientInfo ::= SEQUENCE {
version CMSVersion, -- always set to 0
keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier
OPTIONAL,
keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
encryptedKey EncryptedKey }
OtherRecipientInfo ::= SEQUENCE {
oriType OBJECT IDENTIFIER,
oriValue ANY DEFINED BY oriType }
DigestedData ::= SEQUENCE {
version CMSVersion,
digestAlgorithm DigestAlgorithmIdentifier,
encapContentInfo EncapsulatedContentInfo,
digest Digest }
Digest ::= OCTET STRING
EncryptedData ::= SEQUENCE {
version CMSVersion,
encryptedContentInfo EncryptedContentInfo,
unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }
AuthenticatedData ::= SEQUENCE {
version CMSVersion,
originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
recipientInfos RecipientInfos,
macAlgorithm MessageAuthenticationCodeAlgorithm,
digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL,
encapContentInfo EncapsulatedContentInfo,
authAttrs [2] IMPLICIT AuthAttributes OPTIONAL,
mac MessageAuthenticationCode,
unauthAttrs [3] IMPLICIT UnauthAttributes OPTIONAL }
AuthAttributes ::= SET SIZE (1..MAX) OF Attribute
UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute
MessageAuthenticationCode ::= OCTET STRING
DigestAlgorithmIdentifier ::= AlgorithmIdentifier
SignatureAlgorithmIdentifier ::= AlgorithmIdentifier
KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
MessageAuthenticationCodeAlgorithm ::= AlgorithmIdentifier
KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier
RevocationInfoChoices ::= SET OF RevocationInfoChoice
RevocationInfoChoice ::= CHOICE {
crl CertificateList,
other [1] IMPLICIT OtherRevocationInfoFormat }
OtherRevocationInfoFormat ::= SEQUENCE {
otherRevInfoFormat OBJECT IDENTIFIER,
otherRevInfo ANY DEFINED BY otherRevInfoFormat }
CertificateChoices ::= CHOICE {
certificate Certificate,
extendedCertificate [0] IMPLICIT ExtendedCertificate, -- Obsolete
v1AttrCert [1] IMPLICIT AttributeCertificateV1, -- Obsolete
v2AttrCert [2] IMPLICIT AttributeCertificateV2,
other [3] IMPLICIT OtherCertificateFormat }
AttributeCertificateV2 ::= AttributeCertificate
OtherCertificateFormat ::= SEQUENCE {
otherCertFormat OBJECT IDENTIFIER,
otherCert ANY DEFINED BY otherCertFormat }
CertificateSet ::= SET OF CertificateChoices
IssuerAndSerialNumber ::= SEQUENCE {
issuer Name,
serialNumber CertificateSerialNumber }
CMSVersion ::= INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) }
UserKeyingMaterial ::= OCTET STRING
OtherKeyAttribute ::= SEQUENCE {
keyAttrId OBJECT IDENTIFIER,
keyAttr ANY DEFINED BY keyAttrId OPTIONAL }
-- Content Type Object Identifiers
id-ct-contentInfo OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs9(9) smime(16) ct(1) 6 }
id-data OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs7(7) 1 }
id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }
id-envelopedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs7(7) 3 }
id-digestedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs7(7) 5 }
id-encryptedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs7(7) 6 }
id-ct-authData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1) 2 }
-- The CMS Attributes
MessageDigest ::= OCTET STRING
SigningTime ::= Time
Time ::= CHOICE {
utcTime UTCTime,
generalTime GeneralizedTime }
Countersignature ::= SignerInfo
-- Attribute Object Identifiers
id-contentType OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs9(9) 3 }
id-messageDigest OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs9(9) 4 }
id-signingTime OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs9(9) 5 }
id-countersignature OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs9(9) 6 }
-- Obsolete Extended Certificate syntax from PKCS#6
ExtendedCertificateOrCertificate ::= CHOICE {
certificate Certificate,
extendedCertificate [0] IMPLICIT ExtendedCertificate }
ExtendedCertificate ::= SEQUENCE {
extendedCertificateInfo ExtendedCertificateInfo,
signatureAlgorithm SignatureAlgorithmIdentifier,
signature Signature }
ExtendedCertificateInfo ::= SEQUENCE {
version CMSVersion,
certificate Certificate,
attributes UnauthAttributes }
Signature ::= BIT STRING
END -- of CryptographicMessageSyntax2004

View File

@ -1,284 +0,0 @@
PKIXCRMF-2005 {iso(1) identified-organization(3) dod(6) internet(1)
security(5) mechanisms(5) pkix(7) id-mod(0) id-mod-crmf2005(36)}
DEFINITIONS IMPLICIT TAGS ::=
BEGIN
-- fake imports
-- Directory Authentication Framework (X.509)
Version ::= INTEGER
AlgorithmIdentifier ::= ANY
Name ::= CHOICE { any ANY }
Time ::= CHOICE { any ANY }
SubjectPublicKeyInfo ::= ANY
Extensions ::= ANY
UniqueIdentifier ::= BIT STRING
Attribute ::= ANY
-- Certificate Extensions (X.509)
GeneralName ::= CHOICE { any ANY }
-- Cryptographic Message Syntax
EnvelopedData ::= ANY
-- The following definition may be uncommented for use with
-- ASN.1 compilers that do not understand UTF8String.
-- UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING
-- The contents of this type correspond to RFC 2279.
id-pkix OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
dod(6) internet(1) security(5) mechanisms(5) 7 }
-- arc for Internet X.509 PKI protocols and their components
id-pkip OBJECT IDENTIFIER ::= { id-pkix 5 }
id-smime OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs9(9) 16 }
id-ct OBJECT IDENTIFIER ::= { id-smime 1 } -- content types
-- Core definitions for this module
CertReqMessages ::= SEQUENCE SIZE (1..MAX) OF CertReqMsg
CertReqMsg ::= SEQUENCE {
certReq CertRequest,
popo ProofOfPossession OPTIONAL,
-- content depends upon key type
regInfo SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue OPTIONAL }
CertRequest ::= SEQUENCE {
certReqId INTEGER, -- ID for matching request and reply
certTemplate CertTemplate, -- Selected fields of cert to be issued
controls Controls OPTIONAL } -- Attributes affecting issuance
CertTemplate ::= SEQUENCE {
version [0] Version OPTIONAL,
serialNumber [1] INTEGER OPTIONAL,
signingAlg [2] AlgorithmIdentifier OPTIONAL,
issuer [3] Name OPTIONAL,
validity [4] OptionalValidity OPTIONAL,
subject [5] Name OPTIONAL,
publicKey [6] SubjectPublicKeyInfo OPTIONAL,
issuerUID [7] UniqueIdentifier OPTIONAL,
subjectUID [8] UniqueIdentifier OPTIONAL,
extensions [9] Extensions OPTIONAL }
OptionalValidity ::= SEQUENCE {
notBefore [0] Time OPTIONAL,
notAfter [1] Time OPTIONAL } -- at least one MUST be present
Controls ::= SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue
AttributeTypeAndValue ::= SEQUENCE {
type OBJECT IDENTIFIER,
value ANY DEFINED BY type }
ProofOfPossession ::= CHOICE {
raVerified [0] NULL,
-- used if the RA has already verified that the requester is in
-- possession of the private key
signature [1] POPOSigningKey,
keyEncipherment [2] POPOPrivKey,
keyAgreement [3] POPOPrivKey }
POPOSigningKey ::= SEQUENCE {
poposkInput [0] POPOSigningKeyInput OPTIONAL,
algorithmIdentifier AlgorithmIdentifier,
signature BIT STRING }
-- The signature (using "algorithmIdentifier") is on the
-- DER-encoded value of poposkInput. NOTE: If the CertReqMsg
-- certReq CertTemplate contains the subject and publicKey values,
-- then poposkInput MUST be omitted and the signature MUST be
-- computed over the DER-encoded value of CertReqMsg certReq. If
-- the CertReqMsg certReq CertTemplate does not contain both the
-- public key and subject values (i.e., if it contains only one
-- of these, or neither), then poposkInput MUST be present and
-- MUST be signed.
POPOSigningKeyInput ::= SEQUENCE {
authInfo CHOICE {
sender [0] GeneralName,
-- used only if an authenticated identity has been
-- established for the sender (e.g., a DN from a
-- previously-issued and currently-valid certificate)
publicKeyMAC PKMACValue },
-- used if no authenticated GeneralName currently exists for
-- the sender; publicKeyMAC contains a password-based MAC
-- on the DER-encoded value of publicKey
publicKey SubjectPublicKeyInfo } -- from CertTemplate
PKMACValue ::= SEQUENCE {
algId AlgorithmIdentifier,
-- algorithm value shall be PasswordBasedMac {1 2 840 113533 7 66 13}
-- parameter value is PBMParameter
value BIT STRING }
PBMParameter ::= SEQUENCE {
salt OCTET STRING,
owf AlgorithmIdentifier,
-- AlgId for a One-Way Function (SHA-1 recommended)
iterationCount INTEGER,
-- number of times the OWF is applied
mac AlgorithmIdentifier
-- the MAC AlgId (e.g., DES-MAC, Triple-DES-MAC [PKCS11],
} -- or HMAC [HMAC, RFC2202])
POPOPrivKey ::= CHOICE {
thisMessage [0] BIT STRING, -- Deprecated
-- possession is proven in this message (which contains the private
-- key itself (encrypted for the CA))
subsequentMessage [1] SubsequentMessage,
-- possession will be proven in a subsequent message
dhMAC [2] BIT STRING, -- Deprecated
agreeMAC [3] PKMACValue,
encryptedKey [4] EnvelopedData }
-- for keyAgreement (only), possession is proven in this message
-- (which contains a MAC (over the DER-encoded value of the
-- certReq parameter in CertReqMsg, which MUST include both subject
-- and publicKey) based on a key derived from the end entity's
-- private DH key and the CA's public DH key);
SubsequentMessage ::= INTEGER {
encrCert (0),
-- requests that resulting certificate be encrypted for the
-- end entity (following which, POP will be proven in a
-- confirmation message)
challengeResp (1) }
-- requests that CA engage in challenge-response exchange with
-- end entity in order to prove private key possession
-- Object identifier assignments --
-- Registration Controls in CRMF
id-regCtrl OBJECT IDENTIFIER ::= { id-pkip 1 }
id-regCtrl-regToken OBJECT IDENTIFIER ::= { id-regCtrl 1 }
--with syntax:
RegToken ::= UTF8String
id-regCtrl-authenticator OBJECT IDENTIFIER ::= { id-regCtrl 2 }
--with syntax:
Authenticator ::= UTF8String
id-regCtrl-pkiPublicationInfo OBJECT IDENTIFIER ::= { id-regCtrl 3 }
--with syntax:
PKIPublicationInfo ::= SEQUENCE {
action INTEGER {
dontPublish (0),
pleasePublish (1) },
pubInfos SEQUENCE SIZE (1..MAX) OF SinglePubInfo OPTIONAL }
-- pubInfos MUST NOT be present if action is "dontPublish"
-- (if action is "pleasePublish" and pubInfos is omitted,
-- "dontCare" is assumed)
SinglePubInfo ::= SEQUENCE {
pubMethod INTEGER {
dontCare (0),
x500 (1),
web (2),
ldap (3) },
pubLocation GeneralName OPTIONAL }
id-regCtrl-pkiArchiveOptions OBJECT IDENTIFIER ::= { id-regCtrl 4 }
--with syntax:
PKIArchiveOptions ::= CHOICE {
encryptedPrivKey [0] EncryptedKey,
-- the actual value of the private key
keyGenParameters [1] KeyGenParameters,
-- parameters that allow the private key to be re-generated
archiveRemGenPrivKey [2] BOOLEAN }
-- set to TRUE if sender wishes receiver to archive the private
-- key of a key pair that the receiver generates in response to
-- this request; set to FALSE if no archival is desired.
EncryptedKey ::= CHOICE {
encryptedValue EncryptedValue, -- Deprecated
envelopedData [0] EnvelopedData }
-- The encrypted private key MUST be placed in the envelopedData
-- encryptedContentInfo encryptedContent OCTET STRING.
EncryptedValue ::= SEQUENCE {
intendedAlg [0] AlgorithmIdentifier OPTIONAL,
-- the intended algorithm for which the value will be used
symmAlg [1] AlgorithmIdentifier OPTIONAL,
-- the symmetric algorithm used to encrypt the value
encSymmKey [2] BIT STRING OPTIONAL,
-- the (encrypted) symmetric key used to encrypt the value
keyAlg [3] AlgorithmIdentifier OPTIONAL,
-- algorithm used to encrypt the symmetric key
valueHint [4] OCTET STRING OPTIONAL,
-- a brief description or identifier of the encValue content
-- (may be meaningful only to the sending entity, and used only
-- if EncryptedValue might be re-examined by the sending entity
-- in the future)
encValue BIT STRING }
-- the encrypted value itself
-- When EncryptedValue is used to carry a private key (as opposed to
-- a certificate), implementations MUST support the encValue field
-- containing an encrypted PrivateKeyInfo as defined in [PKCS11],
-- section 12.11. If encValue contains some other format/encoding
-- for the private key, the first octet of valueHint MAY be used
-- to indicate the format/encoding (but note that the possible values
-- of this octet are not specified at this time). In all cases, the
-- intendedAlg field MUST be used to indicate at least the OID of
-- the intended algorithm of the private key, unless this information
-- is known a priori to both sender and receiver by some other means.
KeyGenParameters ::= OCTET STRING
id-regCtrl-oldCertID OBJECT IDENTIFIER ::= { id-regCtrl 5 }
--with syntax:
OldCertId ::= CertId
CertId ::= SEQUENCE {
issuer GeneralName,
serialNumber INTEGER }
id-regCtrl-protocolEncrKey OBJECT IDENTIFIER ::= { id-regCtrl 6 }
--with syntax:
ProtocolEncrKey ::= SubjectPublicKeyInfo
-- Registration Info in CRMF
id-regInfo OBJECT IDENTIFIER ::= { id-pkip 2 }
id-regInfo-utf8Pairs OBJECT IDENTIFIER ::= { id-regInfo 1 }
--with syntax
UTF8Pairs ::= UTF8String
id-regInfo-certReq OBJECT IDENTIFIER ::= { id-regInfo 2 }
--with syntax
CertReq ::= CertRequest
-- id-ct-encKeyWithID is a new content type used for CMS objects.
-- it contains both a private key and an identifier for key escrow
-- agents to check against recovery requestors.
id-ct-encKeyWithID OBJECT IDENTIFIER ::= {id-ct 21}
EncKeyWithID ::= SEQUENCE {
privateKey PrivateKeyInfo,
identifier CHOICE {
string UTF8String,
generalName GeneralName
} OPTIONAL
}
PrivateKeyInfo ::= SEQUENCE {
version INTEGER,
privateKeyAlgorithm AlgorithmIdentifier,
privateKey OCTET STRING,
attributes [0] IMPLICIT Attributes OPTIONAL
}
Attributes ::= SET OF Attribute
END

View File

@ -1,425 +0,0 @@
EnrollmentMessageSyntax-2011-v88
{ iso(1) identified-organization(3) dod(6) internet(1)
security(5) mechanisms(5) pkix(7) id-mod(0)
id-mod-enrollMsgSyntax-2011-88(76) }
DEFINITIONS IMPLICIT TAGS ::=
BEGIN
-- EXPORTS All --
-- The types and values defined in this module are exported for use
-- in the other ASN.1 modules. Other applications may use them for
-- their own purposes.
-- fake imports
-- PKIX Part 1 - Implicit From [RFC5280]
GeneralName ::= CHOICE { any ANY }
CRLReason ::= INTEGER
ReasonFlags ::= BIT STRING
GeneralNames ::= ANY
-- PKIX Part 1 - Explicit From [RFC5280]
AlgorithmIdentifier ::= ANY
Extension ::= ANY
Name ::= CHOICE { any ANY }
CertificateSerialNumber ::= INTEGER
-- Cryptographic Message Syntax FROM [CMS]
ContentInfo ::= ANY
Attribute ::= ANY
IssuerAndSerialNumber ::= ANY
-- CRMF FROM [RFC4211]
CertReqMsg ::= ANY
PKIPublicationInfo ::= ANY
CertTemplate ::= ANY
-- Global Types
-- UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING
-- The content of this type conforms to RFC 3629.
id-pkix OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
dod(6) internet(1) security(5) mechanisms(5) pkix(7) }
id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
id-cmc OBJECT IDENTIFIER ::= {id-pkix 7} -- CMC controls
id-cct OBJECT IDENTIFIER ::= {id-pkix 12} -- CMC content types
-- The following controls have the type OCTET STRING
id-cmc-identityProof OBJECT IDENTIFIER ::= {id-cmc 3}
id-cmc-dataReturn OBJECT IDENTIFIER ::= {id-cmc 4}
id-cmc-regInfo OBJECT IDENTIFIER ::= {id-cmc 18}
id-cmc-responseInfo OBJECT IDENTIFIER ::= {id-cmc 19}
id-cmc-queryPending OBJECT IDENTIFIER ::= {id-cmc 21}
id-cmc-popLinkRandom OBJECT IDENTIFIER ::= {id-cmc 22}
id-cmc-popLinkWitness OBJECT IDENTIFIER ::= {id-cmc 23}
-- The following controls have the type UTF8String
id-cmc-identification OBJECT IDENTIFIER ::= {id-cmc 2}
-- The following controls have the type INTEGER
id-cmc-transactionId OBJECT IDENTIFIER ::= {id-cmc 5}
-- The following controls have the type OCTET STRING
id-cmc-senderNonce OBJECT IDENTIFIER ::= {id-cmc 6}
id-cmc-recipientNonce OBJECT IDENTIFIER ::= {id-cmc 7}
-- This is the content type used for a request message
-- in the protocol
id-cct-PKIData OBJECT IDENTIFIER ::= { id-cct 2 }
PKIData ::= SEQUENCE {
controlSequence SEQUENCE SIZE(0..MAX) OF TaggedAttribute,
reqSequence SEQUENCE SIZE(0..MAX) OF TaggedRequest,
cmsSequence SEQUENCE SIZE(0..MAX) OF TaggedContentInfo,
otherMsgSequence SEQUENCE SIZE(0..MAX) OF OtherMsg
}
bodyIdMax INTEGER ::= 4294967295
BodyPartID ::= INTEGER(0..bodyIdMax)
TaggedAttribute ::= SEQUENCE {
bodyPartID BodyPartID,
attrType OBJECT IDENTIFIER,
attrValues SET OF AttributeValue
}
AttributeValue ::= ANY
TaggedRequest ::= CHOICE {
tcr [0] TaggedCertificationRequest,
crm [1] CertReqMsg,
orm [2] SEQUENCE {
bodyPartID BodyPartID,
requestMessageType OBJECT IDENTIFIER,
requestMessageValue ANY DEFINED BY requestMessageType
}
}
TaggedCertificationRequest ::= SEQUENCE {
bodyPartID BodyPartID,
certificationRequest CertificationRequest
}
CertificationRequest ::= SEQUENCE {
certificationRequestInfo SEQUENCE {
version INTEGER,
subject Name,
subjectPublicKeyInfo SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING },
attributes [0] IMPLICIT SET OF Attribute },
signatureAlgorithm AlgorithmIdentifier,
signature BIT STRING
}
TaggedContentInfo ::= SEQUENCE {
bodyPartID BodyPartID,
contentInfo ContentInfo
}
OtherMsg ::= SEQUENCE {
bodyPartID BodyPartID,
otherMsgType OBJECT IDENTIFIER,
otherMsgValue ANY DEFINED BY otherMsgType }
-- This defines the response message in the protocol
id-cct-PKIResponse OBJECT IDENTIFIER ::= { id-cct 3 }
ResponseBody ::= PKIResponse
PKIResponse ::= SEQUENCE {
controlSequence SEQUENCE SIZE(0..MAX) OF TaggedAttribute,
cmsSequence SEQUENCE SIZE(0..MAX) OF TaggedContentInfo,
otherMsgSequence SEQUENCE SIZE(0..MAX) OF OtherMsg
}
-- Used to return status state in a response
id-cmc-statusInfo OBJECT IDENTIFIER ::= {id-cmc 1}
CMCStatusInfo ::= SEQUENCE {
cMCStatus CMCStatus,
bodyList SEQUENCE SIZE (1..MAX) OF BodyPartID,
statusString UTF8String OPTIONAL,
otherInfo CHOICE {
failInfo CMCFailInfo,
pendInfo PendInfo } OPTIONAL
}
PendInfo ::= SEQUENCE {
pendToken OCTET STRING,
pendTime GeneralizedTime
}
CMCStatus ::= INTEGER {
success (0),
failed (2),
pending (3),
noSupport (4),
confirmRequired (5),
popRequired (6),
partial (7)
}
-- Note:
-- The spelling of unsupportedExt is corrected in this version.
-- In RFC 2797, it was unsuportedExt.
CMCFailInfo ::= INTEGER {
badAlg (0),
badMessageCheck (1),
badRequest (2),
badTime (3),
badCertId (4),
unsupportedExt (5),
mustArchiveKeys (6),
badIdentity (7),
popRequired (8),
popFailed (9),
noKeyReuse (10),
internalCAError (11),
tryLater (12),
authDataFail (13)
}
-- Used for RAs to add extensions to certification requests
id-cmc-addExtensions OBJECT IDENTIFIER ::= {id-cmc 8}
AddExtensions ::= SEQUENCE {
pkiDataReference BodyPartID,
certReferences SEQUENCE OF BodyPartID,
extensions SEQUENCE OF Extension
}
id-cmc-encryptedPOP OBJECT IDENTIFIER ::= {id-cmc 9}
id-cmc-decryptedPOP OBJECT IDENTIFIER ::= {id-cmc 10}
EncryptedPOP ::= SEQUENCE {
request TaggedRequest,
cms ContentInfo,
thePOPAlgID AlgorithmIdentifier,
witnessAlgID AlgorithmIdentifier,
witness OCTET STRING
}
DecryptedPOP ::= SEQUENCE {
bodyPartID BodyPartID,
thePOPAlgID AlgorithmIdentifier,
thePOP OCTET STRING
}
id-cmc-lraPOPWitness OBJECT IDENTIFIER ::= {id-cmc 11}
LraPopWitness ::= SEQUENCE {
pkiDataBodyid BodyPartID,
bodyIds SEQUENCE OF BodyPartID
}
--
id-cmc-getCert OBJECT IDENTIFIER ::= {id-cmc 15}
GetCert ::= SEQUENCE {
issuerName GeneralName,
serialNumber INTEGER }
id-cmc-getCRL OBJECT IDENTIFIER ::= {id-cmc 16}
GetCRL ::= SEQUENCE {
issuerName Name,
cRLName GeneralName OPTIONAL,
time GeneralizedTime OPTIONAL,
reasons ReasonFlags OPTIONAL }
id-cmc-revokeRequest OBJECT IDENTIFIER ::= {id-cmc 17}
RevokeRequest ::= SEQUENCE {
issuerName Name,
serialNumber INTEGER,
reason CRLReason,
invalidityDate GeneralizedTime OPTIONAL,
passphrase OCTET STRING OPTIONAL,
comment UTF8String OPTIONAL }
id-cmc-confirmCertAcceptance OBJECT IDENTIFIER ::= {id-cmc 24}
CMCCertId ::= IssuerAndSerialNumber
-- The following is used to request V3 extensions be added to a
-- certificate
id-ExtensionReq OBJECT IDENTIFIER ::= {iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs-9(9) 14}
ExtensionReq ::= SEQUENCE SIZE (1..MAX) OF Extension
-- The following exists to allow Diffie-Hellman Certification
-- Request Messages to be well-formed
id-alg-noSignature OBJECT IDENTIFIER ::= {id-pkix id-alg(6) 2}
NoSignatureValue ::= OCTET STRING
-- Unauthenticated attribute to carry removable data.
-- This could be used in an update of "CMC Extensions: Server
-- Side Key Generation and Key Escrow" (February 2005) and in
-- other documents.
id-aa OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) id-aa(2)}
id-aa-cmc-unsignedData OBJECT IDENTIFIER ::= {id-aa 34}
CMCUnsignedData ::= SEQUENCE {
bodyPartPath BodyPartPath,
identifier OBJECT IDENTIFIER,
content ANY DEFINED BY identifier
}
-- Replaces CMC Status Info
--
id-cmc-statusInfoV2 OBJECT IDENTIFIER ::= {id-cmc 25}
CMCStatusInfoV2 ::= SEQUENCE {
cMCStatus CMCStatus,
bodyList SEQUENCE SIZE (1..MAX) OF
BodyPartReference,
statusString UTF8String OPTIONAL,
otherInfo CHOICE {
failInfo CMCFailInfo,
pendInfo PendInfo,
extendedFailInfo SEQUENCE {
failInfoOID OBJECT IDENTIFIER,
failInfoValue AttributeValue
}
} OPTIONAL
}
BodyPartReference ::= CHOICE {
bodyPartID BodyPartID,
bodyPartPath BodyPartPath
}
BodyPartPath ::= SEQUENCE SIZE (1..MAX) OF BodyPartID
-- Allow for distribution of trust anchors
--
id-cmc-trustedAnchors OBJECT IDENTIFIER ::= {id-cmc 26}
PublishTrustAnchors ::= SEQUENCE {
seqNumber INTEGER,
hashAlgorithm AlgorithmIdentifier,
anchorHashes SEQUENCE OF OCTET STRING
}
id-cmc-authData OBJECT IDENTIFIER ::= {id-cmc 27}
AuthPublish ::= BodyPartID
-- These two items use BodyPartList
id-cmc-batchRequests OBJECT IDENTIFIER ::= {id-cmc 28}
id-cmc-batchResponses OBJECT IDENTIFIER ::= {id-cmc 29}
BodyPartList ::= SEQUENCE SIZE (1..MAX) OF BodyPartID
--
id-cmc-publishCert OBJECT IDENTIFIER ::= {id-cmc 30}
CMCPublicationInfo ::= SEQUENCE {
hashAlg AlgorithmIdentifier,
certHashes SEQUENCE OF OCTET STRING,
pubInfo PKIPublicationInfo
}
id-cmc-modCertTemplate OBJECT IDENTIFIER ::= {id-cmc 31}
ModCertTemplate ::= SEQUENCE {
pkiDataReference BodyPartPath,
certReferences BodyPartList,
replace BOOLEAN DEFAULT TRUE,
certTemplate CertTemplate
}
-- Inform follow-on servers that one or more controls have already
-- been processed
id-cmc-controlProcessed OBJECT IDENTIFIER ::= {id-cmc 32}
ControlsProcessed ::= SEQUENCE {
bodyList SEQUENCE SIZE(1..MAX) OF BodyPartReference
}
-- Identity Proof control w/ algorithm agility
id-cmc-identityProofV2 OBJECT IDENTIFIER ::= { id-cmc 34 }
IdentifyProofV2 ::= SEQUENCE {
proofAlgID AlgorithmIdentifier,
macAlgId AlgorithmIdentifier,
witness OCTET STRING
}
id-cmc-popLinkWitnessV2 OBJECT IDENTIFIER ::= { id-cmc 33 }
PopLinkWitnessV2 ::= SEQUENCE {
keyGenAlgorithm AlgorithmIdentifier,
macAlgorithm AlgorithmIdentifier,
witness OCTET STRING
}
--
id-cmc-raIdentityWitness OBJECT IDENTIFIER ::= {id-cmc 35}
--
-- Allow for an End-Entity to request a change in name.
-- This item is added to RegControlSet in CRMF.
--
id-cmc-changeSubjectName OBJECT IDENTIFIER ::= {id-cmc 36}
ChangeSubjectName ::= SEQUENCE {
subject Name OPTIONAL,
subjectAlt GeneralNames OPTIONAL
}
-- (WITH COMPONENTS {..., subject PRESENT} |
-- WITH COMPONENTS {..., subjectAlt PRESENT} )
--
-- Embedded response from a third party for processing
--
id-cmc-responseBody OBJECT IDENTIFIER ::= {id-cmc 37}
--
-- Key purpose identifiers are in the Extended Key Usage extension
--
id-kp-cmcCA OBJECT IDENTIFIER ::= { id-kp 27 }
id-kp-cmcRA OBJECT IDENTIFIER ::= { id-kp 28 }
id-kp-cmcArchive OBJECT IDENTIFIER ::= { id-kp 28 }
--
-- Subject Information Access identifier
--
id-ad-cmc OBJECT IDENTIFIER ::= { id-ad 12 }
END

View File

@ -1,43 +0,0 @@
#!/bin/sh
if [ "$1" = "-h" -o "$1" = "--help" ] ; then
echo "Usage: [PY=optional/path/python] $0"
echo
echo "Run Anchor with default uwsgi settings. It will spawn 4 workers"
echo "and use either the default reachable 'python' or one defined in the"
echo "\$PY variable."
echo
exit 0
fi
if ! which uwsgi > /dev/null ; then
echo "You need to install uwsgi to run anchor using this script."
exit 1
fi
PY=${PY:-python}
if ! [ -x "$PY" ] ; then
if ! [ -x "$(which "$PY")" ] ; then
echo "Python interpreter not found (use PY variable to specify)."
exit 1
fi
fi
STR="import pkg_resources; print(pkg_resources.get_distribution('anchor').location)"
PKG_PATH=$("$PY" -c "$STR")
if ! [ -d "$PKG_PATH" ] ; then
echo "Cannot find installed anchor package."
exit 1
fi
if [ -z "$VIRTUAL_ENV" ]; then
OPTS="-p 4"
else
OPTS="--venv ""${VIRTUAL_ENV}"" -p 4"
fi
uwsgi --http-socket :5016 \
--pecan "${PKG_PATH}/anchor/config.py" \
${OPTS}

View File

@ -1,6 +0,0 @@
#!/bin/sh
VENV=$1
[ -n "$VENV" ] || ( echo "provide virtual env path as parameter" && exit 1 )
"$VENV/bin/pecan" serve --reload config.py

View File

@ -1,67 +0,0 @@
import os
import shutil
from subprocess import call
import logging
logging.basicConfig()
logger = logging.getLogger('Anchor_Bootstrap')
logger.setLevel(logging.DEBUG)
# This script looks for two mounted volumes '/key' and '/config'. They can
# contain key material and configuration files respectively. If data is found
# in either of these volumes it will be used to over-write the defaults within
# the Anchor container.
# In the case that '/key' is empty. This script will generate a new private key
# and copy that over the one to be used by Anchor.
# In the case that '/config' is empty no action will be taken
# It's worth noting that the default location for key material can be modified
# in the config.json. That's really up to the deployer.
# The reason we have a separate /key volume is to trigger a new key to be
# created even if we want to use a default configuration.
newkey_newcert = ["openssl", "req", "-out", "CA/root-ca.crt", "-keyout",
"CA/root-ca-unwrapped.key", "-newkey", "rsa:4096", "-subj",
"/CN=Anchor Test CA", "-nodes", "-x509", "-days", "365"]
newcert_existkey = ["openssl", "req", "-new" "-out", "CA/root-ca.crt", "-key",
"/key/root-ca-unwrapped.key", "-subj", "/CN=Anchor Test CA",
"-nodes", "-x509", "-days", "365"]
# Anchor containers no longer build with built in keys. See if a deployer has
# provided a key, if they have, use that. If not then build one now. The key
# built in this way will disappear along with the container.
if os.path.exists('/key/root-ca-unwrapped.key'):
if os.path.exists('/key/root-ca.crt'):
# Provided both a key and a certificate
logger.info("Private key and certificate provided")
shutil.copy2('/key/root-ca-unwrapped.key', 'CA/')
shutil.copy2('/key/root-ca.crt', 'CA/')
os.chmod('CA/root-ca-unwrapped.key', 0400)
else:
# Provided key but no certificate
logger.info("Key provided without certificate. Generating certificate")
call(newcert_existingkey)
shutil.copy2('/key/root-ca-unwrapped.key', 'CA/')
os.chmod('CA/root-ca-unwrapped.key', 0400)
else:
logger.info("No key provided, Anchor will generate a dynamic one")
logger.info("To use a persistent key, create one and provide it in a key volume")
logger.info("Generating new key and certificate")
call(newkey_newcert) #No key or cert provided. Possibly no /key volume at all
os.chmod('CA/root-ca-unwrapped.key', 0400)
# If the user has provdided a config file in a /config volume, use that
#/config
if os.path.exists('/config/config.json'):
shutil.copy2('/config/config.json','./')
if os.path.exists('/config/config.py'):
shutil.copy2('/config/config.py','./')
#Start the pecan service
call(['pecan','serve','config.py'])

View File

View File

@ -1,36 +0,0 @@
{
"authentication": {
"method_1": {
"backend": "static",
"secret": "simplepassword",
"user": "myusername"
}
},
"signing_ca": {
"local": {
"backend": "anchor",
"cert_path": "CA/root-ca.crt",
"key_path": "CA/root-ca-unwrapped.key",
"output_path": "certs",
"signing_hash": "sha256",
"valid_hours": 24
}
},
"registration_authority": {
"default": {
"authentication": "method_1",
"signing_ca": "local",
"validators": {
"standards_compliance": {
"label_re": "^[a-z](?:[-a-z0-9]*[a-z0-9])?$"
},
"source_cidrs": {
"cidrs": ["127.0.0.0/8"]
}
}
}
},
"audit": {
"target": "log"
}
}

View File

@ -1,48 +0,0 @@
API version 1
=============
The following endpoints are available in version 1 of the API.
/robots.txt (GET)
-----------------
Prevents attempts to index the service.
/v1/sign/<registration_authority> (POST)
----------------------------------------
Requests signing of the CSR provided in the POST parameters. The request is
processed by the selected virtual registration authority.
Request parameters
~~~~~~~~~~~~~~~~~~
* ``user``: username used in authentication (optional)
* ``secret``: secret used in authentication
* ``encoding``: request encoding - currently supported: "pem"
* ``csr``: the text of the submitted CSR
Result
~~~~~~
Signed certificate
/v1/ca/<registration_authority> (GET)
----------------------------------------
Requests the CA which would be used to sign the request made to this
registration authority.
Do *NOT* use this to retrieve the certificate needed to trust communication
with Anchor. Connection to Anchor *MUST* be fully trusted before using this
endpoint for further configuration.
Request parameters
~~~~~~~~~~~~~~~~~~
* ``encoding``: request encoding - currently supported: "pem"
Result
~~~~~~
CA certificate used to sign for this RA.

View File

@ -1,27 +0,0 @@
Audit
=====
Anchor produces audit messages using the PyCADF library and aims for CADF
compatibility. The two events being emitted right now are ``audit.sign`` and
``audit.auth``, used for certificate signing and authentication events
respectively.
In the configuration, audit events can be sent either to the log stream, or
to the standard openstack message queue. This is configured using the
``audit.target`` option. See the :doc:`configuration section <configuration>`
for more details.
Capturing events in Ceilometer
------------------------------
In order to get events processed by Ceilometer, two configuration files need to
be provided - event pipeline and definitions. The default
``event_pipeline.yaml`` as described in Ceilometer documentation is compatible
with Anchor. As for ``event_definitions.yaml``, it needs to include the
``audit.auth`` and ``audit.sign`` events.
On the Ceilometer side, it needs the `notification agent`_ installed in order
to receive data from the message queue. Add incoming events will then be saved
and visible after running ``ceilometer event-list``.
.. _notification agent: http://docs.openstack.org/developer/ceilometer/architecture.html#notification-agents-listening-for-data

View File

@ -1,258 +0,0 @@
# -*- coding: utf-8 -*-
#
# Anchor documentation build configuration file, created by
# sphinx-quickstart on Wed Jul 29 15:52:08 2015.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Anchor'
copyright = u'2015'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = 'dev'
# The full version, including alpha/beta/rc tags.
release = 'dev'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
#html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Anchordoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'Anchor.tex', u'Anchor Documentation',
u'see git log', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'anchor', u'Anchor Documentation',
[u'see git log'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Anchor', u'Anchor Documentation',
u'see git log', 'Anchor', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False

View File

@ -1,240 +0,0 @@
Configuration files
===================
Anchor is configured using two files: ``config.py`` and ``config.json``. The
first one defines the Python and webservice related values. You can change the
listening iterface address and port there, as well as logging details to suit
your deployment. The second configuration defines the service behaviour at
runtime.
There are three main sections at the moment: ``authentication`` for
authentication parameters, ``signing_ca`` for defining signing authorities, and
``registration_authority`` for listing virtual registration authorities which
can be selected by client requests.
The main ``config.json`` structure looks like this:
.. code:: json
{
"authentication": { ... },
"signing_ca": { ... },
"registration_authority": { ... }
}
Each block apart from ``registration_authority`` defines a number of mapping
from labels to definitions. Those labels can then be used in the
``registration_authority`` block to refer to settings defined earlier.
Authentication
--------------
The authentication block can define any number of authentication blocks, each
using one specific authentication backend.
Currently available authentication methods are: ``static``, ``keystone``, and
``ldap``.
Static
~~~~~~
Username and password are present in ``config.json``. This mode should be used
only for development and testing.
.. code:: json
{
"authentication": {
"method_1": {
"backend": "static",
"secret": "simplepassword",
"user": "myusername"
}
}
}
Keystone
~~~~~~~~
Username is ignored, but password is a token valid in the configured keystone
location.
.. code:: json
{
"authentication": {
"method_2": {
"backend": "keystone",
"url": "https://keystone.example.com"
}
}
}
LDAP
~~~~
Username and password are used to bind to an LDAP user in a configured domain.
User's groups for the ``server_group`` filter are retrieved from attribute
``memberOf`` in search for ``(sAMAccountName=username@domain)``. The search is done
in the configured base.
.. code:: json
{
"authentication": {
"method_3": {
"backend": "ldap",
"host": "ldap.example.com",
"base": "ou=Users,dc=example,dc=com",
"domain": "example.com",
"port": 636,
"ssl": true
}
}
}
Signing authority
-----------------
The ``signing_ca`` section defines any number of signing authorities which can
be referenced later on. Currently there's only one, default implementation
which uses local files. An example configuration looks like this.
.. code:: json
{
"signing_ca": {
"local": {
"backend": "anchor",
"cert_path": "CA/root-ca.crt",
"key_path": "CA/root-ca-unwrapped.key",
"output_path": "certs",
"signing_hash": "sha256",
"valid_hours": 24
}
}
}
Anchor allows the use of configurable signing backend. While it provides a
default implementation (based on cryptography.io and OpenSSL), other
implementations may be configured. The backend is configured by setting the
``backend`` value to the name of the right entry point. Backend implementations
need to provide only one function: ``sign(csr, config)``, taking the parsed CSR
and their own ``singing_ca`` block of the configuration as parameters and
returning signed certificate in PEM format.
The backends are loaded using the ``stevedore`` module from the registered
entry points. The name space is ``anchor.signing_backends``.
Each backend may take different configuration options. Please refer to
:doc:`signing backends section </signing_backends>`.
Virtual registration authority
------------------------------
The registration authority section puts together previously described elements
and the list of validators applied to each request.
.. code:: json
{
"registration_authority": {
"default": {
"authentication": "method_1",
"signing_ca": "local",
"validators": {
"ca_status": {
"ca_requested": false
},
"source_cidrs": {
"cidrs": [ "127.0.0.0/8" ]
}
},
"fixups": {
}
}
}
}
In the example above, CSRs sent to registration authority ``default`` will be
authenticated using previously defined block ``method_1``, will be validated
against two validators (``ca_status`` and ``source_cidrs``) and if they pass,
the CSR will be signed by the previously defined signing ca called ``local``.
Each validator has its own set of parameters described separately in the
:doc:`validators section </validators>`. Same for fixups described in
:doc:`fixups section </fixups>`
Audit
-----
Audit has two possible targets: ``log`` for output in the standard logging
stream and ``messaging`` for the openstack message queue. The first one doesn't
require any extra options:
.. code:: json
{
"audit": {
"target": "log"
}
}
The message queue version requires defining a target in a way compatible with
``oslo_messaging`` `transport URIs`_. For example:
.. code:: json
{
"audit": {
"target": "messaging",
"url": "rabbit:guest@localhost:5672"
}
}
Example configuration
---------------------
.. code:: json
{
"authentication": {
"method_1": {
"backend": "static",
"secret": "simplepassword",
"user": "myusername"
}
},
"signing_ca": {
"local": {
"cert_path": "CA/root-ca.crt",
"key_path": "CA/root-ca-unwrapped.key",
"output_path": "certs",
"signing_hash": "sha256",
"valid_hours": 24
}
},
"registration_authority": {
"default": {
"authentication": "method_1",
"signing_ca": "local",
"validators": {
"ca_status": {
"ca_requested": false
},
"source_cidrs": {
"cidrs": [ "127.0.0.0/8" ]
}
},
"fixups": {
}
}
}
}
.. _transport URIs: https://wiki.openstack.org/wiki/Oslo/Messaging#Transports

View File

@ -1,129 +0,0 @@
Ephemeral PKI
=============
Anchor is a Certificate and Registration Authority built to provide ephemeral
PKI services for large scale infrastructure deployments such as OpenStack. It
exists to solve two problems that typically affect PKI deployments but that
often go ignored by users; securely provision certificates in live environments
is incredibly difficult and effectively revoking bad certificates is nearly
impossible with the cryptographic libraries that are available to handle PKI
operations today.
Traditional Provisioning
------------------------
One of the challenges for managing PKI in large infrastructures is ensuring
that certificates are provisioned securely and effectively. In traditional PKI
a certificate signing request (CSR) would be created by a user who requires a
certificate for some service that they are managing. The user would then
typically submit that CSR to whatever corporate PKI system is in use, likely
Dogtag_ or Active Directory Certificate Services (ADCS_). That submission would
then trigger a process of verification that often includes a PKI administrator
manually inspecting that the various fields within the CSR and approving the
issuing of a certificate. When the certificate is issued the original requestor
needs to be notified, often by way of email - the requestor then accesses the
CA and retrieves their newly signed certificate.
.. _Dogtag: http://pki.fedoraproject.org/wiki/PKI_Main_Page
.. _ADCS: https://technet.microsoft.com/en-us/windowsserver/dd448615.aspx
This heavily manual process is fraught with opportunities for human error and
tends to scale very poorly. This workflow may have sufficed for managing the
certificates that an organization might want to provision at it's edge but it
cannot cope with the massive number of certificates required for running large
data centers.
Methods for automatically issuing certificates such as SCEP and ADCS
auto-enrollment exist to help solve this problem but often require significant
architectural changes to use them securely. For example, SCEP requires a
secure network to work (in most cases, if such a network already exists then
certificates would not be necessary) so it is typically only used when
infrastructure is provisioned - before being moved into production. ADCS
auto-enrollment requires all of your servers to be running on Microsoft
Windows, which is often not the case for large scale cloud-type environments.
Anchor provides an alternative mechanism for provisioning certificates that
allows each server in a cluster to request its own certificate while
enforcing strong issuing policies that introduce capabilities beyond those that
can be leveraged by the manual process described above - and it can do it at
large scale.
Anchor Provisioning
-------------------
Anchor expects that a machine which requires a certificate will request it
directly, rather than some user requesting it and then installing it on the
machine. This requires the machine to somehow track existing certificates and
request new ones when they expire. There are many ways to approach this and
often a simple cron.d bash script will suffice. The Cathead_ and Certmonger_
projects both exist to help with system based certificate management but only
Cathead natively supports Anchor, however Certmonger can be modified to work
with Anchor if required.
.. _Cathead: https://github.com/stackforge/cathead
.. _Certmonger: https://fedorahosted.org/certmonger/
Anchor provides multiple ways for machines to authenticate. The currently
supported options are LDAP, Keystone and a pre-shared Username/Password
combination. As every machine in a data centre can potentially have it's own
set of credentials Anchor can make very fine grained decisions regarding which
machines should be trusted at any given time. There's more information on
Anchor authentication in the :doc:`configuration` section.
Along with fine grained access control Anchor, supports various
:doc:`validators` that can be used by PKI administrators to set tight policy
constraints on what is allowed within a certificate. These validators provide a
powerful construct for programmatically verifying that a certificate meets
policy requirements for a particular environment.
Traditional Revocation
----------------------
Certificates can require revocation for a number of reasons, they may no longer
be required, they may have been incorrectly issued or the private key for a
certificate may have been compromised.
There are two methods that exist for revoking certificates; Certificate
Revocation Lists (CRL_) and the Online Certificate Status Protocol (OCSP_).
Unfortunately neither system is particularly robust when attempting to use them
within dynamic, large scale environments. CRLs are updated only periodically
and have significant scale issues when used within systems that change
certificates regularly. OCSP was created to address a number of the issues that
hinder CRLs but unfortunately is very poorly supported in cryptographic
libraries outside of web-browser software. Using OCSP incurs some
infrastructure overhead because it needs to maintain a level of availability
that normally requires it to be load balanced to ensure that a responder is
always available, not receiving an OCSP response will cause a client to not
trust a certificate.
.. _CRL: https://www.ietf.org/rfc/rfc5280.txt
.. _OCSP: https://tools.ietf.org/html/rfc6960
To recap; CRLs do not work terribly well in large scale, dynamic environments
where multiple certificates might be required in a machine's lifetime as it is
repurposed. OCSP doesn't work outside of web browsers and is of little value
as a revocation system for large scale infrastructure.
Passive Revocation
------------------
During our testing of TLS client libraries it became obvious that OCSP was
poorly supported and that CRLs weren't reliable enough to provide strong
assurance that certificates would be revoked when required. We did observe that
expired certificates were correctly handled in the most common TLS libraries.
Anchor leverages expiry dates and issues very short lifetime certificates,
typically certificates will be issued with an expiry date set just 12-24 hours
into the future.
Rather than attempting to actively revoke a certificate in the tradition sense,
Anchor will refuse to re-issue a certificate to a bad machine or user. The
assumption being that a change in policy, or modification to the authentication
platform is all that is required to ensure that a bad actor cannot gain access
to certificates. We refer to this process as "Passive Revocation".
When using passive revocation one accepts that there is a certain window of
compromise when a "bad" certificate may still be used within the system.
Although this may seem like a sub-optimal way to handle revocation it actually
results in better performance than more traditional revocation techniques. As
discussed earlier, CRLs can be unreliable and OCSP is generally not supported
outside of web browsers. However, even if it were, the passive revocation
window typically employed by Anchor will be shorter than the OCSP cached
response when using an OCSP responder. This means that in most typical
configurations, using Anchor will result in more reliable and timely
certificate revocation than any other mechanism available today.

View File

@ -1,42 +0,0 @@
Extension support
=================
Extensions in Anchor are supported on 3 levels:
* CSR parser (deciding what OIDs are recognised and the what is the interface
to extensions)
* validators / fixups which operate on extensions
* signing backends which operate on extensions
Anchor needs to parse the extension to use it in a validator or a fixup. That's
not the case of the signing backends however - external backends may add/update
extensions according to their own configuration.
Anchor can parse and analyse the following extensions:
* Basic Constraints
* Key Usage
* Extended Key Usage
* Name Constraints
* Subject Alternative Name
The following extensions are listed as required or preferred, but due to
Anchor's main purpose (ephemeral certificates) they will be either ignored (if
they're not critical), or will prevent signing (if they are):
* Certificate Policies
* Policy Mappings
* Inhibit anyPolicy
* CRL Distribution Points
* Freshest CRL
Other extensions will be added to the implementation when they're needed for
validation / fixups.
Extension limitations
=====================
Due to how Anchor relies on short-term certificates, issuing a CA certificate
from Anchor doesn't really make sense. Certificates which do have a CA flag set
will be rejected unconditionally.
Key usage related to CA status will be treated in a similar way.

View File

@ -1,19 +0,0 @@
Fixups
======
Fixups can be used to modify submitted CSRs before signing. That means for
example adding extra name elements, or extensions. Each fixup is loaded from
the "anchor.fixups" namespace using stevedore and gets access to the parsed CSR
and the configuration.
Unlike validators, each fixup has to return either a new CSR structure or the
modified original.
Included fixups
---------------
``enforce_alternative_names_present``
No parameters.
If the value from CN does not exist in subject alternative names, it will
be copied into either then DNS or IP field, depending on the format.

View File

@ -1,29 +0,0 @@
Welcome to Anchor!
==================================
Anchor is an ephemeral PKI service that, based on certain conditions,
automates the verification of CSRs and signs certificates for clients.
The validity period can be set in the config file with hour resolution.
Contents:
.. toctree::
:maxdepth: 2
configuration
api
extensions
signing_backends
ephemeralPKI
validators
fixups
audit
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`

View File

@ -1,117 +0,0 @@
Signing backends
================
Each signing backend must be registered using an entry point. They're loaded
using the ``stevedore`` module, however this should not affect the calling
behaviour.
The signing CA configuration block allows the following common options:
* ``backend``: name of the requested backend ("anchor" not defined)
* ``output_path``: local path where anchor saves the issued certificates
(optional, output not saved if not defined)
Anchor provides the following backends out of the box:
anchor
------
The default signing backend. It doesn't have any external service dependencies
and all signing happens inside of the Anchor process.
This backend will ignore all non-critical extensions which are not understood
by Anchor and will reject CSRs with unknown critical extensions.
A sample configuration for the ``signing_ca`` block looks like this:
.. code:: json
{
"local": {
"backend": "anchor",
"cert_path": "CA/root-ca.crt",
"key_path": "CA/root-ca-unwrapped.key",
"output_path": "certs",
"signing_hash": "sha256",
"valid_hours": 24
}
}
Valid options for this backend are:
* ``cert_path``: path to the signing CA certificate
* ``key_path``: path to the matching key
* ``signing_hash``: hash to use when signing the issued certificate ("md5",
"sha1", "sha224, "sha256" are valid options)
* ``valid_hours``: validity period for the issued certificates, defined in
hours
pkcs11
------
This backend uses a provided pkcs11 library for the signing operation. The
final certificate is created in the same way as with `anchor` backend with
regards to extensions and fixups.
The interface doesn't rely on any special functionality of the store. Only the
RSA private key needs to be available as a secret. The only used mechanism is
CKM_RSA_PKCS. That means any pkcs11 backend from gnome keyring to tpm and
external HSMs should work.
This backend requires ``PyKCS11`` package to be installed.
A sample configuration for the ``signing_ca`` block looks like this:
.. code:: json
{
"local": {
"backend": "pkcs11",
"cert_path": "CA/root-ca.crt",
"output_path": "certs",
"signing_hash": "sha256",
"valid_hours": 24,
"slot": 18,
"pin": "the_pin",
"key_id": "b22f6e84a7b29db389b57a24384b95cca0bb4bc0",
"pkcs11_path": "/usr/lib/.../pkcs11/...-pkcs11.so"
}
}
Valid options for this backend are:
* ``cert_path``: path to the signing CA certificate
* ``signing_hash``: hash to use when signing the issued certificate ("sha224,
"sha256", "sha384", "sha512" are valid options)
* ``valid_hours``: validity period for the issued certificates, defined in
hours
* ``slot``: slot number where the key can be found
* ``pin``: text version of the pin required to access the right slot
* ``key_id``: key id written as a hex string
* ``pkcs11_path``: path to the dynamic library compatible with pkcs11 interface
Backend development
-------------------
Backends are simple functions which need to take 2 parameters: the CSR in PEM
format and the configuration block contents. Configuration can contain any keys
required by the backend.
The return value must be a signed certificate in PEM format, however in most
cases it's enough to implement the actual hash signing part and rely on
``anchor.signer.sign_generic`` framework. The backend may either throw a
specific ``WebOb`` HTTP exception, or SigningError exception which will result
in a 500 response.
For security, http exceptions from the signing backend should not expose any
specific information about the reason for failure. Internal exceptions are
preferred for this reason and their details will be logged in Anchor.
The backend must not rely on the received CSR signature. If any modifications
are applied to the submitted CSR in Anchor, they will invalidate the signature.
Unless the backend is intended to work only with validators, and not any fixup
operations in the future, the signature field should be ignored and the request
treated as already correct/verified.
Configuration is verified using the function provided using the
``@signers.config_validator(f)`` decorator.

View File

@ -1,184 +0,0 @@
Validators
==========
Currently validators can check three things: the CSR, the incoming connection,
and the authentication. The resulting action can be only pass or fail.
Validators are configured in the ``config.json`` file and each one comes with
different options.
Included validators
-------------------
The following validators are implemented at the moment:
``standards_compliance``
Verifies: CSR.
Ensures that the CSR does not break any rules defined in the standards
documents (mostly RFC5280). Specific checks may be added over time in new
versions of Anchor. This validator should be only skipped if there's a
known compatibility issue. Otherwise it should be used in all environments.
Any requests produced using standard tooling that fail this check should be
reported as Anchor issues.
Parameters:
- ``label_re``: pattern for acceptable domain label format
``whitelist_names``
Verifies: CSR. Parameters:
- ``names``: list of names/ips/ip ranges allowed in various fields
- ``allow_cn_id``: allow name in subject CN
- ``allow_dns_id``: allow name in SAN dns entry
- ``allow_ip_id``: allow name in SAN IP entry
- ``allow_wildcard``: allow wildcard certificate to match '%'
IDs available in various places in the certificate are matched against the
patterns in the ``names`` list. These can be:
- IP addresses: ``1.2.3.4``
- IP ranges: ``1.2.3.0/24``
- complete names: ``some.example.com``
- names with wildcards: ``%.example.com``, ``partial-%.example.com``
Wildcard (``%``) rules: It matches only a single name label, or part of
one. It can be used only in domain names, not IPs. Only one wildcard is
allowed in a label, but multiple in a name, so ``%.%.example.com`` is
valid.
Pattern wildcard (``%``) may match a domain wildcard character (``*``)
only if ``allow_wildcard`` is set to true.
This match will fail if the CSR contains any SAN type not included here.
``blacklist_names``
Verifies: CSR. Parameters: ``allowed_domains``, ``allowed_networks``.
Ensures that the CN and subject alternative names do not contain anything
configured in the ``domains``.
``common_name``
Verifies: CSR. Parameters: ``allowed_domains``, ``allowed_networks``.
Ensures that the CN matches one of names in ``allowed_domains`` or IP
ranges in ``allowed_networks``.
Deprecated: use ``whitelist_names`` / ``blacklist_names`` instead.
``alternative_names``
Verifies: CSR. Parameters: ``allowed_domains``.
Ensures that names specified in the subject alternative names extension
match one of the names in ``allowed_domains``.
Deprecated: use ``whitelist_names`` / ``blacklist_names`` instead.
``alternative_names_ip``
Verifies: CSR. Parameters: ``allowed_domains``, ``allowed_networks``.
Ensures that names specified in the subject alternative names extension
match one of the names in ``allowed_domains`` or IP ranges in
``allowed_networks``.
Deprecated: use ``whitelist_names`` / ``blacklist_names`` instead.
``server_group``
Verifies: Auth, CSR. Parameters: ``group_prefixes``.
Ensures the requester is authorised to get a certificate for a given
server. This is currently assuming specific server naming scheme which
looks like ``{prefix}-{name}.{domain}``. For example if the prefixes are
defined as ``{"Nova": "nv"}``, and the client authentication returns group
"Nova", then a request for ``nv-compute1.domain`` will succeed, but a
request for ``gl-api1.domain`` will fail.
Only CN is checked and if there are no dashes in the CN, validation
succeeds.
Deprecated: use ``whitelist_names`` / ``blacklist_names`` instead.
``extensions``
Verifies: CSR. Parameters: ``allowed_extensions``.
Ensures that only ``allowed_extensions`` are present in the request. The
names recognised by Anchor are:
policyConstraints, basicConstraints, subjectDirectoryAttributes,
deltaCRLIndicator, cRLDistributionPoints, issuingDistributionPoint,
nameConstraints, certificatePolicies, policyMappings,
privateKeyUsagePeriod, keyUsage, authorityKeyIdentifier,
subjectKeyIdentifier, certificateIssuer, subjectAltName, issuerAltName
Alternatively, the extension can be specified by the dotted decimal version
of OID.
``key_usage``
Verifies: CSR. Parameters: ``allowed_usage``.
Ensures only ``allowed_usage`` is requested for the certificate. The names
recognised by Anchor are:
Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment,
Key Agreement, Certificate Sign, CRL Sign, Encipher Only, Decipher Only,
as well as short versions:
digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment,
keyAgreement, keyCertSign, cRLSign, encipherOnly, decipherOnly
``ext_key_usage``
Verifies: CSR. Parameters: ``allowed_usage``.
Ensures only ``allowed_usage`` is requested for the certificate. The names
recognised by Anchor are:
TLS Web Server Authentication, TLS Web Client Authentication, Code Signing,
E-mail Protection, Time Stamping, OCSP Signing, Any Extended Key Usage
as well as short versions:
serverAuth, clientAuth, codeSigning, emailProtection, timeStamping,
ocspSigning, anyExtendedKeyUsage
or text representation of custom OIDs.
``source_cidrs``
Verifies: CSR. Parameters: ``cidrs``.
Ensures the request comes from one of the ranges in `cidrs`.
``public_key``
Verifies: CSR. Parameters: ``allowed_keys``.
Ensures that only selected keys of a minimum specified length can be used
in the CSR. The ``allowed_keys`` parameter is a dictionary where keys are
the uppercase key names and values are minimum key lengths. Valid keys
at the moment are: ``RSA`` and ``DSA``.
Extension interface
-------------------
Custom validators can be used with Anchor without changing the application
itself. All validators are exposed as Stevedore_ extensions. They're registered
as entry points in namespace ``anchor.validators`` and each name points to a
simple function which accepts the following keyword arguments:
``csr`` : anchor.X509.signing_request.X509Csr
An object describing the submitted CSR.
``auth_result`` : anchor.auth.results.AuthDetails
An object which contains authentication information like username and user
groups.
``request`` : pecan.Request
The https request which delivered the CSR.
``conf`` : dict
Dictionary describing the registration authority configuration.
On successful return, the request is passed on to the next validator or signed
if there are no remining ones. On validation failure an
``anchor.validators.ValidationError`` exception must be raised.
.. _Stevedore: http://docs.openstack.org/developer/stevedore/index.html

View File

@ -1,17 +0,0 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
cryptography!=1.3.0,>=1.0 # BSD/Apache-2.0
pyasn1 # BSD
pyasn1-modules # BSD
WebOb>=1.6.0 # MIT
pecan!=1.0.2,!=1.0.3,!=1.0.4,!=1.2,>=1.0.0 # BSD
Paste # MIT
netaddr!=0.7.16,>=0.7.13 # BSD
ldap3>=1.0.2 # LGPLv3
requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0
stevedore>=1.17.1 # Apache-2.0
pycadf!=2.0.0,>=1.1.0 # Apache-2.0
oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
oslo.messaging>=5.14.0 # Apache-2.0
oslo.utils>=3.18.0 # Apache-2.0

View File

@ -1,161 +0,0 @@
#!/bin/bash
# Copyright 2012 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
set -eu
function usage {
echo "Usage: $0 [OPTION]..."
echo "Run Keystone's test suite(s)"
echo ""
echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
echo " -x, --stop Stop running tests after the first error or failure."
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
echo " -u, --update Update the virtual environment with any newer package versions"
echo " -p, --pep8 Just run flake8"
echo " -8, --8 Just run flake8, don't show PEP8 text for each error"
echo " -P, --no-pep8 Don't run flake8"
echo " -c, --coverage Generate coverage report"
echo " -h, --help Print this usage message"
echo ""
echo "Note: with no options specified, the script will try to run the tests in a virtual environment,"
echo " If no virtualenv is found, the script will ask if you would like to create one. If you "
echo " prefer to run tests NOT in a virtual environment, simply pass the -N option."
exit
}
function process_option {
case "$1" in
-h|--help) usage;;
-V|--virtual-env) always_venv=1; never_venv=0;;
-N|--no-virtual-env) always_venv=0; never_venv=1;;
-x|--stop) failfast=1;;
-f|--force) force=1;;
-u|--update) update=1;;
-p|--pep8) just_flake8=1;;
-8|--8) short_flake8=1;;
-P|--no-pep8) no_flake8=1;;
-c|--coverage) coverage=1;;
-*) testropts="$testropts $1";;
*) testrargs="$testrargs $1"
esac
}
venv=.venv
with_venv=tools/with_venv.sh
always_venv=0
never_venv=0
force=0
failfast=0
testrargs=
testropts=--subunit
wrapper=""
just_flake8=0
short_flake8=0
no_flake8=0
coverage=0
update=0
for arg in "$@"; do
process_option $arg
done
TESTRTESTS="python setup.py testr"
# If enabled, tell nose to collect coverage data
if [ $coverage -eq 1 ]; then
TESTRTESTS="$TESTRTESTS --coverage"
fi
function run_tests {
set -e
echo ${wrapper}
if [ $failfast -eq 1 ]; then
testrargs="$testrargs -- --failfast"
fi
${wrapper} $TESTRTESTS --testr-args="$testropts $testrargs" | \
${wrapper} subunit-2to1 | \
${wrapper} tools/colorizer.py
}
function run_flake8 {
FLAGS=--show-pep8
if [ $# -gt 0 ] && [ 'short' == ''$1 ]; then
FLAGS=''
fi
echo "Running flake8 ..."
# Just run flake8 in current environment
echo ${wrapper} flake8 $FLAGS | tee pep8.txt
${wrapper} flake8 $FLAGS | tee pep8.txt
}
if [ $never_venv -eq 0 ]; then
# Remove the virtual environment if --force used
if [ $force -eq 1 ]; then
echo "Cleaning virtualenv..."
rm -rf ${venv}
fi
if [ $update -eq 1 ]; then
echo "Updating virtualenv..."
python tools/install_venv.py
fi
if [ -e ${venv} ]; then
wrapper="${with_venv}"
else
if [ $always_venv -eq 1 ]; then
# Automatically install the virtualenv
python tools/install_venv.py
wrapper="${with_venv}"
else
echo -e "No virtual environment found...create one? (Y/n) \c"
read use_ve
if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then
# Install the virtualenv and run the test suite in it
python tools/install_venv.py
wrapper=${with_venv}
fi
fi
fi
fi
# Delete old coverage data from previous runs
if [ $coverage -eq 1 ]; then
${wrapper} coverage erase
fi
if [ $just_flake8 -eq 1 ]; then
run_flake8
exit
fi
if [ $short_flake8 -eq 1 ]; then
run_flake8 short
exit
fi
run_tests
# NOTE(sirp): we only want to run flake8 when we're running the full-test
# suite, not when we're running tests individually. To handle this, we need to
# distinguish between options (testropts), which begin with a '-', and arguments
# (testrargs).
if [ -z "$testrargs" ]; then
if [ $no_flake8 -eq 0 ]; then
run_flake8
fi
fi

View File

@ -1,67 +0,0 @@
[metadata]
name = anchor
summary = Webservice to auto-sign certificates for short amount of time
description-file =
README.rst
author = OpenStack Security Group
author-email = openstack-dev@lists.openstack.org
home-page = https://wiki.openstack.org/wiki/Security/Projects/Anchor
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
Intended Audience :: System Administrators
Intended Audience :: Developers
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Operating System :: MacOS :: MacOS X
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.5
Topic :: Security
[build_sphinx]
all_files = 1
build-dir = doc/build
source-dir = doc/source
[entry_points]
anchor.signing_backends =
anchor = anchor.signers.cryptography_io:sign
pkcs11 = anchor.signers.pkcs11:sign
anchor.validators =
common_name = anchor.validators.custom:common_name
alternative_names = anchor.validators.custom:alternative_names
alternative_names_ip = anchor.validators.custom:alternative_names_ip
blacklist_names = anchor.validators.custom:blacklist_names
server_group = anchor.validators.custom:server_group
extensions = anchor.validators.custom:extensions
key_usage = anchor.validators.custom:key_usage
ext_key_usage = anchor.validators.custom:ext_key_usage
source_cidrs = anchor.validators.custom:source_cidrs
whitelist_names = anchor.validators.custom:whitelist_names
public_key = anchor.validators.custom:public_key
standards_compliance = anchor.validators.standards:standards_compliance
anchor.authentication =
keystone = anchor.auth.keystone:login
ldap = anchor.auth.ldap:login
static = anchor.auth.static:login
anchor.fixups =
enforce_alternative_names_present = anchor.fixups:enforce_alternative_names_present
[files]
data_files =
etc/anchor =
config.json
packages =
anchor
scripts =
bin/anchor
bin/anchor_debug
[bdist_wheel]
universal=1

View File

@ -1,29 +0,0 @@
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
import setuptools
# In python < 2.7.4, a lazy loading of package `pbr` will break
# setuptools if some other modules registered functions in `atexit`.
# solution from: http://bugs.python.org/issue15881#msg170215
try:
import multiprocessing # noqa
except ImportError:
pass
setuptools.setup(
setup_requires=['pbr>=1.8'],
pbr=True)

View File

@ -1,18 +0,0 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
coverage>=4.0 # Apache-2.0
fixtures>=3.0.0 # Apache-2.0/BSD
hacking<0.10,>=0.9.2
mock>=2.0 # BSD
python-subunit>=0.0.18 # Apache-2.0/BSD
testrepository>=0.0.18 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
testtools>=1.4.0 # MIT
requests-mock>=1.1 # Apache-2.0
# Documentation build requirements
sphinx>=1.5.1 # BSD
oslosphinx>=4.7.0 # Apache-2.0
bandit>=1.1.0 # Apache-2.0

View File

@ -1,15 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDXTICDdXtgyMqmfForj49nr4kOBcs9AdG85iIGCErRYC1tC6Sz
v1E+lblOfadEyf0nykoyptK3aPgXa5S+GGu2zVSQoXmpixbdAr2MIuAjcnHeomKz
EjyjNcbwa5YElhSI3ypiX28ZCFncbVIUN8aUdpfjZCnJKBPpUgT+GGxOFwIDAQAB
AoGBAITpG2UMP7BOBJymo9vEclkmCkv307HDz8D3qQVkVRvQbfqld3Xno7YpJA6K
j5ptv7Sysv918Rt817tNlLONy+AZGY2hxgmXVob4FdwVoRTj7EJhuciIYzecsfHO
PVzc8dF2WCocMWlXQ7a/r2pOa5H/D4x4FyBtpf0ykJps2ZfBAkEA/2SQFYTDBcTX
f1vnL/fnNgUS1T84E6FPlRTO8lpNB1atGf97lLzxv4QafJc3w6PhL1DRMeCmG1QD
7G1pBG4PpwJBANfPiYRg2jyUANbwE47CjsqiviwAC2aboWGWcC/m+LQ/PdvcTD7V
tF83Fn4syEuNKj65xGhmxZmCdn8oHCZBHBECQC0Iam+g7VKDFwyaA/XtXJOl6WA4
uYaclw/Oj38kdRiqK/O9nOjpOCdw/8qgT3Dr4LUbJwgIeMGw2tBBqpbhYVkCQFN3
diVX3DAfwe9fbQEC6H0g0lJsNfyaZqE6sOsl9ryn1QHqwyZuOtO0l6N3KIRn9ZXK
/VavoO8NUU0+sxxshDECQQDtwy89tpRQhqTIQkZzcgXmAiBypdh/Wunj1fzAJ98M
Vo8GJXswSuQXDr4mZnsl0F+RRe4LoLM6i3TYeM+qJZmg
-----END RSA PRIVATE KEY-----

View File

@ -1,58 +0,0 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 16983733478354280881 (0xebb2579d693761b1)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=AU, ST=Some-State, O=Herp Derp plc, OU=herp.derp.plc, CN=herp.derp.plc
Validity
Not Before: Sep 1 23:29:35 2015 GMT
Not After : Sep 2 23:29:35 2015 GMT
Subject: C=AU, ST=Some-State, O=Herp Derp plc, OU=herp.derp.plc, CN=herp.derp.plc
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:9e:7a:a8:35:41:e7:1c:bf:c8:6a:8f:50:4f:f4:
a1:09:5f:94:2c:14:2c:51:eb:63:3c:a6:53:db:e6:
de:2c:2e:8f:14:61:f6:5d:ea:41:4b:70:e3:fc:c7:
3c:30:bf:1f:de:15:8e:92:bb:1e:76:7a:74:35:f7:
ba:3c:68:cc:32:3f:be:e1:32:16:6a:b5:df:0d:0a:
02:c9:31:59:54:6d:18:70:2e:d8:b4:4a:41:c5:3e:
27:34:c0:08:3e:7a:c7:d7:6b:ac:a1:77:94:f1:0b:
e6:ed:8b:b3:20:57:f9:63:03:cd:17:43:11:c7:f3:
13:a3:74:ea:06:37:40:c7:7d
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
DE:D6:97:31:61:61:AB:34:2F:EE:92:CB:85:96:80:86:BF:8D:60:DD
X509v3 Authority Key Identifier:
keyid:DE:D6:97:31:61:61:AB:34:2F:EE:92:CB:85:96:80:86:BF:8D:60:DD
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
9a:50:80:40:5a:11:3d:99:0c:85:0a:68:e2:ad:8a:c9:db:c0:
9d:2f:80:1a:f6:52:cb:bd:5d:3c:de:41:b3:50:76:d9:d9:7a:
e9:ae:97:f4:68:dc:78:4c:90:82:5f:e9:57:17:70:49:26:18:
2b:ab:96:b7:26:0d:6f:63:4e:fd:40:6c:44:6a:5f:b9:26:76:
8d:1b:4a:74:3b:b2:cf:b5:cc:5b:50:a6:ea:1c:67:3a:13:29:
69:93:e2:b6:9e:14:97:a0:b2:3f:5f:3a:f4:c9:7f:5d:5a:7a:
7c:95:d4:2c:dc:83:a2:ba:5f:a9:10:de:f7:80:3d:e6:63:e8:
5b:ef
-----BEGIN CERTIFICATE-----
MIICojCCAgugAwIBAgIJAOuyV51pN2GxMA0GCSqGSIb3DQEBCwUAMGoxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMRYwFAYDVQQKDA1IZXJwIERlcnAg
cGxjMRYwFAYDVQQLDA1oZXJwLmRlcnAucGxjMRYwFAYDVQQDDA1oZXJwLmRlcnAu
cGxjMB4XDTE1MDkwMTIzMjkzNVoXDTE1MDkwMjIzMjkzNVowajELMAkGA1UEBhMC
QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxFjAUBgNVBAoMDUhlcnAgRGVycCBwbGMx
FjAUBgNVBAsMDWhlcnAuZGVycC5wbGMxFjAUBgNVBAMMDWhlcnAuZGVycC5wbGMw
gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ56qDVB5xy/yGqPUE/0oQlflCwU
LFHrYzymU9vm3iwujxRh9l3qQUtw4/zHPDC/H94VjpK7HnZ6dDX3ujxozDI/vuEy
Fmq13w0KAskxWVRtGHAu2LRKQcU+JzTACD56x9drrKF3lPEL5u2LsyBX+WMDzRdD
EcfzE6N06gY3QMd9AgMBAAGjUDBOMB0GA1UdDgQWBBTe1pcxYWGrNC/uksuFloCG
v41g3TAfBgNVHSMEGDAWgBTe1pcxYWGrNC/uksuFloCGv41g3TAMBgNVHRMEBTAD
AQH/MA0GCSqGSIb3DQEBCwUAA4GBAJpQgEBaET2ZDIUKaOKtisnbwJ0vgBr2Usu9
XTzeQbNQdtnZeumul/Ro3HhMkIJf6VcXcEkmGCurlrcmDW9jTv1AbERqX7kmdo0b
SnQ7ss+1zFtQpuocZzoTKWmT4raeFJegsj9fOvTJf11aenyV1Czcg6K6X6kQ3veA
PeZj6Fvv
-----END CERTIFICATE-----

View File

View File

@ -1,270 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest
import netaddr
from pyasn1.codec.der import encoder
from pyasn1.type import univ
from anchor.asn1 import rfc5280
from anchor.X509 import errors
from anchor.X509 import extension
class TestExtensionBase(unittest.TestCase):
def test_no_spec(self):
with self.assertRaises(errors.X509Error):
extension.X509Extension()
def test_invalid_asn(self):
with self.assertRaises(errors.X509Error):
extension.X509Extension("foobar")
def test_unknown_extension_str(self):
asn1 = rfc5280.Extension()
asn1['extnID'] = univ.ObjectIdentifier('1.2.3.4')
asn1['critical'] = False
asn1['extnValue'] = "foobar"
ext = extension.X509Extension(asn1)
self.assertEqual("1.2.3.4: <unknown>", str(ext))
def test_construct(self):
asn1 = rfc5280.Extension()
asn1['extnID'] = univ.ObjectIdentifier('1.2.3.4')
asn1['critical'] = False
asn1['extnValue'] = "foobar"
ext = extension.construct_extension(asn1)
self.assertIsInstance(ext, extension.X509Extension)
def test_construct_invalid_type(self):
with self.assertRaises(errors.X509Error):
extension.construct_extension("foobar")
def test_critical(self):
asn1 = rfc5280.Extension()
asn1['extnID'] = univ.ObjectIdentifier('1.2.3.4')
asn1['critical'] = False
asn1['extnValue'] = "foobar"
ext = extension.construct_extension(asn1)
self.assertFalse(ext.get_critical())
ext.set_critical(True)
self.assertTrue(ext.get_critical())
def test_serialise(self):
asn1 = rfc5280.Extension()
asn1['extnID'] = univ.ObjectIdentifier('1.2.3.4')
asn1['critical'] = False
asn1['extnValue'] = "foobar"
ext = extension.construct_extension(asn1)
self.assertEqual(ext.as_der(), encoder.encode(asn1))
def test_broken_set_value(self):
class SomeExt(extension.X509Extension):
spec = rfc5280.Extension
_oid = univ.ObjectIdentifier('1.2.3.4')
@classmethod
def _get_default_value(cls):
return 1234
with self.assertRaisesRegexp(errors.X509Error, 'incorrect type'):
SomeExt()
class TestBasicConstraints(unittest.TestCase):
def setUp(self):
self.ext = extension.X509ExtensionBasicConstraints()
def test_str(self):
self.assertEqual(str(self.ext),
"basicConstraints: CA: FALSE, pathLen: None")
def test_ca(self):
self.ext.set_ca(True)
self.assertTrue(self.ext.get_ca())
self.ext.set_ca(False)
self.assertFalse(self.ext.get_ca())
def test_pathlen(self):
self.ext.set_path_len_constraint(1)
self.assertEqual(1, self.ext.get_path_len_constraint())
class TestKeyUsage(unittest.TestCase):
def setUp(self):
self.ext = extension.X509ExtensionKeyUsage()
def test_usage_set(self):
self.ext.set_usage('digitalSignature', True)
self.ext.set_usage('keyAgreement', False)
self.assertTrue(self.ext.get_usage('digitalSignature'))
self.assertFalse(self.ext.get_usage('keyAgreement'))
def test_usage_reset(self):
self.ext.set_usage('digitalSignature', True)
self.ext.set_usage('digitalSignature', False)
self.assertFalse(self.ext.get_usage('digitalSignature'))
def test_usage_unset(self):
self.assertFalse(self.ext.get_usage('keyAgreement'))
def test_get_all_usage(self):
self.ext.set_usage('digitalSignature', True)
self.ext.set_usage('keyAgreement', False)
self.ext.set_usage('keyEncipherment', True)
self.assertEqual(set(['digitalSignature', 'keyEncipherment']),
set(self.ext.get_all_usages()))
def test_str(self):
self.ext.set_usage('digitalSignature', True)
self.assertEqual("keyUsage: digitalSignature", str(self.ext))
class TestSubjectAltName(unittest.TestCase):
def setUp(self):
self.ext = extension.X509ExtensionSubjectAltName()
self.domain = 'example.com'
self.ip = netaddr.IPAddress('1.2.3.4')
self.ip6 = netaddr.IPAddress('::1')
def test_dns_ids(self):
self.ext.add_dns_id(self.domain)
self.ext.add_ip(self.ip)
self.assertEqual([self.domain], self.ext.get_dns_ids())
def test_ips(self):
self.ext.add_dns_id(self.domain)
self.ext.add_ip(self.ip)
self.assertEqual([self.ip], self.ext.get_ips())
def test_ipv6(self):
self.ext.add_ip(self.ip6)
self.assertEqual([self.ip6], self.ext.get_ips())
def test_add_ip_invalid(self):
with self.assertRaises(errors.X509Error):
self.ext.add_ip("abcdef")
def test_str(self):
self.ext.add_dns_id(self.domain)
self.ext.add_ip(self.ip)
self.assertEqual("subjectAltName: DNS:example.com, IP:1.2.3.4",
str(self.ext))
class TestNameConstraints(unittest.TestCase):
def setUp(self):
self.ext = extension.X509ExtensionNameConstraints()
def test_length(self):
self.assertEqual(0, self.ext.get_permitted_length())
self.assertEqual(0, self.ext.get_excluded_length())
def test_add(self):
test_name = 'example.com'
test_type = 'dNSName'
self.assertEqual(0, self.ext.get_permitted_length())
self.assertEqual(0, self.ext.get_excluded_length())
self.ext.add_permitted(test_type, test_name)
self.assertEqual(1, self.ext.get_permitted_length())
self.assertEqual(0, self.ext.get_excluded_length())
self.ext.add_excluded(test_type, test_name)
self.assertEqual(1, self.ext.get_permitted_length())
self.assertEqual(1, self.ext.get_excluded_length())
def test_excluded(self):
self.ext.add_excluded('dNSName', 'example.com')
self.assertEqual(self.ext.get_excluded_range(0), (0, None))
self.assertEqual(self.ext.get_excluded_name(0),
('dNSName', b'example.com'))
def test_permitted(self):
self.ext.add_permitted('dNSName', 'example.com')
self.assertEqual(self.ext.get_permitted_range(0), (0, None))
self.assertEqual(self.ext.get_permitted_name(0),
('dNSName', b'example.com'))
class TestExtendedKeyUsage(unittest.TestCase):
def setUp(self):
self.ext = extension.X509ExtensionExtendedKeyUsage()
def test_get_all(self):
self.ext.set_usage(rfc5280.id_kp_clientAuth, True)
self.ext.set_usage(rfc5280.id_kp_codeSigning, True)
usages = self.ext.get_all_usages()
self.assertEqual(2, len(usages))
self.assertIn(rfc5280.id_kp_clientAuth, usages)
def test_get_one(self):
self.assertFalse(self.ext.get_usage(rfc5280.id_kp_clientAuth))
self.ext.set_usage(rfc5280.id_kp_clientAuth, True)
self.assertTrue(self.ext.get_usage(rfc5280.id_kp_clientAuth))
def test_set(self):
self.assertEqual(0, len(self.ext.get_all_usages()))
self.ext.set_usage(rfc5280.id_kp_clientAuth, True)
self.assertEqual(1, len(self.ext.get_all_usages()))
self.ext.set_usage(rfc5280.id_kp_clientAuth, True)
self.assertEqual(1, len(self.ext.get_all_usages()))
self.ext.set_usage(rfc5280.id_kp_codeSigning, True)
self.assertEqual(2, len(self.ext.get_all_usages()))
def test_unset(self):
self.ext.set_usage(rfc5280.id_kp_clientAuth, True)
self.ext.set_usage(rfc5280.id_kp_clientAuth, False)
self.assertEqual(0, len(self.ext.get_all_usages()))
self.ext.set_usage(rfc5280.id_kp_clientAuth, False)
self.assertEqual(0, len(self.ext.get_all_usages()))
def test_str(self):
self.ext.set_usage(rfc5280.id_kp_clientAuth, True)
self.ext.set_usage(rfc5280.id_kp_codeSigning, True)
self.assertEqual(
"extKeyUsage: TLS Web Client Authentication, Code Signing",
str(self.ext))
def test_invalid_usage(self):
self.assertRaises(ValueError, self.ext.get_usage,
univ.ObjectIdentifier('1.2.3.4'))
self.assertRaises(ValueError, self.ext.set_usage, True,
univ.ObjectIdentifier('1.2.3.4'))
class TestAuthorityKeyId(unittest.TestCase):
def setUp(self):
self.ext = extension.X509ExtensionAuthorityKeyId()
def test_key_id(self):
key_id = b"12345678"
self.ext.set_key_id(key_id)
self.assertEqual(key_id, self.ext.get_key_id())
def test_name_serial(self):
s = 12345678
self.ext.set_serial(s)
self.assertEqual(s, self.ext.get_serial())
class TestSubjectKeyId(unittest.TestCase):
def setUp(self):
self.ext = extension.X509ExtensionSubjectKeyId()
def test_key_id(self):
key_id = b"12345678"
self.ext.set_key_id(key_id)
self.assertEqual(key_id, self.ext.get_key_id())

View File

@ -1,34 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest
from anchor.X509 import utils
class TestASN1Time(unittest.TestCase):
def test_round_check(self):
t = 0
asn1_time = utils.timestamp_to_asn1_time(t)
res = utils.asn1_time_to_timestamp(asn1_time)
self.assertEqual(t, res)
def test_post_2050(self):
"""Test date post 2050, which causes different encoding."""
t = 2600000000
asn1_time = utils.timestamp_to_asn1_time(t)
res = utils.asn1_time_to_timestamp(asn1_time)
self.assertEqual(t, res)

View File

@ -1,296 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest
import mock
from pyasn1.type import univ as asn1_univ
import io
import textwrap
from anchor.X509 import certificate
from anchor.X509 import errors as x509_errors
from anchor.X509 import extension
from anchor.X509 import name as x509_name
class TestX509Cert(unittest.TestCase):
cert_data = textwrap.dedent(u"""
-----BEGIN CERTIFICATE-----
MIICuDCCAiGgAwIBAgIJAIaZlZ0Oms2fMA0GCSqGSIb3DQEBCwUAMGoxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMRYwFAYDVQQKDA1IZXJwIERlcnAg
cGxjMRYwFAYDVQQLDA1oZXJwLmRlcnAucGxjMRYwFAYDVQQDDA1oZXJwLmRlcnAu
cGxjMB4XDTE1MDkwMTIzNDcwNVoXDTE1MDkwMjIzNDcwNVowgZQxCzAJBgNVBAYT
AlVLMQ8wDQYDVQQIDAZOYXJuaWExEjAQBgNVBAcMCUZ1bmt5dG93bjEXMBUGA1UE
CgwOQW5jaG9yIFRlc3RpbmcxEDAOBgNVBAsMB3Rlc3RpbmcxFDASBgNVBAMMC2Fu
Y2hvci50ZXN0MR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGFuY2hvci50ZXN0MIGfMA0G
CSqGSIb3DQEBAQUAA4GNADCBiQKBgQCeeqg1Qeccv8hqj1BP9KEJX5QsFCxR62M8
plPb5t4sLo8UYfZd6kFLcOP8xzwwvx/eFY6Sux52enQ197o8aMwyP77hMhZqtd8N
CgLJMVlUbRhwLti0SkHFPic0wAg+esfXa6yhd5TxC+bti7MgV/ljA80XQxHH8xOj
dOoGN0DHfQIDAQABozswOTAfBgNVHSMEGDAWgBTe1pcxYWGrNC/uksuFloCGv41g
3TAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE8DANBgkqhkiG9w0BAQsFAAOBgQAy+2HQ
kXyNc5SwjvCXMDWMTKSB5bEWPxuJw3Lf1G4czHAyANzGlm1HJ/h6Z8NSwEy9x0xj
iFnpbc39fGoeApkEqVhY0WyJ7qbCuJsExE+ra6w+iPIKvjez+Ymp+zCDsiTIJEnf
2jsyzhghVa/FgDpQYQEJHAuGTEAvkQITp8IUvg==
-----END CERTIFICATE-----""")
key_dsa_data = textwrap.dedent("""
-----BEGIN DSA PARAMETERS-----
MIICLAKCAQEA59W1OsK9Tv7DRbxzibGVpBAL2Oz8JhbV3ii7WAat+UfTBLAnfdva
7UE8odu1l8p41N/8H/tDWgPh6tOgdX0YT9HDsILymQxzUEscliFZKmYg7YdSH3Zd
6DglOT7CqYxX0r9gK/BOh8ESe3gqKncnThHnO8Eu9wP8HNcrN00EOqP+fJpbS0lu
iifD9JdFY5YpCsLDIvpPbM0NCDuANPo10N3qqC8BuNiu0VfZpRSBcqzU1kwABT5n
y7+8RMh5Xaa7xnhGctJ9s9n+QfWcF/vbgiDOBttb3d8r8Pqvoou8v7Q38Q6zILhf
hajevqjGqZwodbvbHGfFbWapgBjpBIr4zwIhAOq6uryEHQglirWCGFJLQlkzxghy
ctHBRXGuKYb+ltRTAoIBAHRUFxzd1vhjKQ5atIdG0AiXUNm7/uboe21EJDLf4lkE
7UHDZfwsHXxQHfozzIsp7gHcw7F6AVCgiNRi9vBYOemPswevoWiVKqLTVt1wMogD
EJI6VAQEbBmSrtvyuClCkEAlIY6daX9EV9KqbnetS4/xv4WFQ9FPE47VyQ50vvxK
JSyNZnJ1lN6FUD9R5YYfwERgND8EYJBD10UBKIvtORICTJUfaDAweTWhaVcXUID7
VGNGPauOdVQzWsWTrQn/f/hbXCB/KXgv1l92D6rEoT2j2YrqIv/qD/ZxPwhBfLdr
W241Cb+LT05LVCokRbWUdjfuO8SdSBAIvT9P6umG/uQ=
-----END DSA PARAMETERS-----
-----BEGIN DSA PRIVATE KEY-----
MIIDVwIBAAKCAQEA59W1OsK9Tv7DRbxzibGVpBAL2Oz8JhbV3ii7WAat+UfTBLAn
fdva7UE8odu1l8p41N/8H/tDWgPh6tOgdX0YT9HDsILymQxzUEscliFZKmYg7YdS
H3Zd6DglOT7CqYxX0r9gK/BOh8ESe3gqKncnThHnO8Eu9wP8HNcrN00EOqP+fJpb
S0luiifD9JdFY5YpCsLDIvpPbM0NCDuANPo10N3qqC8BuNiu0VfZpRSBcqzU1kwA
BT5ny7+8RMh5Xaa7xnhGctJ9s9n+QfWcF/vbgiDOBttb3d8r8Pqvoou8v7Q38Q6z
ILhfhajevqjGqZwodbvbHGfFbWapgBjpBIr4zwIhAOq6uryEHQglirWCGFJLQlkz
xghyctHBRXGuKYb+ltRTAoIBAHRUFxzd1vhjKQ5atIdG0AiXUNm7/uboe21EJDLf
4lkE7UHDZfwsHXxQHfozzIsp7gHcw7F6AVCgiNRi9vBYOemPswevoWiVKqLTVt1w
MogDEJI6VAQEbBmSrtvyuClCkEAlIY6daX9EV9KqbnetS4/xv4WFQ9FPE47VyQ50
vvxKJSyNZnJ1lN6FUD9R5YYfwERgND8EYJBD10UBKIvtORICTJUfaDAweTWhaVcX
UID7VGNGPauOdVQzWsWTrQn/f/hbXCB/KXgv1l92D6rEoT2j2YrqIv/qD/ZxPwhB
fLdrW241Cb+LT05LVCokRbWUdjfuO8SdSBAIvT9P6umG/uQCggEBAKrZAppbnKf1
pzSvE3gTaloitAJG+79BML5h1n67EWuv0i+Fq4eUAVJ23R8GR1HrYw6utZoYbu8u
k8eHrArMfTfbFaLwK/Nv33Hfm3aTTXnY6auLNkpbiZXuCQjWBFhb6F+B42V9/JJ8
RJ1UV6Y2ajjjMvpeh0cPlARw5UpKBgQ933DhefCWyFBPsPToFvd3uPO+GUN6VpNY
iR7G0AH3/LSVJRuz5/QCp86uLIoU3fBEf1KGYJrkVKlc9DtcNmDXgpP0d3fK+4Jw
bGvi5AD1sQOWryNujyS/d2K/PAagsD0M6XJFgkEV592OSlygbYtuo3t4AtAy8F0f
VHNXq2l01FMCIQCrkk1749eQg4W6j7HfLFvjbDcuIFTw98IKyEZuZ93cdA==
-----END DSA PRIVATE KEY-----""").encode('ascii')
key_rsa_data = textwrap.dedent("""
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCeeqg1Qeccv8hqj1BP9KEJX5QsFCxR62M8plPb5t4sLo8UYfZd
6kFLcOP8xzwwvx/eFY6Sux52enQ197o8aMwyP77hMhZqtd8NCgLJMVlUbRhwLti0
SkHFPic0wAg+esfXa6yhd5TxC+bti7MgV/ljA80XQxHH8xOjdOoGN0DHfQIDAQAB
AoGBAJ2ozJpe+7qgGJPaCz3f0izvBwtq7kR49fqqRZbo8HHnx7OxWVVI7LhOkKEy
2/Bq0xsvOu1CdiXL4LynvIDIiQqLaeINzG48Rbk+0HadbXblt3nDkIWdYII6zHKI
W9ewX4KpHEPbrlEO9BjAlAcYsDIvFIMYpQhtQ+0R/gmZ99WJAkEAz5C2a6FIcMbE
o3aTc9ECq99zY7lxh+6aLpUdIeeHyb/QzfGDBdlbpBAkA6EcxSqp0aqH4xIQnYHa
3P5ZCShqSwJBAMN1sb76xq94xkg2cxShPFPAE6xKRFyKqLgsBYVtulOdfOtOnjh9
1SK2XQQfBRIRdG4Q/gDoCP8XQHpJcWMk+FcCQDnuJqulaOVo5GrG5mJ1nCxCAh98
G06X7lo/7dCPoRtSuMExvaK9RlFk29hTeAcjYCAPWzupyA9dtarmJg1jRT8CQCKf
gYnb8D/6+9yk0IPR/9ayCooVacCeyz48hgnZowzWs98WwQ4utAd/GED3obVOpDov
Bl9wus889i3zPoOac+cCQCZHredQcJGd4dlthbVtP2NhuPXz33JuETGR9pXtsDUZ
uX/nSq1oo9kUh/dPOz6aP5Ues1YVe3LExmExPBQfwIE=
-----END RSA PRIVATE KEY-----""").encode('ascii')
def setUp(self):
super(TestX509Cert, self).setUp()
self.cert = certificate.X509Certificate.from_buffer(
TestX509Cert.cert_data)
def tearDown(self):
pass
def test_bad_data_throws(self):
bad_data = (
u"some bad data is "
"EHRlc3RAYW5jaG9yLnRlc3QwTDANBgkqhkiG9w0BAQEFAAM7ADA4AjEA6m")
cert = certificate.X509Certificate()
self.assertRaises(x509_errors.X509Error,
cert.from_buffer,
bad_data)
def test_get_subject_countryName(self):
name = self.cert.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_countryName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "countryName")
self.assertEqual(entries[0].get_value(), "UK")
def test_get_subject_stateOrProvinceName(self):
name = self.cert.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_stateOrProvinceName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "stateOrProvinceName")
self.assertEqual(entries[0].get_value(), "Narnia")
def test_get_subject_localityName(self):
name = self.cert.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_localityName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "localityName")
self.assertEqual(entries[0].get_value(), "Funkytown")
def test_get_subject_organizationName(self):
name = self.cert.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_organizationName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "organizationName")
self.assertEqual(entries[0].get_value(), "Anchor Testing")
def test_get_subject_organizationUnitName(self):
name = self.cert.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_organizationalUnitName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "organizationalUnitName")
self.assertEqual(entries[0].get_value(), "testing")
def test_get_subject_commonName(self):
name = self.cert.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_commonName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "commonName")
self.assertEqual(entries[0].get_value(), "anchor.test")
def test_get_subject_emailAddress(self):
name = self.cert.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_pkcs9_emailAddress)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "emailAddress")
self.assertEqual(entries[0].get_value(), "test@anchor.test")
def test_get_issuer_countryName(self):
name = self.cert.get_issuer()
entries = name.get_entries_by_oid(x509_name.OID_countryName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "countryName")
self.assertEqual(entries[0].get_value(), "AU")
def test_get_issuer_stateOrProvinceName(self):
name = self.cert.get_issuer()
entries = name.get_entries_by_oid(x509_name.OID_stateOrProvinceName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "stateOrProvinceName")
self.assertEqual(entries[0].get_value(), "Some-State")
def test_get_issuer_organizationName(self):
name = self.cert.get_issuer()
entries = name.get_entries_by_oid(x509_name.OID_organizationName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "organizationName")
self.assertEqual(entries[0].get_value(), "Herp Derp plc")
def test_get_issuer_commonName(self):
name = self.cert.get_issuer()
entries = name.get_entries_by_oid(x509_name.OID_commonName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "commonName")
self.assertEqual(entries[0].get_value(), "herp.derp.plc")
def test_set_subject(self):
name = x509_name.X509Name()
name.add_name_entry(x509_name.OID_countryName, 'UK')
self.cert.set_subject(name)
name = self.cert.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_countryName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "countryName")
self.assertEqual(entries[0].get_value(), "UK")
def test_set_issuer(self):
name = x509_name.X509Name()
name.add_name_entry(x509_name.OID_countryName, 'UK')
self.cert.set_issuer(name)
name = self.cert.get_issuer()
entries = name.get_entries_by_oid(x509_name.OID_countryName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "countryName")
self.assertEqual(entries[0].get_value(), "UK")
def test_read_from_file(self):
open_name = 'anchor.X509.certificate.open'
f = io.StringIO(TestX509Cert.cert_data)
with mock.patch(open_name, create=True) as mock_open:
mock_open.return_value = f
cert = certificate.X509Certificate.from_file("some_path")
name = cert.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_countryName)
self.assertEqual(entries[0].get_value(), "UK")
def test_get_fingerprint(self):
fp = self.cert.get_fingerprint()
self.assertEqual(fp, '03C6B30446157984C28A3C97F1616B96'
'5DED16744573F203A4EA51AB1AFA1F10')
def test_get_fingerprint_invalid_hash(self):
with self.assertRaises(x509_errors.X509Error):
self.cert.get_fingerprint('no_such_hash')
def test_get_version(self):
v = self.cert.get_version()
self.assertEqual(v, 2)
def test_set_version(self):
self.cert.set_version(5)
v = self.cert.get_version()
self.assertEqual(v, 5)
def test_get_not_before(self):
val = self.cert.get_not_before()
self.assertEqual(1441151225.0, val)
def test_set_not_before(self):
self.cert.set_not_before(0) # seconds since epoch
val = self.cert.get_not_before()
self.assertEqual(0, val)
def test_get_not_after(self):
val = self.cert.get_not_after()
self.assertEqual(1441237625.0, val)
def test_set_not_after(self):
self.cert.set_not_after(0) # seconds since epoch
val = self.cert.get_not_after()
self.assertEqual(0, val)
def test_get_extensions(self):
exts = self.cert.get_extensions()
self.assertEqual(3, len(exts))
def test_add_extensions(self):
bc = extension.X509ExtensionBasicConstraints()
self.cert.add_extension(bc, 2)
exts = self.cert.get_extensions()
self.assertEqual(3, len(exts))
def test_add_extensions_invalid(self):
with self.assertRaises(x509_errors.X509Error):
self.cert.add_extension("abcdef", 2)
def test_verify_unknown_key(self):
with self.assertRaises(x509_errors.X509Error):
self.cert.verify("abc")
def test_verify_signature_mismatch(self):
alg = asn1_univ.ObjectIdentifier('1.2.3.4')
self.cert._cert['signatureAlgorithm']['algorithm'] = alg
with self.assertRaises(x509_errors.X509Error):
self.cert.verify()
def test_verify_algo_mismatch(self):
alg = asn1_univ.ObjectIdentifier('1.2.3.4')
self.cert._cert['signatureAlgorithm']['algorithm'] = alg
with self.assertRaises(x509_errors.X509Error):
self.cert.verify("abc")

View File

@ -1,199 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import io
import textwrap
import unittest
import mock
from pyasn1_modules import rfc2459
from anchor.signers import cryptography_io
from anchor.X509 import errors as x509_errors
from anchor.X509 import extension
from anchor.X509 import name as x509_name
from anchor.X509 import signing_request
from anchor.X509 import utils
import tests
class TestX509Csr(tests.DefaultRequestMixin, unittest.TestCase):
key_rsa_data = textwrap.dedent("""
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCeeqg1Qeccv8hqj1BP9KEJX5QsFCxR62M8plPb5t4sLo8UYfZd
6kFLcOP8xzwwvx/eFY6Sux52enQ197o8aMwyP77hMhZqtd8NCgLJMVlUbRhwLti0
SkHFPic0wAg+esfXa6yhd5TxC+bti7MgV/ljA80XQxHH8xOjdOoGN0DHfQIDAQAB
AoGBAJ2ozJpe+7qgGJPaCz3f0izvBwtq7kR49fqqRZbo8HHnx7OxWVVI7LhOkKEy
2/Bq0xsvOu1CdiXL4LynvIDIiQqLaeINzG48Rbk+0HadbXblt3nDkIWdYII6zHKI
W9ewX4KpHEPbrlEO9BjAlAcYsDIvFIMYpQhtQ+0R/gmZ99WJAkEAz5C2a6FIcMbE
o3aTc9ECq99zY7lxh+6aLpUdIeeHyb/QzfGDBdlbpBAkA6EcxSqp0aqH4xIQnYHa
3P5ZCShqSwJBAMN1sb76xq94xkg2cxShPFPAE6xKRFyKqLgsBYVtulOdfOtOnjh9
1SK2XQQfBRIRdG4Q/gDoCP8XQHpJcWMk+FcCQDnuJqulaOVo5GrG5mJ1nCxCAh98
G06X7lo/7dCPoRtSuMExvaK9RlFk29hTeAcjYCAPWzupyA9dtarmJg1jRT8CQCKf
gYnb8D/6+9yk0IPR/9ayCooVacCeyz48hgnZowzWs98WwQ4utAd/GED3obVOpDov
Bl9wus889i3zPoOac+cCQCZHredQcJGd4dlthbVtP2NhuPXz33JuETGR9pXtsDUZ
uX/nSq1oo9kUh/dPOz6aP5Ues1YVe3LExmExPBQfwIE=
-----END RSA PRIVATE KEY-----""").encode('ascii')
def setUp(self):
super(TestX509Csr, self).setUp()
self.csr = signing_request.X509Csr.from_buffer(
TestX509Csr.csr_sample_bytes)
def tearDown(self):
pass
def test_get_pubkey(self):
pubkey = self.csr.get_pubkey()
self.assertEqual(pubkey['algorithm']['algorithm'],
rfc2459.rsaEncryption)
def test_get_extensions(self):
exts = self.csr.get_extensions()
self.assertEqual(len(exts), 2)
self.assertFalse(exts[1].get_ca())
self.assertIsNone(exts[1].get_path_len_constraint())
self.assertTrue(exts[0].get_usage('digitalSignature'))
self.assertTrue(exts[0].get_usage('nonRepudiation'))
self.assertTrue(exts[0].get_usage('keyEncipherment'))
self.assertFalse(exts[0].get_usage('cRLSign'))
def test_add_extension(self):
csr = signing_request.X509Csr()
bc = extension.X509ExtensionBasicConstraints()
san = extension.X509ExtensionSubjectAltName()
csr.add_extension(bc)
self.assertEqual(1, len(csr.get_extensions()))
csr.add_extension(bc)
self.assertEqual(1, len(csr.get_extensions()))
csr.add_extension(san)
self.assertEqual(2, len(csr.get_extensions()))
def test_add_extension_invalid_type(self):
csr = signing_request.X509Csr()
with self.assertRaises(x509_errors.X509Error):
csr.add_extension(1234)
def test_read_from_file(self):
open_name = 'anchor.X509.signing_request.open'
f = io.BytesIO(self.csr_sample_bytes)
with mock.patch(open_name, create=True) as mock_open:
mock_open.return_value = f
csr = signing_request.X509Csr.from_file("some_path")
name = csr.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_countryName)
self.assertEqual(entries[0].get_value(), "UK")
def test_open_failure_throws(self):
open_name = 'anchor.X509.signing_request.open'
with mock.patch(open_name, create=True) as mock_open:
mock_open.side_effect = IOError(2, "No such file or directory",
"some_path")
self.assertRaisesRegexp(x509_errors.X509Error,
"Could not read file",
signing_request.X509Csr.from_file,
"some_path")
def test_read_failure_throws(self):
f = mock.Mock()
f.read.side_effect = IOError(5, "Read failed")
self.assertRaisesRegexp(x509_errors.X509Error,
"Could not read from source",
signing_request.X509Csr.from_open_file,
f)
def test_bad_pem_throws(self):
bad_data = (
b"-----BEGIN SOMETHING-----\n"
b"++++++\n"
b"-----END SOMETHING-----\n"
)
csr = signing_request.X509Csr()
self.assertRaisesRegexp(x509_errors.X509Error, "not in PEM format",
csr.from_buffer,
bad_data)
def test_bad_data_throws(self):
bad_data = (
b"some bad data is "
b"EHRlc3RAYW5jaG9yLnRlc3QwTDANBgkqhkiG9w0BAQEFAAM7ADA4AjEA6m")
csr = signing_request.X509Csr()
self.assertRaisesRegexp(x509_errors.X509Error, "No PEM data found",
csr.from_buffer,
bad_data)
def test_get_subject_countryName(self):
name = self.csr.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_countryName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "countryName")
self.assertEqual(entries[0].get_value(), "UK")
def test_get_subject_stateOrProvinceName(self):
name = self.csr.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_stateOrProvinceName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "stateOrProvinceName")
self.assertEqual(entries[0].get_value(), "Narnia")
def test_get_subject_localityName(self):
name = self.csr.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_localityName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "localityName")
self.assertEqual(entries[0].get_value(), "Funkytown")
def test_get_subject_organizationName(self):
name = self.csr.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_organizationName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "organizationName")
self.assertEqual(entries[0].get_value(), "Anchor Testing")
def test_get_subject_organizationUnitName(self):
name = self.csr.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_organizationalUnitName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "organizationalUnitName")
self.assertEqual(entries[0].get_value(), "testing")
def test_get_subject_commonName(self):
name = self.csr.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_commonName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "commonName")
self.assertEqual(entries[0].get_value(), self.csr_sample_cn)
def test_get_subject_emailAddress(self):
name = self.csr.get_subject()
entries = name.get_entries_by_oid(x509_name.OID_pkcs9_emailAddress)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "emailAddress")
self.assertEqual(entries[0].get_value(), "test@example.com")
def test_sign(self):
key = utils.get_private_key_from_pem(self.key_rsa_data)
signer = cryptography_io.make_signer(key, 'RSA', 'SHA256')
self.csr.sign('RSA', 'SHA256', signer)
# 10 bytes is definitely enough for non malicious case, right?
self.assertEqual(b'\x16\xbd!\x9b\xfb\xfd\x10\xa1\xaf\x92',
self.csr._get_signature()[:10])
def test_verify(self):
self.assertTrue(self.csr.verify())

View File

@ -1,132 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest
from anchor.X509 import errors as x509_errors
from anchor.X509 import name as x509_name
class TestX509Name(unittest.TestCase):
def setUp(self):
super(TestX509Name, self).setUp()
self.name = x509_name.X509Name()
self.name.add_name_entry(x509_name.OID_countryName,
"UK") # must be 2 chars
self.name.add_name_entry(x509_name.OID_stateOrProvinceName, "test_ST")
self.name.add_name_entry(x509_name.OID_localityName, "test_L")
self.name.add_name_entry(x509_name.OID_organizationName, "test_O")
self.name.add_name_entry(x509_name.OID_organizationalUnitName,
"test_OU")
self.name.add_name_entry(x509_name.OID_commonName, "test_CN")
self.name.add_name_entry(x509_name.OID_pkcs9_emailAddress,
"test_Email")
self.name.add_name_entry(x509_name.OID_surname, "test_SN")
self.name.add_name_entry(x509_name.OID_givenName, "test_GN")
def tearDown(self):
pass
def test_add_bad_entry_throws(self):
self.assertRaises(x509_errors.X509Error,
self.name.add_name_entry,
-1, "BAD_WRONG")
def test_set_bad_c_throws(self):
self.assertRaises(x509_errors.X509Error,
self.name.add_name_entry,
x509_name.OID_countryName, "BAD_WRONG")
def test_name_to_string(self):
val = str(self.name)
self.assertEqual(val, ("/C=UK/ST=test_ST/L=test_L/O=test_O/OU=test_OU"
"/CN=test_CN/emailAddress=test_Email/"
"SN=test_SN/GN=test_GN"))
def test_get_countryName(self):
entries = self.name.get_entries_by_oid(x509_name.OID_countryName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "countryName")
self.assertEqual(entries[0].get_value(), "UK")
def test_get_stateOrProvinceName(self):
entries = self.name.get_entries_by_oid(
x509_name.OID_stateOrProvinceName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "stateOrProvinceName")
self.assertEqual(entries[0].get_value(), "test_ST")
def test_get_subject_localityName(self):
entries = self.name.get_entries_by_oid(x509_name.OID_localityName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "localityName")
self.assertEqual(entries[0].get_value(), "test_L")
def test_get_organizationName(self):
entries = self.name.get_entries_by_oid(x509_name.OID_organizationName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "organizationName")
self.assertEqual(entries[0].get_value(), "test_O")
def test_get_organizationUnitName(self):
entries = self.name.get_entries_by_oid(
x509_name.OID_organizationalUnitName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "organizationalUnitName")
self.assertEqual(entries[0].get_value(), "test_OU")
def test_get_commonName(self):
entries = self.name.get_entries_by_oid(x509_name.OID_commonName)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "commonName")
self.assertEqual(entries[0].get_value(), "test_CN")
def test_get_emailAddress(self):
entries = self.name.get_entries_by_oid(
x509_name.OID_pkcs9_emailAddress)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].get_name(), "emailAddress")
self.assertEqual(entries[0].get_value(), "test_Email")
def test_entry_to_string(self):
entries = self.name.get_entries_by_oid(
x509_name.OID_pkcs9_emailAddress)
self.assertEqual(len(entries), 1)
self.assertEqual(str(entries[0]), "emailAddress: test_Email")
def test_entry_length(self):
num = len(self.name)
self.assertEqual(num, 9)
def test_entry_index_good(self):
self.assertEqual("givenName: test_GN", str(self.name[8]))
def test_entry_index_bad(self):
with self.assertRaises(IndexError):
self.name[9]
def test_entry_itter(self):
val = [str(e) for e in self.name]
self.assertEqual("countryName: UK", val[0])
self.assertEqual("givenName: test_GN", val[8])
def test_deep_clone(self):
orig = x509_name.X509Name()
orig.add_name_entry(x509_name.OID_countryName, "UK")
clone = x509_name.X509Name(orig._name_obj)
self.assertEqual(str(orig), str(clone))
clone.add_name_entry(x509_name.OID_stateOrProvinceName, "test_ST")
self.assertNotEqual(str(orig), str(clone))

View File

@ -1,102 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
import textwrap
# NOTE(tkelsey): by default Python 2.7 has no default logging handler
# this fixes the "No handler for logger ..." message spam
#
handler = logging.NullHandler()
logging.getLogger().addHandler(handler)
class DefaultConfigMixin(object):
"""Mixin for reuse in any test class which needs to load a config.
`sample_conf` is always a valid, no thrills configuration. It can be
reused in any test case. Constructing it in setUp() guarantees that it
can be changed without affecting other tests.
"""
def setUp(self):
self.sample_conf_auth = {
"default_auth": {
"backend": "static",
"user": "myusername",
"secret": "simplepassword"
}
}
self.sample_conf_ca = {
"default_ca": {
"backend": "anchor",
"cert_path": "tests/CA/root-ca.crt",
"key_path": "tests/CA/root-ca-unwrapped.key",
"output_path": "certs",
"signing_hash": "sha256",
"valid_hours": 24
}
}
self.sample_conf_validators = {
"common_name": {
"allowed_domains": [".example.com"]
}
}
self.sample_conf_fixups = {
}
self.sample_conf_ra = {
"default_ra": {
"authentication": "default_auth",
"signing_ca": "default_ca",
"validators": self.sample_conf_validators,
"fixups": self.sample_conf_fixups,
}
}
self.sample_conf = {
"authentication": self.sample_conf_auth,
"signing_ca": self.sample_conf_ca,
"registration_authority": self.sample_conf_ra,
}
super(DefaultConfigMixin, self).setUp()
class DefaultRequestMixin(object):
# CN=server1.example.com
# 2048 RSA, basicConstraints, keyUsage exts
csr_sample_cn = 'server1.example.com'
csr_sample = textwrap.dedent("""
-----BEGIN CERTIFICATE REQUEST-----
MIIDDjCCAfYCAQAwgZwxCzAJBgNVBAYTAlVLMQ8wDQYDVQQIEwZOYXJuaWExEjAQ
BgNVBAcTCUZ1bmt5dG93bjEXMBUGA1UEChMOQW5jaG9yIFRlc3RpbmcxEDAOBgNV
BAsTB3Rlc3RpbmcxHDAaBgNVBAMTE3NlcnZlcjEuZXhhbXBsZS5jb20xHzAdBgkq
hkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDhQloUTMZwBFgbseH5vk4S+mgqwyZDytu9S6x7YPv4aav/FTQd
W/RJB07YvUIZSJ50YScNSzXrtjqqifjdvnyiVYpS+vP8/yZIclJt8BNLwA3ESvHO
75leRhSahxMkIMW7WfaV4ys8jkGDx3fISCn/jo5zelaLXaiHAzGRRMKefWmy54lX
W6jh1caoadRsnFQbAmAljW0JNQ53Sr2KOwVu6I8/IJ9PcT16D0WembvuOsNZZ8V9
y2FYiJ4FYesN9JGoKvBC8U1pr+FXpNfEdaniNbfRsz5gCsap3mxMMLKlFS7AB2ar
zw5awegV9M7gMYkg4e6HWl33fS+kt/zSC53rAgMBAAGgLDAqBgkqhkiG9w0BCQ4x
HTAbMAsGA1UdDwQEAwIF4DAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IB
AQArTSUNFZHqUnCL+TLVgDSq9oaSutO3vu1g+EKfFxN2rG5HrxbAc2eC8TaMfUVd
D2JaEkhi9X7wPpVKIVwMo4nYVO8ke1MdXRLecNzLRT4sC40ZuOoDxOFEzm5BibGv
OLty0xKx3fylL0qa+wMXQNDWVcbq3OcJNo4v41fl4jlab4Fx5mWaCnKja+LnJT45
4wJQQN+UFPwvEt3Ay2UqvzVVUlJ3tO30f5WZitlpYy9txLaV9v6xdc2N/YMgQ7Tz
DxpZNBHlkA6LWaRqAtWws3uvom7IjHGgSr7UITrOR5iO5Hrm85X7K0AT6Bu75RZL
+uYLLfj9Nb/iznREl9E3a/fN
-----END CERTIFICATE REQUEST-----""")
csr_sample_bytes = csr_sample.encode('ascii')

View File

View File

@ -1,143 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest
import uuid
import mock
import requests
import requests_mock
from anchor.auth import keystone
from anchor.auth import results
class AuthKeystoneTests(unittest.TestCase):
def setUp(self):
self.config = "anchor.jsonloader.conf._config"
self.data = {'auth': {'keystone': {'url': 'http://localhost:35357'}}}
self.json_response = {
"token": {
"audit_ids": [
"TPDsHuK_QCaKwvkVlAer8A"
],
"catalog": [
{
"endpoints": [
{
"id": "1390df96096d4bd19add44811db34397",
"interface": "public",
"region": "RegionOne",
"region_id": "RegionOne",
"url": "http://10.0.2.15:5000/v2.0"
},
{
"id": "534bcae735614781a03069d637b21570",
"interface": "internal",
"region": "RegionOne",
"region_id": "RegionOne",
"url": "http://10.0.2.15:5000/v2.0"
},
{
"id": "cc7e879d691e4e4b9f4afecb1a3ce8f0",
"interface": "admin",
"region": "RegionOne",
"region_id": "RegionOne",
"url": "http://10.0.2.15:35357/v2.0"
}
],
"id": "3010a0c9af684db28659f0e9e08ee863",
"name": "keystone",
"type": "identity"
}
],
"expires_at": "2015-07-27T02:38:09.000000Z",
"extras": {},
"issued_at": "2015-07-27T01:38:09.409616",
"methods": [
"password",
"token"
],
"project": {
"domain": {
"id": "default",
"name": "Default"
},
"id": "5b2e7bd5d5954fdaa2d931285df8a132",
"name": "demo"
},
"roles": [
{
"id": "35a1d29b54f64c969aa9be288ec9d39a",
"name": "anotherrole"
},
{
"id": "9f64371fcbd64c669ab1a24686a1a367",
"name": "Member"
}
],
"user": {
"domain": {
"id": "default",
"name": "Default"
},
"id": "b2016b9338214cda926d5631c1fbc40c",
"name": "demo"
}
}
}
self.user = self.json_response['token']['user']['name']
self.roles = [role['name']
for role in self.json_response['token']['roles']]
self.user_id = self.json_response['token']['user']['id']
self.project_id = self.json_response['token']['project']['id']
self.expected = results.AuthDetails(
username=self.user, groups=self.roles,
user_id=self.user_id, project_id=self.project_id)
self.keystone_url = self.data['auth'][
'keystone']['url'] + '/v3/auth/tokens'
self.keystone_token = uuid.uuid4().hex
super(AuthKeystoneTests, self).setUp()
def tearDown(self):
pass
def test_parse_keystone_valid_response(self):
with mock.patch.dict(self.config, self.data):
with requests_mock.mock() as m:
m.get(self.keystone_url, json=self.json_response,
status_code=200)
requests.get(self.keystone_url)
self.assertEqual(keystone.login(
None, self.keystone_token), self.expected)
def test_parse_keystone_auth_fail(self):
with mock.patch.dict(self.config, self.data):
with requests_mock.mock() as m:
m.get(self.keystone_url, status_code=401)
self.assertEqual(keystone.login(
None, self.keystone_token), None)
def test_parse_keystone_ok_but_malformed_response(self):
with mock.patch.dict(self.config, self.data):
with requests_mock.mock() as m:
m.get(self.keystone_url, json={}, status_code=200)
self.assertEqual(keystone.login(
None, self.keystone_token), None)

View File

@ -1,117 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest
from ldap3.core import exceptions as ldap3_exc
import mock
from webob import exc as http_status
from anchor import auth
from anchor.auth import results
from anchor import jsonloader
import tests
class AuthLdapTests(tests.DefaultConfigMixin, unittest.TestCase):
def setUp(self):
super(AuthLdapTests, self).setUp()
self.sample_conf_auth['default_auth'] = {
"backend": "ldap",
"host": "ldap.example.com",
"base": "CN=Users,DC=example,DC=com",
"domain": "example.com",
"port": 636,
"ssl": True
}
def tearDown(self):
pass
@mock.patch('ldap3.Connection')
def test_login_good(self, mock_connection):
"""Test all static user/pass authentication paths."""
jsonloader.conf.load_extensions()
config = "anchor.jsonloader.conf._config"
mock_ldc = mock.Mock()
mock_connection.return_value = mock_ldc
mock_ldc.result = {'result': 0}
mock_ldc.response = [{'attributes': {}}]
with mock.patch.dict(config, self.sample_conf):
expected = results.AuthDetails(username='user', groups=[])
self.assertEqual(auth.validate('default_ra', 'user', 'pass'),
expected)
@mock.patch('ldap3.Connection')
def test_login_good_with_groups(self, mock_connection):
"""Test all static user/pass authentication paths."""
jsonloader.conf.load_extensions()
config = "anchor.jsonloader.conf._config"
mock_ldc = mock.Mock()
mock_connection.return_value = mock_ldc
mock_ldc.result = {'result': 0}
mock_ldc.response = [{'attributes': {'memberOf': [
u'CN=some_group,OU=Groups,DC=example,DC=com',
u'CN=other_group,OU=Groups,DC=example,DC=com']}}]
with mock.patch.dict(config, self.sample_conf):
expected = results.AuthDetails(
username='user',
groups=[u'some_group', u'other_group'])
self.assertEqual(auth.validate('default_ra', 'user', 'pass'),
expected)
@mock.patch('ldap3.Connection')
def test_login_search_fail(self, mock_connection):
"""Test all static user/pass authentication paths."""
jsonloader.conf.load_extensions()
config = "anchor.jsonloader.conf._config"
mock_ldc = mock.Mock()
mock_connection.return_value = mock_ldc
mock_ldc.result = {'result': 1}
with mock.patch.dict(config, self.sample_conf):
with self.assertRaises(http_status.HTTPUnauthorized):
auth.validate('default_ra', 'user', 'pass')
@mock.patch('ldap3.Connection')
def test_login_bind_fail(self, mock_connection):
"""Test all static user/pass authentication paths."""
jsonloader.conf.load_extensions()
config = "anchor.jsonloader.conf._config"
mock_connection.side_effect = ldap3_exc.LDAPBindError()
with mock.patch.dict(config, self.sample_conf):
with self.assertRaises(http_status.HTTPUnauthorized):
auth.validate('default_ra', 'user', 'pass')
@mock.patch('ldap3.Connection')
def test_login_connection_fail(self, mock_connection):
"""Test all static user/pass authentication paths."""
jsonloader.conf.load_extensions()
config = "anchor.jsonloader.conf._config"
mock_connection.side_effect = ldap3_exc.LDAPSocketOpenError()
with mock.patch.dict(config, self.sample_conf):
with self.assertRaises(http_status.HTTPUnauthorized):
auth.validate('default_ra', 'user', 'pass')

View File

@ -1,70 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2015 Nebula Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest
import mock
from webob import exc as http_status
from anchor import auth
from anchor.auth import results
from anchor import jsonloader
import tests
class AuthStaticTests(tests.DefaultConfigMixin, unittest.TestCase):
def setUp(self):
super(AuthStaticTests, self).setUp()
def tearDown(self):
pass
def test_validate_static(self):
"""Test all static user/pass authentication paths."""
jsonloader.conf.load_extensions()
config = "anchor.jsonloader.conf._config"
self.sample_conf_auth['default_auth'] = {
"backend": "static",
"user": "myusername",
"secret": "simplepassword"
}
data = self.sample_conf
with mock.patch.dict(config, data):
valid_user = self.sample_conf_auth['default_auth']['user']
valid_pass = self.sample_conf_auth['default_auth']['secret']
expected = results.AuthDetails(username=valid_user, groups=[])
self.assertEqual(auth.validate('default_ra', valid_user,
valid_pass), expected)
with self.assertRaises(http_status.HTTPUnauthorized):
auth.validate('default_ra', valid_user, 'badpass')
with self.assertRaises(http_status.HTTPUnauthorized):
auth.validate('default_ra', 'baduser', valid_pass)
with self.assertRaises(http_status.HTTPUnauthorized):
auth.validate('default_ra', 'baduser', 'badpass')
def test_validate_static_malformed1(self):
"""Test static user/pass authentication with malformed config."""
jsonloader.conf.load_extensions()
config = "anchor.jsonloader.conf._config"
self.sample_conf_auth['default_auth'] = {'backend': 'static'}
data = self.sample_conf
with mock.patch.dict(config, data):
with self.assertRaises(http_status.HTTPUnauthorized):
auth.validate('default_ra', 'baduser', 'badpass')

View File

@ -1,256 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import os
import stat
import unittest
import mock
from anchor import app
from anchor import errors
from anchor import jsonloader
from anchor import util
import tests
class TestApp(tests.DefaultConfigMixin, unittest.TestCase):
def setUp(self):
self.expected_key_permissions = (stat.S_IRUSR | stat.S_IFREG)
jsonloader.conf.load_extensions()
super(TestApp, self).setUp()
def tearDown(self):
jsonloader.conf._config = {}
super(TestApp, self).tearDown()
def test_self_test(self):
self.assertTrue(True)
@mock.patch('anchor.util.check_file_exists')
@mock.patch('anchor.util.check_file_permissions')
def test_config_check_domains_good(self, a, b):
self.sample_conf_ra['default_ra']['validators'] = {
"common_name": {
"allowed_domains": [".example.com"]
}
}
config = json.dumps(self.sample_conf)
jsonloader.conf.load_str_data(config)
config = {'return_value.st_mode': (stat.S_IRUSR | stat.S_IFREG)}
with mock.patch("os.stat", **config):
self.assertEqual(app.validate_config(jsonloader.conf), None)
@mock.patch('anchor.util.check_file_exists')
@mock.patch('anchor.util.check_file_permissions')
def test_config_check_domains_bad(self, a, b):
self.sample_conf_ra['default_ra']['validators'] = {
"common_name": {
"allowed_domains": ["error.example.com"]
}
}
config = json.dumps(self.sample_conf)
jsonloader.conf.load_str_data(config)
config = {'return_value.st_mode': (stat.S_IRUSR | stat.S_IFREG)}
with mock.patch("os.stat", **config):
self.assertRaises(
errors.ConfigValidationException,
app.validate_config,
jsonloader.conf
)
def test_check_file_permissions_good(self):
config = {'return_value.st_mode': (stat.S_IRUSR | stat.S_IFREG)}
with mock.patch("os.stat", **config):
util.check_file_permissions("/mock/path")
def test_check_file_permissions_bad(self):
config = {'return_value.st_mode': (stat.S_IWOTH | stat.S_IFREG)}
with mock.patch("os.stat", **config):
self.assertRaises(errors.ConfigValidationException,
util.check_file_permissions, "/mock/path")
def test_validate_old_config(self):
config = json.dumps({
"ca": {},
"auth": {},
"validators": {},
})
jsonloader.conf.load_str_data(config)
self.assertRaisesRegexp(errors.ConfigValidationException,
"old version of Anchor",
app.validate_config, jsonloader.conf)
@mock.patch('anchor.util.check_file_permissions')
def test_validate_config_no_registration_authorities(self,
mock_check_perm):
del self.sample_conf['registration_authority']
config = json.dumps(self.sample_conf)
jsonloader.conf.load_str_data(config)
self.assertRaisesRegexp(errors.ConfigValidationException,
"No registration authorities present",
app.validate_config, jsonloader.conf)
@mock.patch('anchor.util.check_file_permissions')
def test_validate_config_no_auth(self, mock_check_perm):
del self.sample_conf['authentication']
config = json.dumps(self.sample_conf)
jsonloader.conf.load_str_data(config)
self.assertRaisesRegexp(errors.ConfigValidationException,
"No authentication methods present",
app.validate_config, jsonloader.conf)
@mock.patch('anchor.util.check_file_permissions')
def test_validate_config_no_auth_backend(self, mock_check_perm):
del self.sample_conf_auth['default_auth']['backend']
config = json.dumps(self.sample_conf)
jsonloader.conf.load_str_data(config)
self.assertRaisesRegexp(errors.ConfigValidationException,
"Authentication method .* doesn't define "
"backend",
app.validate_config, jsonloader.conf)
@mock.patch('anchor.util.check_file_permissions')
def test_validate_config_no_ra_auth(self, mock_check_perm):
del self.sample_conf_ra['default_ra']['authentication']
config = json.dumps(self.sample_conf)
jsonloader.conf.load_str_data(config)
self.assertRaisesRegexp(errors.ConfigValidationException,
"No authentication .* for .* default_ra",
app.validate_config, jsonloader.conf)
def test_validate_config_no_ca(self):
del self.sample_conf['signing_ca']
config = json.dumps(self.sample_conf)
jsonloader.conf.load_str_data(config)
self.assertRaisesRegexp(errors.ConfigValidationException,
"No signing CA configurations present",
app.validate_config, jsonloader.conf)
@mock.patch('anchor.util.check_file_permissions')
def test_validate_config_no_ra_ca(self, mock_check_perm):
del self.sample_conf_ra['default_ra']['signing_ca']
config = json.dumps(self.sample_conf)
jsonloader.conf.load_str_data(config)
self.assertRaisesRegexp(errors.ConfigValidationException,
"No signing CA .* for .* default_ra",
app.validate_config, jsonloader.conf)
@mock.patch('anchor.util.check_file_permissions')
def test_validate_config_ca_config_reqs(self, mock_check_perm):
ca_config_requirements = ["cert_path", "key_path", "output_path",
"signing_hash", "valid_hours"]
config = json.dumps(self.sample_conf)
jsonloader.conf.load_str_data(config)
# Iterate through the ca_config_requirements, replace each one in turn
# with 'missing_req', perform validation. Each should raise in turn
for req in ca_config_requirements:
jsonloader.conf.load_str_data(config.replace(req, "missing_req"))
self.assertRaisesRegexp(errors.ConfigValidationException,
"CA config missing: %s" % req,
app.validate_config, jsonloader.conf)
@mock.patch('os.path.isfile')
def test_validate_config_no_ca_cert_file(self, isfile):
config = json.dumps(self.sample_conf)
jsonloader.conf.load_str_data(config)
isfile.return_value = False
self.assertRaisesRegexp(errors.ConfigValidationException,
"could not read file: tests/CA/root-ca.crt",
app.validate_config, jsonloader.conf)
@mock.patch('anchor.util.check_file_permissions')
@mock.patch('os.path.isfile')
@mock.patch('os.access')
@mock.patch('os.stat')
def test_validate_config_no_validators(self, stat, access, isfile,
mock_check_perm):
self.sample_conf_ra['default_ra']['validators'] = {}
config = json.dumps(self.sample_conf)
jsonloader.conf.load_str_data(config)
isfile.return_value = True
access.return_value = True
stat.return_value.st_mode = self.expected_key_permissions
self.assertRaisesRegexp(errors.ConfigValidationException,
"No validators configured",
app.validate_config, jsonloader.conf)
@mock.patch('anchor.util.check_file_permissions')
@mock.patch('os.path.isfile')
@mock.patch('os.access')
@mock.patch('os.stat')
def test_validate_config_unknown_validator(self, stat, access, isfile,
mock_check_perm):
self.sample_conf_validators['unknown_validator'] = {}
config = json.dumps(self.sample_conf)
jsonloader.conf.load_str_data(config)
isfile.return_value = True
access.return_value = True
stat.return_value.st_mode = self.expected_key_permissions
with self.assertRaises(errors.ConfigValidationException,
msg="Unknown validator <unknown_validator> "
"found (for registration authority "
"default)"):
app.validate_config(jsonloader.conf)
@mock.patch('anchor.util.check_file_permissions')
@mock.patch('os.path.isfile')
@mock.patch('os.access')
@mock.patch('os.stat')
def test_validate_config_good(self, stat, access, isfile, mock_check_perm):
config = json.dumps(self.sample_conf)
jsonloader.conf.load_str_data(config)
isfile.return_value = True
access.return_value = True
stat.return_value.st_mode = self.expected_key_permissions
app.validate_config(jsonloader.conf)
@mock.patch('anchor.jsonloader.conf.load_file_data')
def test_config_paths_env(self, conf):
with mock.patch.dict('os.environ', {'ANCHOR_CONF': '/fake/fake'}):
app.load_config()
conf.assert_called_with('/fake/fake')
@mock.patch('anchor.jsonloader.conf.load_file_data')
def test_config_paths_local(self, conf):
ret = lambda x: True if x == 'config.json' else False
with mock.patch("os.path.isfile", ret):
app.load_config()
conf.assert_called_with('config.json')
@mock.patch('anchor.jsonloader.conf.load_file_data')
def test_config_paths_user(self, conf):
ret = (lambda x: True if x == '/fake/.config/anchor/config.json'
else False)
with mock.patch('os.path.isfile', ret):
with mock.patch.dict('os.environ', {'HOME': '/fake'}):
app.load_config()
conf.assert_called_with('/fake/.config/anchor/config.json')
@mock.patch('anchor.jsonloader.conf.load_file_data')
def test_config_paths_system(self, conf):
path = os.path.join(os.environ.get('VIRTUAL_ENV', os.sep),
'etc/anchor/config.json')
ret = lambda x: x == path
with mock.patch('os.path.isfile', ret):
app.load_config()
conf.assert_called_with(path)

View File

@ -1,109 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest
import netaddr
from anchor import fixups
from anchor.X509 import extension
from anchor.X509 import name
from anchor.X509 import signing_request
class TestEnsureAlternativeNamesPresent(unittest.TestCase):
def setUp(self):
super(TestEnsureAlternativeNamesPresent, self).setUp()
def _csr_with_cn(self, cn):
csr = signing_request.X509Csr()
subject = name.X509Name()
subject.add_name_entry(name.OID_commonName, cn)
csr.set_subject(subject)
return csr
def test_no_cn(self):
csr = signing_request.X509Csr()
subject = name.X509Name()
subject.add_name_entry(name.OID_localityName, "somewhere")
csr.set_subject(subject)
new_csr = fixups.enforce_alternative_names_present(csr=csr)
self.assertEqual(0, len(new_csr.get_extensions()))
def test_cn_only_ip(self):
csr = self._csr_with_cn("1.2.3.4")
new_csr = fixups.enforce_alternative_names_present(csr=csr)
self.assertEqual(1, len(new_csr.get_extensions()))
ext = new_csr.get_extensions(extension.X509ExtensionSubjectAltName)[0]
self.assertEqual([netaddr.IPAddress("1.2.3.4")], ext.get_ips())
def test_cn_only_dns(self):
csr = self._csr_with_cn("example.com")
new_csr = fixups.enforce_alternative_names_present(csr=csr)
self.assertEqual(1, len(new_csr.get_extensions()))
ext = new_csr.get_extensions(extension.X509ExtensionSubjectAltName)[0]
self.assertEqual(["example.com"], ext.get_dns_ids())
def test_cn_existing_ip(self):
csr = self._csr_with_cn("1.2.3.4")
san = extension.X509ExtensionSubjectAltName()
san.add_ip(netaddr.IPAddress("1.2.3.4"))
csr.add_extension(san)
new_csr = fixups.enforce_alternative_names_present(csr=csr)
self.assertEqual(1, len(new_csr.get_extensions()))
ext = new_csr.get_extensions(extension.X509ExtensionSubjectAltName)[0]
self.assertEqual([netaddr.IPAddress("1.2.3.4")], ext.get_ips())
def test_cn_existing_dns(self):
csr = self._csr_with_cn("example.com")
san = extension.X509ExtensionSubjectAltName()
san.add_dns_id("example.com")
csr.add_extension(san)
new_csr = fixups.enforce_alternative_names_present(csr=csr)
self.assertEqual(1, len(new_csr.get_extensions()))
ext = new_csr.get_extensions(extension.X509ExtensionSubjectAltName)[0]
self.assertEqual(["example.com"], ext.get_dns_ids())
def test_cn_extra_ip(self):
csr = self._csr_with_cn("1.2.3.4")
san = extension.X509ExtensionSubjectAltName()
san.add_ip(netaddr.IPAddress("2.3.4.5"))
csr.add_extension(san)
new_csr = fixups.enforce_alternative_names_present(csr=csr)
self.assertEqual(1, len(new_csr.get_extensions()))
ext = new_csr.get_extensions(extension.X509ExtensionSubjectAltName)[0]
ips = ext.get_ips()
self.assertIn(netaddr.IPAddress("1.2.3.4"), ips)
self.assertIn(netaddr.IPAddress("2.3.4.5"), ips)
def test_cn_extra_dns(self):
csr = self._csr_with_cn("example.com")
san = extension.X509ExtensionSubjectAltName()
san.add_dns_id("other.example.com")
csr.add_extension(san)
new_csr = fixups.enforce_alternative_names_present(csr=csr)
self.assertEqual(1, len(new_csr.get_extensions()))
ext = new_csr.get_extensions(extension.X509ExtensionSubjectAltName)[0]
ids = ext.get_dns_ids()
self.assertIn("example.com", ids)
self.assertIn("other.example.com", ids)

View File

@ -1,84 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest
import mock
import webob
from anchor import certificate_ops
from anchor import jsonloader
from anchor.X509 import signing_request
import tests
class TestFixupFunctionality(tests.DefaultConfigMixin,
tests.DefaultRequestMixin,
unittest.TestCase):
def setUp(self):
super(TestFixupFunctionality, self).setUp()
jsonloader.conf.load_extensions()
self.csr = signing_request.X509Csr.from_buffer(
TestFixupFunctionality.csr_sample_bytes)
def test_with_noop(self):
"""Ensure single fixup is processed."""
self.sample_conf_ra['default_ra']['fixups'] = {'noop': {}}
data = self.sample_conf
config = "anchor.jsonloader.conf._config"
mock_noop = mock.MagicMock()
mock_noop.name = "noop"
mock_noop.plugin.return_value = self.csr
jsonloader.conf._fixups = jsonloader.conf._fixups.make_test_instance(
[mock_noop], 'anchor.fixups')
with mock.patch.dict(config, data):
certificate_ops.fixup_csr('default_ra', self.csr, None)
mock_noop.plugin.assert_called_with(
csr=self.csr, conf=self.sample_conf_ra['default_ra'], request=None)
def test_with_no_fixups(self):
"""Ensure no fixups is ok."""
self.sample_conf_ra['default_ra']['fixups'] = {}
data = self.sample_conf
config = "anchor.jsonloader.conf._config"
with mock.patch.dict(config, data):
res = certificate_ops.fixup_csr('default_ra', self.csr, None)
self.assertIs(res, self.csr)
def test_with_broken_fixup(self):
"""Ensure broken fixups stop processing."""
self.sample_conf_ra['default_ra']['fixups'] = {'broken': {}}
data = self.sample_conf
config = "anchor.jsonloader.conf._config"
mock_noop = mock.MagicMock()
mock_noop.name = "broken"
mock_noop.plugin.side_effects = Exception("BOOM")
jsonloader.conf._fixups = jsonloader.conf._fixups.make_test_instance(
[mock_noop], 'anchor.fixups')
with mock.patch.dict(config, data):
with self.assertRaises(webob.exc.WSGIHTTPException):
certificate_ops.fixup_csr('default_ra', self.csr, None)

View File

@ -1,148 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2015 Nebula Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest
import mock
from webob import exc as http_status
from anchor import certificate_ops
from anchor import jsonloader
from anchor.X509 import name as x509_name
import tests
class CertificateOpsTests(tests.DefaultConfigMixin, tests.DefaultRequestMixin,
unittest.TestCase):
def setUp(self):
jsonloader.conf.load_extensions()
super(CertificateOpsTests, self).setUp()
def tearDown(self):
pass
def test_parse_csr_success1(self):
"""Test basic success path for parse_csr."""
result = certificate_ops.parse_csr(self.csr_sample, 'pem')
subject = result.get_subject()
actual_cn = subject.get_entries_by_oid(
x509_name.OID_commonName)[0].get_value()
self.assertEqual(actual_cn, self.csr_sample_cn)
def test_parse_csr_success2(self):
"""Test basic success path for parse_csr."""
result = certificate_ops.parse_csr(self.csr_sample, 'PEM')
subject = result.get_subject()
actual_cn = subject.get_entries_by_oid(
x509_name.OID_commonName)[0].get_value()
self.assertEqual(actual_cn, self.csr_sample_cn)
def test_parse_csr_fail1(self):
"""Test invalid CSR format (wrong value) for parse_csr."""
with self.assertRaises(http_status.HTTPClientError):
certificate_ops.parse_csr(self.csr_sample, 'blah')
def test_parse_csr_fail2(self):
"""Test invalid CSR format (wrong type) for parse_csr."""
with self.assertRaises(http_status.HTTPClientError):
certificate_ops.parse_csr(self.csr_sample, True)
def test_parse_csr_fail3(self):
"""Test invalid CSR (None) format for parse_csr."""
with self.assertRaises(http_status.HTTPClientError):
certificate_ops.parse_csr(None, 'pem')
def test_parse_csr_fail4(self):
"""Test invalid CSR (wrong value) format for parse_csr."""
with self.assertRaises(http_status.HTTPClientError):
certificate_ops.parse_csr('invalid csr input', 'pem')
def test_validate_csr_success(self):
"""Test basic success path for validate_csr."""
csr_obj = certificate_ops.parse_csr(self.csr_sample, 'pem')
config = "anchor.jsonloader.conf._config"
self.sample_conf_ra['default_ra']['validators'] = {'extensions': {
'allowed_extensions': ['basicConstraints', 'keyUsage']}}
data = self.sample_conf
with mock.patch.dict(config, data):
certificate_ops.validate_csr('default_ra', None, csr_obj, None)
def test_validate_csr_bypass(self):
"""Test empty validator set for validate_csr."""
csr_obj = certificate_ops.parse_csr(self.csr_sample, 'pem')
config = "anchor.jsonloader.conf._config"
self.sample_conf_ra['default_ra']['validators'] = {}
data = self.sample_conf
with mock.patch.dict(config, data):
# this should work, it allows people to bypass validation
certificate_ops.validate_csr('default_ra', None, csr_obj, None)
def test_validate_csr_fail(self):
"""Test failure path for validate_csr."""
csr_obj = certificate_ops.parse_csr(self.csr_sample, 'pem')
config = "anchor.jsonloader.conf._config"
self.sample_conf_ra['default_ra']['validators'] = {
'common_name': {
'allowed_domains': ['.testing.example.com']
}
}
data = self.sample_conf
with mock.patch.dict(config, data):
with self.assertRaises(http_status.HTTPException) as cm:
certificate_ops.validate_csr('default_ra', None, csr_obj, None)
self.assertEqual(cm.exception.code, 400)
def test_ca_cert_read_failure(self):
"""Test CA certificate read failure."""
csr_obj = certificate_ops.parse_csr(self.csr_sample, 'pem')
config = "anchor.jsonloader.conf._config"
ca_conf = self.sample_conf_ca['default_ca']
ca_conf['cert_path'] = '/xxx/not/a/valid/path'
ca_conf['key_path'] = 'tests/CA/root-ca-unwrapped.key'
data = self.sample_conf
with mock.patch.dict(config, data):
with self.assertRaises(http_status.HTTPException) as cm:
certificate_ops.dispatch_sign('default_ra', csr_obj)
self.assertEqual(cm.exception.code, 500)
def test_ca_key_read_failure(self):
"""Test CA key read failure."""
csr_obj = certificate_ops.parse_csr(self.csr_sample, 'pem')
config = "anchor.jsonloader.conf._config"
self.sample_conf_ca['default_ca']['cert_path'] = 'tests/CA/root-ca.crt'
self.sample_conf_ca['default_ca']['key_path'] = '/xxx/not/a/valid/path'
data = self.sample_conf
with mock.patch.dict(config, data):
with self.assertRaises(http_status.HTTPException) as cm:
certificate_ops.dispatch_sign('default_ra', csr_obj)
self.assertEqual(cm.exception.code, 500)
def test_ca_cert_not_configured(self):
"""Test CA cert read failure."""
config = "anchor.jsonloader.conf._config"
self.sample_conf_ca['default_ca']['cert_path'] = None
data = self.sample_conf
with mock.patch.dict(config, data):
with self.assertRaises(http_status.HTTPException) as cm:
certificate_ops.get_ca('default_ra')
self.assertEqual(cm.exception.code, 404)

View File

@ -1,98 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from anchor import jsonloader
import json
import logging
import sys
import unittest
import mock
import tests
logger = logging.getLogger(__name__)
# find the class representing an open file; it depends on the python version
# it's used later for mocking
if sys.version_info[0] < 3:
file_class = file # noqa
else:
import _io
file_class = _io.TextIOWrapper
class TestConfig(tests.DefaultConfigMixin, unittest.TestCase):
def test_wrong_key(self):
"""Wrong config key should raise the right error."""
jsonloader.conf = jsonloader.AnchorConf(logger)
with self.assertRaises(AttributeError):
jsonloader.conf.abcdef
def test_load_file(self):
"""Test loading of a correct configuration."""
jsonloader.conf = jsonloader.AnchorConf(logger)
open_name = 'anchor.jsonloader.open'
with mock.patch(open_name, create=True) as mock_open:
mock_open.return_value = mock.MagicMock(spec=file_class)
m_file = mock_open.return_value.__enter__.return_value
m_file.read.return_value = json.dumps(self.sample_conf)
jsonloader.conf.load_file_data('/tmp/impossible_path')
self.assertEqual(
(jsonloader.conf.registration_authority['default_ra']
['authentication']),
'default_auth')
self.assertEqual(
jsonloader.conf.signing_ca['default_ca']['valid_hours'],
24)
def test_load_file_cant_open(self):
"""Test failures when opening files."""
jsonloader.conf = jsonloader.AnchorConf(logger)
open_name = 'anchor.jsonloader.open'
with mock.patch(open_name, create=True) as mock_open:
mock_open.return_value = mock.MagicMock(spec=file_class)
mock_open.side_effect = IOError("can't open file")
with self.assertRaises(IOError):
jsonloader.conf.load_file_data('/tmp/impossible_path')
def test_load_file_cant_parse(self):
"""Test failues when parsing json format."""
jsonloader.conf = jsonloader.AnchorConf(logger)
open_name = 'anchor.jsonloader.open'
with mock.patch(open_name, create=True) as mock_open:
mock_open.return_value = mock.MagicMock(spec=file_class)
m_file = mock_open.return_value.__enter__.return_value
m_file.read.return_value = "{{{{ bad json"
with self.assertRaises(ValueError):
jsonloader.conf.load_file_data('/tmp/impossible_path')
def test_registration_authority_names(self):
"""Instances should be listed once config is loaded."""
jsonloader.conf = jsonloader.AnchorConf(logger)
jsonloader.conf.load_str_data(json.dumps(self.sample_conf))
self.assertEqual(list(jsonloader.registration_authority_names()),
['default_ra'])

View File

@ -1,149 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
import json
import os
import stat
import tempfile
import unittest
import mock
import pecan
from pecan import testing as pecan_testing
import stevedore
from anchor import config
from anchor import jsonloader
from anchor.X509 import certificate as X509_cert
import tests
class TestFunctional(tests.DefaultConfigMixin, tests.DefaultRequestMixin,
unittest.TestCase):
def setUp(self):
super(TestFunctional, self).setUp()
# Load config from json test config
jsonloader.conf.load_str_data(json.dumps(self.sample_conf))
jsonloader.conf.load_extensions()
self.conf = jsonloader.conf._config
ca_conf = self.conf["signing_ca"]["default_ca"]
ca_conf["output_path"] = tempfile.mkdtemp()
# Set CA file permissions
os.chmod(ca_conf["cert_path"], stat.S_IRUSR | stat.S_IFREG)
os.chmod(ca_conf["key_path"], stat.S_IRUSR | stat.S_IFREG)
app_conf = {"app": copy.deepcopy(config.app),
"logging": copy.deepcopy(config.logging)}
self.app = pecan_testing.load_test_app(app_conf)
def tearDown(self):
pecan.set_config({}, overwrite=True)
self.app.reset()
def test_check_unauthorised(self):
resp = self.app.post('/v1/sign/default_ra', expect_errors=True)
self.assertEqual(401, resp.status_int)
def test_robots(self):
resp = self.app.get('/robots.txt')
self.assertEqual(200, resp.status_int)
self.assertEqual("User-agent: *\nDisallow: /\n", resp.text)
def test_check_missing_csr(self):
data = {'user': 'myusername',
'secret': 'simplepassword',
'encoding': 'pem'}
resp = self.app.post('/v1/sign/default_ra', data, expect_errors=True)
self.assertEqual(400, resp.status_int)
def test_check_unknown_instance(self):
data = {'user': 'myusername',
'secret': 'simplepassword',
'encoding': 'pem',
'csr': self.csr_sample}
resp = self.app.post('/v1/sign/unknown', data, expect_errors=True)
self.assertEqual(404, resp.status_int)
def test_check_bad_csr(self):
data = {'user': 'myusername',
'secret': 'simplepassword',
'encoding': 'unknown',
'csr': self.csr_sample}
resp = self.app.post('/v1/sign/default_ra', data, expect_errors=True)
self.assertEqual(400, resp.status_int)
def test_check_good_csr(self):
data = {'user': 'myusername',
'secret': 'simplepassword',
'encoding': 'pem',
'csr': self.csr_sample}
resp = self.app.post('/v1/sign/default_ra', data, expect_errors=False)
self.assertEqual(200, resp.status_int)
cert = X509_cert.X509Certificate.from_buffer(resp.text)
# make sure the cert is what we asked for
self.assertEqual(("/C=UK/ST=Narnia/L=Funkytown/O=Anchor Testing"
"/OU=testing/CN=server1.example.com"
"/emailAddress=test@example.com"),
str(cert.get_subject()))
# make sure the cert was issued by anchor
self.assertEqual("/C=AU/ST=Some-State/O=Herp Derp plc/OU"
"=herp.derp.plc/CN=herp.derp.plc",
str(cert.get_issuer()))
def test_check_broken_validator(self):
data = {'user': 'myusername',
'secret': 'simplepassword',
'encoding': 'pem',
'csr': self.csr_sample}
derp = mock.MagicMock()
derp.side_effect = Exception("BOOM")
derp_ext = stevedore.extension.Extension("broken_validator", None,
derp, None)
manager = jsonloader.conf._validators.make_test_instance([derp_ext])
jsonloader.conf._validators = manager
ra = jsonloader.conf.registration_authority['default_ra']
ra['validators'] = {"broken_validator": {}}
resp = self.app.post('/v1/sign/default_ra', data, expect_errors=True)
self.assertEqual(500, resp.status_int)
self.assertTrue(("Internal Validation Error") in str(resp))
self.assertTrue(derp.called)
def test_get_ca(self):
data = {'encoding': 'pem'}
resp = self.app.get('/v1/ca/default_ra', data, expect_errors=False)
self.assertEqual(200, resp.status_int)
cert = X509_cert.X509Certificate.from_buffer(resp.text)
# make sure the cert is what we asked for
self.assertEqual("/C=AU/ST=Some-State/O=Herp Derp plc/OU"
"=herp.derp.plc/CN=herp.derp.plc",
str(cert.get_subject()))

View File

@ -1,265 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import textwrap
import unittest
import mock
from pyasn1.type import univ as asn1_univ
from anchor import errors
from anchor import signers
from anchor.signers import cryptography_io
from anchor.signers import pkcs11
from anchor import util
from anchor.X509 import certificate
from anchor.X509 import extension
from anchor.X509 import signing_request
from anchor.X509 import utils
import tests
class UnknownExtension(extension.X509Extension):
_oid = asn1_univ.ObjectIdentifier("1.2.3.4")
spec = asn1_univ.Null
class SigningBackendExtensions(tests.DefaultConfigMixin,
tests.DefaultRequestMixin, unittest.TestCase):
def test_copy_good_extensions(self):
csr = signing_request.X509Csr.from_buffer(self.csr_sample_bytes)
ext = extension.X509ExtensionSubjectAltName()
ext.add_dns_id("example.com")
csr.add_extension(ext)
pem = signers.sign_generic(csr, self.sample_conf_ca['default_ca'],
'RSA', lambda x: b"")
cert = certificate.X509Certificate.from_buffer(pem)
self.assertEqual(1, len(cert.get_extensions(
extension.X509ExtensionSubjectAltName)))
def test_ignore_unknown_extensions(self):
csr = signing_request.X509Csr.from_buffer(self.csr_sample_bytes)
ext = UnknownExtension()
csr.add_extension(ext)
pem = signers.sign_generic(csr, self.sample_conf_ca['default_ca'],
'RSA', lambda x: b"")
cert = certificate.X509Certificate.from_buffer(pem)
self.assertEqual(0, len(cert.get_extensions(UnknownExtension)))
def test_fail_critical_unknown_extensions(self):
csr = signing_request.X509Csr.from_buffer(self.csr_sample_bytes)
ext = UnknownExtension()
ext.set_critical(True)
csr.add_extension(ext)
with self.assertRaises(signers.SigningError):
signers.sign_generic(csr, self.sample_conf_ca['default_ca'],
'RSA', lambda x: b"")
class TestCryptographyBackend(tests.DefaultConfigMixin,
tests.DefaultRequestMixin, unittest.TestCase):
key_rsa_data = textwrap.dedent("""
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCeeqg1Qeccv8hqj1BP9KEJX5QsFCxR62M8plPb5t4sLo8UYfZd
6kFLcOP8xzwwvx/eFY6Sux52enQ197o8aMwyP77hMhZqtd8NCgLJMVlUbRhwLti0
SkHFPic0wAg+esfXa6yhd5TxC+bti7MgV/ljA80XQxHH8xOjdOoGN0DHfQIDAQAB
AoGBAJ2ozJpe+7qgGJPaCz3f0izvBwtq7kR49fqqRZbo8HHnx7OxWVVI7LhOkKEy
2/Bq0xsvOu1CdiXL4LynvIDIiQqLaeINzG48Rbk+0HadbXblt3nDkIWdYII6zHKI
W9ewX4KpHEPbrlEO9BjAlAcYsDIvFIMYpQhtQ+0R/gmZ99WJAkEAz5C2a6FIcMbE
o3aTc9ECq99zY7lxh+6aLpUdIeeHyb/QzfGDBdlbpBAkA6EcxSqp0aqH4xIQnYHa
3P5ZCShqSwJBAMN1sb76xq94xkg2cxShPFPAE6xKRFyKqLgsBYVtulOdfOtOnjh9
1SK2XQQfBRIRdG4Q/gDoCP8XQHpJcWMk+FcCQDnuJqulaOVo5GrG5mJ1nCxCAh98
G06X7lo/7dCPoRtSuMExvaK9RlFk29hTeAcjYCAPWzupyA9dtarmJg1jRT8CQCKf
gYnb8D/6+9yk0IPR/9ayCooVacCeyz48hgnZowzWs98WwQ4utAd/GED3obVOpDov
Bl9wus889i3zPoOac+cCQCZHredQcJGd4dlthbVtP2NhuPXz33JuETGR9pXtsDUZ
uX/nSq1oo9kUh/dPOz6aP5Ues1YVe3LExmExPBQfwIE=
-----END RSA PRIVATE KEY-----""").encode('ascii')
def test_sign_bad_md(self):
key = utils.get_private_key_from_pem(self.key_rsa_data)
with self.assertRaises(signers.SigningError):
cryptography_io.make_signer(key, "BAD", "RSA")
def test_sign_bad_key(self):
with self.assertRaises(signers.SigningError):
cryptography_io.make_signer("BAD", "sha256", "RSA")
class TestPKCSBackend(unittest.TestCase):
def setUp(self):
self.good_conf = {
"cert_path": "tests/CA/root-ca.crt",
"output_path": "/somepath",
"signing_hash": "sha256",
"valid_hours": 24,
"slot": 5,
"pin": "somepin",
"key_id": "aabbccddeeff",
"pkcs11_path": "/somepath/library.so",
}
def test_conf_checks_package(self):
with mock.patch.object(util, 'check_file_exists', return_value=True):
with mock.patch.object(pkcs11, 'import_pkcs',
side_effect=ImportError()):
with self.assertRaises(errors.ConfigValidationException):
pkcs11.conf_validator("name", self.good_conf)
def test_conf_checks_fields(self):
for key in self.good_conf:
conf = self.good_conf.copy()
del conf[key]
with self.assertRaises(errors.ConfigValidationException):
pkcs11.conf_validator("name", conf)
def test_conf_checks_file_permissions(self):
with mock.patch.object(util, 'check_file_exists', return_value=False):
with self.assertRaises(errors.ConfigValidationException):
pkcs11.conf_validator("name", self.good_conf)
def test_conf_checks_library_loading(self):
class MockExc(Exception):
pass
lib = mock.Mock()
lib.load.side_effect = MockExc()
mod = mock.Mock()
mod.PyKCS11Error = MockExc
mod.PyKCS11Lib.return_value = lib
with mock.patch.object(util, 'check_file_exists', return_value=True):
with mock.patch.object(pkcs11, 'import_pkcs', return_value=mod):
with self.assertRaises(errors.ConfigValidationException):
pkcs11.conf_validator("name", self.good_conf)
def test_conf_checks_valid_slot(self):
class MockExc(Exception):
pass
lib = mock.Mock()
lib.getSlotList.return_value = [4, 6]
mod = mock.Mock()
mod.PyKCS11Error = MockExc
mod.PyKCS11Lib.return_value = lib
with mock.patch.object(util, 'check_file_exists', return_value=True):
with mock.patch.object(pkcs11, 'import_pkcs', return_value=mod):
with self.assertRaises(errors.ConfigValidationException):
pkcs11.conf_validator("name", self.good_conf)
def test_conf_checks_valid_pin(self):
class MockExc(Exception):
pass
session = mock.Mock()
session.login.side_effect = MockExc()
lib = mock.Mock()
lib.getSlotList.return_value = [self.good_conf['slot']]
lib.openSession.return_value = session
mod = mock.Mock()
mod.PyKCS11Error = MockExc
mod.PyKCS11Lib.return_value = lib
with mock.patch.object(util, 'check_file_exists', return_value=True):
with mock.patch.object(pkcs11, 'import_pkcs', return_value=mod):
with self.assertRaises(errors.ConfigValidationException):
pkcs11.conf_validator("name", self.good_conf)
def test_conf_allows_valid(self):
session = mock.Mock()
lib = mock.Mock()
lib.getSlotList.return_value = [self.good_conf['slot']]
lib.openSession.return_value = session
mod = mock.Mock()
mod.PyKCS11Lib.return_value = lib
with mock.patch.object(util, 'check_file_exists', return_value=True):
with mock.patch.object(pkcs11, 'import_pkcs', return_value=mod):
pkcs11.conf_validator("name", self.good_conf)
def test_make_signer_fails(self):
with mock.patch.object(pkcs11, 'make_signer',
side_effect=signers.SigningError):
with self.assertRaises(signers.SigningError):
pkcs11.sign(mock.Mock(), self.good_conf)
def test_sign_login_fails(self):
class MockExc(Exception):
pass
session = mock.Mock()
session.login.side_effect = MockExc()
lib = mock.Mock()
lib.openSession.return_value = session
mod = mock.Mock()
mod.PyKCS11Error = MockExc
mod.PyKCS11Lib.return_value = lib
with mock.patch.object(pkcs11, 'import_pkcs', return_value=mod):
with self.assertRaisesRegexp(signers.SigningError,
"pkcs11 session"):
pkcs11.sign(mock.Mock(), self.good_conf)
def test_sign_key_missing(self):
class MockExc(Exception):
pass
session = mock.Mock()
session.findObjects.return_value = []
lib = mock.Mock()
lib.openSession.return_value = session
mod = mock.Mock()
mod.PyKCS11Lib.return_value = lib
with mock.patch.object(pkcs11, 'import_pkcs', return_value=mod):
with self.assertRaisesRegexp(signers.SigningError,
"requested key"):
pkcs11.sign(mock.Mock(), self.good_conf)
def test_sign_bad_hash(self):
session = mock.Mock()
session.findObjects.return_value = [object()]
lib = mock.Mock()
lib.openSession.return_value = session
mod = mock.Mock()
mod.PyKCS11Lib.return_value = lib
self.good_conf['signing_hash'] = 'unknown'
with mock.patch.object(pkcs11, 'import_pkcs', return_value=mod):
with self.assertRaisesRegexp(signers.SigningError,
"hash is not supported"):
pkcs11.sign(mock.Mock(), self.good_conf)
def test_working_signer(self):
res = b"123"
session = mock.Mock()
session.findObjects.return_value = [object()]
session.sign.return_value = res
lib = mock.Mock()
lib.openSession.return_value = session
mod = mock.Mock()
mod.PyKCS11Lib.return_value = lib
with mock.patch.object(pkcs11, 'import_pkcs', return_value=mod):
signer = pkcs11.make_signer((1, 2, 3), self.good_conf['slot'],
self.good_conf['pin'],
self.good_conf['pkcs11_path'],
self.good_conf['signing_hash'].upper())
self.assertEqual(res, signer(b"data"))

View File

@ -1,82 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest
import netaddr
from anchor.validators import errors
from anchor.validators import utils
from anchor.X509 import name
from anchor.X509 import signing_request
import tests
class TestBaseValidators(tests.DefaultRequestMixin, unittest.TestCase):
def setUp(self):
super(TestBaseValidators, self).setUp()
self.csr = signing_request.X509Csr.from_buffer(
self.csr_sample_bytes)
def tearDown(self):
super(TestBaseValidators, self).tearDown()
def test_csr_require_cn(self):
common_name = utils.csr_require_cn(self.csr)
self.assertEqual(common_name, self.csr_sample_cn)
self.csr.set_subject(name.X509Name())
with self.assertRaises(errors.ValidationError):
utils.csr_require_cn(self.csr)
def test_check_domains(self):
test_domain = 'good.example.com'
test_allowed = ['.example.com', '.example.net']
self.assertTrue(utils.check_domains(test_domain, test_allowed))
self.assertFalse(utils.check_domains('bad.example.org',
test_allowed))
def test_check_networks(self):
good_ip = netaddr.IPAddress('10.2.3.4')
bad_ip = netaddr.IPAddress('88.2.3.4')
test_allowed = ['10/8']
self.assertTrue(utils.check_networks(good_ip, test_allowed))
self.assertFalse(utils.check_networks(bad_ip, test_allowed))
def test_check_networks_invalid(self):
with self.assertRaises(TypeError):
utils.check_networks('1.2.3.4', ['10/8'])
def test_check_networks_passthrough(self):
good_ip = netaddr.IPAddress('10.2.3.4')
self.assertTrue(utils.check_networks(good_ip, []))
def test_check_compare_name_pattern(self):
cases = [
("example.com", "example.com", False, True),
("*.example.com", "*.example.com", False, True),
("*.example.com", "%.example.com", True, True),
("*.example.com", "%.example.com", False, False),
("abc.example.com", "%.example.com", False, True),
("abc.def.example.com", "%.example.com", False, False),
("abc.def.example.com", "%.%.example.com", False, True),
("host-123.example.com", "host-%.example.com", False, True),
]
for value, pattern, wildcard, result in cases:
self.assertEqual(
result,
utils.compare_name_pattern(value, pattern, wildcard),
"checking %s against %s failed" % (value, pattern))

Some files were not shown because too many files have changed in this diff Show More