diff options
author | Legrandin <gooksankoo@hoiptorrow.mailexpire.com> | 2009-12-27 17:26:59 +0100 |
---|---|---|
committer | Dwayne C. Litzenberger <dlitz@dlitz.net> | 2010-08-02 16:34:13 -0400 |
commit | 0f45878cef67965dc39144b5536368d934d61589 (patch) | |
tree | 021e7237ce518c4f10ade298a8950066b1e2a46e /lib/Crypto/Util | |
parent | 6289ec502253524a44007e469341e26368c6b276 (diff) | |
download | pycrypto-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__.py | 3 | ||||
-rw-r--r-- | lib/Crypto/Util/asn1.py | 162 |
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 + |