summaryrefslogtreecommitdiff
path: root/lib/Crypto/Util
diff options
context:
space:
mode:
authorLegrandin <gooksankoo@hoiptorrow.mailexpire.com>2009-12-27 17:26:59 +0100
committerDwayne C. Litzenberger <dlitz@dlitz.net>2010-08-02 16:34:13 -0400
commit0f45878cef67965dc39144b5536368d934d61589 (patch)
tree021e7237ce518c4f10ade298a8950066b1e2a46e /lib/Crypto/Util
parent6289ec502253524a44007e469341e26368c6b276 (diff)
downloadpycrypto-0f45878cef67965dc39144b5536368d934d61589.tar.gz
Add ability to export and import RSA keys in DER and PEM format.
Typical usage for importing an RSA key: f = file("ssl.pem") key = RSA.importKey(f.read()) f.close() key.verify(hash, signature) Typical usage for exporting an RSA public key: key = RSA.generate(512, randfunc) f = file("ssl.der","w") f.write(key.publickey.exportKey('DER')) f.close() I confirm I am eligible for submitting code to pycrypto according to http://www.dlitz.net/software/pycrypto/submission-requirements/ fetched on 27 December 2009. Committer: Legrandin <gooksankoo@hoiptorrow.mailexpire.com>
Diffstat (limited to 'lib/Crypto/Util')
-rw-r--r--lib/Crypto/Util/__init__.py3
-rw-r--r--lib/Crypto/Util/asn1.py162
2 files changed, 164 insertions, 1 deletions
diff --git a/lib/Crypto/Util/__init__.py b/lib/Crypto/Util/__init__.py
index aecd539..a3bef8a 100644
--- a/lib/Crypto/Util/__init__.py
+++ b/lib/Crypto/Util/__init__.py
@@ -27,10 +27,11 @@ Crypto.Util.number Number-theoretic functions (primality testing, etc.)
Crypto.Util.randpool Random number generation
Crypto.Util.RFC1751 Converts between 128-bit keys and human-readable
strings of words.
+Crypto.Util.asn1 Minimal support for ASN.1 DER encoding
"""
-__all__ = ['randpool', 'RFC1751', 'number', 'strxor']
+__all__ = ['randpool', 'RFC1751', 'number', 'strxor', 'asn1' ]
__revision__ = "$Id$"
diff --git a/lib/Crypto/Util/asn1.py b/lib/Crypto/Util/asn1.py
new file mode 100644
index 0000000..7e3041b
--- /dev/null
+++ b/lib/Crypto/Util/asn1.py
@@ -0,0 +1,162 @@
+# -*- coding: ascii -*-
+#
+# Util/asn1.py : Minimal support for ASN.1 DER binary encoding.
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from Crypto.Util.number import long_to_bytes, bytes_to_long
+
+__all__ = [ 'DerObject', 'DerInteger', 'DerSequence' ]
+
+class DerObject(object):
+ typeTags = { 'SEQUENCE':'\x30', 'BIT STRING':'\x03', 'INTEGER':'\x02' }
+
+ def __init__(self, ASN1Type=None):
+ self.typeTag = self.typeTags.get(ASN1Type, ASN1Type)
+ self.payload = ''
+
+ def _lengthOctets(self, payloadLen):
+ '''
+ Return an octet string that is suitable for the BER/DER
+ length element if the relevant payload is of the given
+ size (in bytes).
+ '''
+ if payloadLen>127:
+ encoding = long_to_bytes(payloadLen)
+ return chr(len(encoding)+128) + encoding
+ return chr(payloadLen)
+
+ def encode(self):
+ return self.typeTag + self._lengthOctets(len(self.payload)) + self.payload
+
+ def _decodeLen(self, idx, str):
+ '''
+ Given a string and an index to a DER LV,
+ this function returns a tuple with the length of V
+ and an index to the first byte of it.
+ '''
+ length = ord(str[idx])
+ if length<=127:
+ return (length,idx+1)
+ else:
+ payloadLength = bytes_to_long(str[idx+1:idx+1+(length & 0x7F)])
+ if payloadLength<=127:
+ raise ValueError("Not a DER length tag.")
+ return (payloadLength, idx+1+(length & 0x7F))
+
+ def decode(self, input, noLeftOvers=False):
+ try:
+ self.typeTag = input[0]
+ if (ord(self.typeTag) & 0x1F)==0x1F:
+ raise ValueError("Unsupported DER tag")
+ (length,idx) = self._decodeLen(1,input)
+ if noLeftOvers and len(input) != (idx+length):
+ raise ValueError("Not a DER structure")
+ self.payload = input[idx:idx+length]
+ except IndexError:
+ raise ValueError("Not a valid DER SEQUENCE.")
+ return idx+length
+
+class DerInteger(DerObject):
+ def __init__(self, value = 0):
+ DerObject.__init__(self, 'INTEGER')
+ self.value = value
+
+ def encode(self):
+ self.payload = long_to_bytes(self.value)
+ if ord(self.payload[0])>127:
+ self.payload = '\x00' + self.payload
+ return DerObject.encode(self)
+
+ def decode(self, input, noLeftOvers=False):
+ tlvLength = DerObject.decode(self, input,noLeftOvers)
+ if ord(self.payload[0])>127:
+ raise ValueError ("Negative INTEGER.")
+ self.value = bytes_to_long(self.payload)
+ return tlvLength
+
+class DerSequence(DerObject):
+ def __init__(self):
+ DerObject.__init__(self, 'SEQUENCE')
+ self._seq = []
+ def __delitem__(self, n):
+ del self._seq[n]
+ def __getitem__(self, n):
+ return self._seq[n]
+ def __setitem__(self, key, value):
+ self._seq[key] = value
+ def __setslice__(self,i,j,sequence):
+ self._seq[i:j] = sequence
+ def __delslice__(self,i,j):
+ del self._seq[i:j]
+ def __len__(self):
+ return len(self._seq)
+ def append(self, item):
+ return self._seq.append(item)
+
+ def hasOnlyInts(self):
+ if not self._seq: return False
+ for item in self._seq:
+ if not isinstance(item,(int, long)):
+ return False
+ return True
+
+ def encode(self):
+ '''
+ Return the DER encoding for the ASN.1 SEQUENCE containing
+ the non-negative integers and longs added to this object.
+ '''
+ self.payload = ''
+ for item in self._seq:
+ if isinstance(item,(long, int)):
+ self.payload += DerInteger(item).encode()
+ elif isinstance(item,basestring):
+ self.payload += item
+ else:
+ raise ValueError("Trying to DER encode an unknown object")
+ return DerObject.encode(self)
+
+ def decode(self, input,noLeftOvers=False):
+ '''
+ This function decodes the given string into a sequence of
+ ASN.1 objects. Yet, we only know about unsigned INTEGERs.
+ Any other type is stored as its rough TLV. In the latter
+ case, the correctectness of the TLV is not checked.
+ '''
+ self._seq = []
+ try:
+ tlvLength = DerObject.decode(self, input,noLeftOvers)
+ if self.typeTag!=self.typeTags['SEQUENCE']:
+ raise ValueError("Not a DER SEQUENCE.")
+ # Scan one TLV at once
+ idx = 0
+ while idx<len(self.payload):
+ typeTag = self.payload[idx]
+ if typeTag==self.typeTags['INTEGER']:
+ newInteger = DerInteger()
+ idx += newInteger.decode(self.payload[idx:])
+ self._seq.append(newInteger.value)
+ else:
+ itemLen,itemIdx = self._decodeLen(idx+1,self.payload)
+ self._seq.append(self.payload[idx:itemIdx+itemLen])
+ idx = itemIdx + itemLen
+ except IndexError:
+ raise ValueError("Not a valid DER SEQUENCE.")
+ return tlvLength
+