Index: django/db/backends/util.py
===================================================================
--- django/db/backends/util.py	(revision 8169)
+++ django/db/backends/util.py	(working copy)
@@ -1,6 +1,6 @@
 import datetime
-import md5
 from time import time
+from django.utils.hashcompat import md5_constructor
 
 try:
     import decimal
@@ -114,7 +114,7 @@
     if length is None or len(name) <= length:
         return name
 
-    hash = md5.md5(name).hexdigest()[:4]
+    hash = md5_constructor(name).hexdigest()[:4]
 
     return '%s%s' % (name[:length-4], hash)
 
Index: django/core/cache/backends/filebased.py
===================================================================
--- django/core/cache/backends/filebased.py	(revision 8169)
+++ django/core/cache/backends/filebased.py	(working copy)
@@ -1,12 +1,12 @@
 "File-based cache backend"
 
-import md5
 import os, time
 try:
     import cPickle as pickle
 except ImportError:
     import pickle
 from django.core.cache.backends.base import BaseCache
+from django.utils.hashcompat import md5_constructor
 
 class CacheClass(BaseCache):
     def __init__(self, dir, params):
@@ -137,7 +137,7 @@
         Thus, a cache key of "foo" gets turnned into a file named
         ``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``.
         """
-        path = md5.new(key.encode('utf-8')).hexdigest()
+        path = md5_constructor(key.encode('utf-8')).hexdigest()
         path = os.path.join(path[:2], path[2:4], path[4:])
         return os.path.join(self._dir, path)
 
Index: django/contrib/formtools/wizard.py
===================================================================
--- django/contrib/formtools/wizard.py	(revision 8169)
+++ django/contrib/formtools/wizard.py	(working copy)
@@ -10,7 +10,7 @@
 from django.shortcuts import render_to_response
 from django.template.context import RequestContext
 import cPickle as pickle
-import md5
+from django.utils.hashcompat import md5_constructor
 
 class FormWizard(object):
     # Dictionary of extra template context variables.
@@ -150,7 +150,7 @@
         # Use HIGHEST_PROTOCOL because it's the most efficient. It requires
         # Python 2.3, but Django requires 2.3 anyway, so that's OK.
         pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
-        return md5.new(pickled).hexdigest()
+        return md5_constructor(pickled).hexdigest()
 
     def determine_step(self, request, *args, **kwargs):
         """
Index: django/contrib/formtools/preview.py
===================================================================
--- django/contrib/formtools/preview.py	(revision 8169)
+++ django/contrib/formtools/preview.py	(working copy)
@@ -7,7 +7,7 @@
 from django.shortcuts import render_to_response
 from django.template.context import RequestContext
 import cPickle as pickle
-import md5
+from django.utils.hashcompat import md5_constructor
 
 AUTO_ID = 'formtools_%s' # Each form here uses this as its auto_id parameter.
 
@@ -109,7 +109,7 @@
         # Use HIGHEST_PROTOCOL because it's the most efficient. It requires
         # Python 2.3, but Django requires 2.3 anyway, so that's OK.
         pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
-        return md5.new(pickled).hexdigest()
+        return md5_constructor(pickled).hexdigest()
 
     def failed_hash(self, request):
         "Returns an HttpResponse in the case of an invalid security hash."
Index: django/contrib/comments/models.py
===================================================================
--- django/contrib/comments/models.py	(revision 8169)
+++ django/contrib/comments/models.py	(working copy)
@@ -29,8 +29,8 @@
         'pa,ra') and target (something like 'lcom.eventtimes:5157'). Used to
         validate that submitted form options have not been tampered-with.
         """
-        import md5
-        return md5.new(options + photo_options + rating_options + target + settings.SECRET_KEY).hexdigest()
+        from django.utils.hashcompat import md5_constructor
+        return md5_constructor(options + photo_options + rating_options + target + settings.SECRET_KEY).hexdigest()
 
     def get_rating_options(self, rating_string):
         """
Index: django/contrib/admin/views/decorators.py
===================================================================
--- django/contrib/admin/views/decorators.py	(revision 8169)
+++ django/contrib/admin/views/decorators.py	(working copy)
@@ -1,5 +1,4 @@
 import base64
-import md5
 import cPickle as pickle
 try:
     from functools import wraps
@@ -12,6 +11,7 @@
 from django.contrib.auth import authenticate, login
 from django.shortcuts import render_to_response
 from django.utils.translation import ugettext_lazy, ugettext as _
+from django.utils.hashcompat import md5_constructor
 
 ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.")
 LOGIN_FORM_KEY = 'this_is_the_login_form'
@@ -35,13 +35,13 @@
 
 def _encode_post_data(post_data):
     pickled = pickle.dumps(post_data)
-    pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
+    pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
     return base64.encodestring(pickled + pickled_md5)
 
 def _decode_post_data(encoded_data):
     encoded_data = base64.decodestring(encoded_data)
     pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
-    if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
+    if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
         from django.core.exceptions import SuspiciousOperation
         raise SuspiciousOperation, "User may have tampered with session cookie."
     return pickle.loads(pickled)
Index: django/contrib/admin/sites.py
===================================================================
--- django/contrib/admin/sites.py	(revision 8169)
+++ django/contrib/admin/sites.py	(working copy)
@@ -12,7 +12,7 @@
 import base64
 import cPickle as pickle
 import datetime
-import md5
+from django.utils.hashcompat import md5_constructor
 import re
 
 ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.")
@@ -29,14 +29,14 @@
 def _encode_post_data(post_data):
     from django.conf import settings
     pickled = pickle.dumps(post_data)
-    pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
+    pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
     return base64.encodestring(pickled + pickled_md5)
 
 def _decode_post_data(encoded_data):
     from django.conf import settings
     encoded_data = base64.decodestring(encoded_data)
     pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
-    if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
+    if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
         from django.core.exceptions import SuspiciousOperation
         raise SuspiciousOperation, "User may have tampered with session cookie."
     return pickle.loads(pickled)
Index: django/contrib/csrf/middleware.py
===================================================================
--- django/contrib/csrf/middleware.py	(revision 8169)
+++ django/contrib/csrf/middleware.py	(working copy)
@@ -8,7 +8,7 @@
 from django.conf import settings
 from django.http import HttpResponseForbidden
 from django.utils.safestring import mark_safe
-import md5
+from django.utils.hashcompat import md5_constructor
 import re
 import itertools
 
@@ -20,7 +20,7 @@
 _HTML_TYPES = ('text/html', 'application/xhtml+xml')    
 
 def _make_token(session_id):
-    return md5.new(settings.SECRET_KEY + session_id).hexdigest()
+    return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
 
 class CsrfMiddleware(object):
     """Django middleware that adds protection against Cross Site
Index: django/contrib/auth/tokens.py
===================================================================
--- django/contrib/auth/tokens.py	(revision 8169)
+++ django/contrib/auth/tokens.py	(working copy)
@@ -50,10 +50,10 @@
         # last_login will also change), we produce a hash that will be
         # invalid as soon as it is used.
         # We limit the hash to 20 chars to keep URL short
-        import sha
-        hash = sha.new(settings.SECRET_KEY + unicode(user.id) + 
-                       user.password + unicode(user.last_login) + 
-                       unicode(timestamp)).hexdigest()[::2]
+        from django.utils.hashcompat import sha_constructor 
+        hash = sha_constructor(settings.SECRET_KEY + unicode(user.id) + 
+                               user.password + unicode(user.last_login) + 
+                               unicode(timestamp)).hexdigest()[::2]
         return "%s-%s" % (ts_b36, hash)
 
     def _num_days(self, dt):
Index: django/contrib/sessions/backends/base.py
===================================================================
--- django/contrib/sessions/backends/base.py	(revision 8169)
+++ django/contrib/sessions/backends/base.py	(working copy)
@@ -1,5 +1,4 @@
 import base64
-import md5
 import os
 import random
 import sys
@@ -12,8 +11,8 @@
 
 from django.conf import settings
 from django.core.exceptions import SuspiciousOperation
+from django.utils.hashcompat import md5_constructor
 
-
 class SessionBase(object):
     """
     Base class for all Session classes.
@@ -73,13 +72,13 @@
     def encode(self, session_dict):
         "Returns the given session dictionary pickled and encoded as a string."
         pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL)
-        pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
+        pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
         return base64.encodestring(pickled + pickled_md5)
 
     def decode(self, session_data):
         encoded_data = base64.decodestring(session_data)
         pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
-        if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
+        if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
             raise SuspiciousOperation("User tampered with session cookie.")
         try:
             return pickle.loads(pickled)
@@ -117,8 +116,8 @@
             # No getpid() in Jython, for example
             pid = 1
         while 1:
-            session_key = md5.new("%s%s%s%s" % (random.randint(0, sys.maxint - 1),
-                                  pid, time.time(), settings.SECRET_KEY)).hexdigest()
+            session_key = md5_constructor("%s%s%s%s" % (random.randint(0, sys.maxint - 1),
+                                          pid, time.time(), settings.SECRET_KEY)).hexdigest()
             if not self.exists(session_key):
                 break
         return session_key
Index: django/contrib/sessions/models.py
===================================================================
--- django/contrib/sessions/models.py	(revision 8169)
+++ django/contrib/sessions/models.py	(working copy)
@@ -1,10 +1,10 @@
 import base64
-import md5
 import cPickle as pickle
 
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
 from django.conf import settings
+from django.utils.hashcompat import md5_constructor
 
 
 class SessionManager(models.Manager):
@@ -13,7 +13,7 @@
         Returns the given session dictionary pickled and encoded as a string.
         """
         pickled = pickle.dumps(session_dict)
-        pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
+        pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
         return base64.encodestring(pickled + pickled_md5)
 
     def save(self, session_key, session_dict, expire_date):
@@ -56,7 +56,7 @@
     def get_decoded(self):
         encoded_data = base64.decodestring(self.session_data)
         pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
-        if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
+        if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
             from django.core.exceptions import SuspiciousOperation
             raise SuspiciousOperation, "User tampered with session cookie."
         try:
Index: django/utils/cache.py
===================================================================
--- django/utils/cache.py	(revision 8169)
+++ django/utils/cache.py	(working copy)
@@ -17,7 +17,6 @@
 "Accept-language" header.
 """
 
-import md5
 import re
 import time
 try:
@@ -29,6 +28,7 @@
 from django.core.cache import cache
 from django.utils.encoding import smart_str, iri_to_uri
 from django.utils.http import http_date
+from django.utils.hashcompat import md5_constructor
 
 cc_delim_re = re.compile(r'\s*,\s*')
 
@@ -104,7 +104,7 @@
     if cache_timeout < 0:
         cache_timeout = 0 # Can't have max-age negative
     if not response.has_header('ETag'):
-        response['ETag'] = '"%s"' % md5.new(response.content).hexdigest()
+        response['ETag'] = '"%s"' % md5_constructor(response.content).hexdigest()
     if not response.has_header('Last-Modified'):
         response['Last-Modified'] = http_date()
     if not response.has_header('Expires'):
@@ -138,7 +138,7 @@
 
 def _generate_cache_key(request, headerlist, key_prefix):
     """Returns a cache key from the headers given in the header list."""
-    ctx = md5.new()
+    ctx = md5_constructor()
     for header in headerlist:
         value = request.META.get(header, None)
         if value is not None:
Index: django/utils/hashcompat.py
===================================================================
--- django/utils/hashcompat.py	(revision 0)
+++ django/utils/hashcompat.py	(revision 0)
@@ -0,0 +1,10 @@
+try:
+    import hashlib
+    md5_constructor = hashlib.md5
+    sha_constructor = hashlib.sha1
+except ImportError:
+    import md5
+    md5_constructor = md5.new
+    import sha
+    sha_constructor = sha.new
+    
\ No newline at end of file
Index: django/middleware/common.py
===================================================================
--- django/middleware/common.py	(revision 8169)
+++ django/middleware/common.py	(working copy)
@@ -1,4 +1,3 @@
-import md5
 import re
 
 from django.conf import settings
@@ -6,6 +5,7 @@
 from django.core.mail import mail_managers
 from django.utils.http import urlquote
 from django.core import urlresolvers
+from django.utils.hashcompat import md5_constructor
 
 class CommonMiddleware(object):
     """
@@ -108,7 +108,7 @@
             if response.has_header('ETag'):
                 etag = response['ETag']
             else:
-                etag = '"%s"' % md5.new(response.content).hexdigest()
+                etag = '"%s"' % md5_constructor(response.content).hexdigest()
             if response.status_code >= 200 and response.status_code < 300 and request.META.get('HTTP_IF_NONE_MATCH') == etag:
                 cookies = response.cookies
                 response = http.HttpResponseNotModified()
Index: tests/regressiontests/cache/tests.py
===================================================================
--- tests/regressiontests/cache/tests.py	(revision 8169)
+++ tests/regressiontests/cache/tests.py	(working copy)
@@ -99,10 +99,10 @@
             self.assertEqual(cache.get(key), value)
 
 import os
-import md5
 import shutil
 import tempfile
 from django.core.cache.backends.filebased import CacheClass as FileCache
+from django.utils.hashcompat import md5_constructor
 
 class FileBasedCacheTests(unittest.TestCase):
     """
@@ -119,7 +119,7 @@
     def test_hashing(self):
         """Test that keys are hashed into subdirectories correctly"""
         self.cache.set("foo", "bar")
-        keyhash = md5.new("foo").hexdigest()
+        keyhash = md5_constructor("foo").hexdigest()
         keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
         self.assert_(os.path.exists(keypath))
         
@@ -128,7 +128,7 @@
         Make sure that the created subdirectories are correctly removed when empty.
         """
         self.cache.set("foo", "bar")
-        keyhash = md5.new("foo").hexdigest()
+        keyhash = md5_constructor("foo").hexdigest()
         keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
         self.assert_(os.path.exists(keypath))
 
Index: tests/regressiontests/file_uploads/tests.py
===================================================================
--- tests/regressiontests/file_uploads/tests.py	(revision 8169)
+++ tests/regressiontests/file_uploads/tests.py	(working copy)
@@ -1,6 +1,5 @@
 import os
 import errno
-import sha
 import shutil
 import unittest
 
@@ -8,6 +7,7 @@
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.test import TestCase, client
 from django.utils import simplejson
+from django.utils.hashcompat import sha_constructor
 
 from models import FileModel, UPLOAD_ROOT, UPLOAD_TO
 
@@ -45,10 +45,10 @@
 
         for key in post_data.keys():
             try:
-                post_data[key + '_hash'] = sha.new(post_data[key].read()).hexdigest()
+                post_data[key + '_hash'] = sha_constructor(post_data[key].read()).hexdigest()
                 post_data[key].seek(0)
             except AttributeError:
-                post_data[key + '_hash'] = sha.new(post_data[key]).hexdigest()
+                post_data[key + '_hash'] = sha_constructor(post_data[key]).hexdigest()
 
         response = self.client.post('/file_uploads/verify/', post_data)
 
Index: tests/regressiontests/test_client_regress/models.py
===================================================================
--- tests/regressiontests/test_client_regress/models.py	(revision 8169)
+++ tests/regressiontests/test_client_regress/models.py	(working copy)
@@ -6,7 +6,6 @@
 from django.core.urlresolvers import reverse
 from django.core.exceptions import SuspiciousOperation
 import os
-import sha
 
 class AssertContainsTests(TestCase):
     def test_contains(self):
