diff --git a/java/src/main/java/com/google/crypto/tink/mac/HmacKeyManager.java b/java/src/main/java/com/google/crypto/tink/mac/HmacKeyManager.java
index 12629712cdf645a1f11b6bb24e942b3f294b21ec..a7863a0bd8ca6a503d3497ddc7c94cb9cf3f65ea 100644
--- a/java/src/main/java/com/google/crypto/tink/mac/HmacKeyManager.java
+++ b/java/src/main/java/com/google/crypto/tink/mac/HmacKeyManager.java
@@ -16,7 +16,7 @@
 
 package com.google.crypto.tink.mac;
 
-import com.google.crypto.tink.KeyManagerBase;
+import com.google.crypto.tink.KeyTypeManager;
 import com.google.crypto.tink.Mac;
 import com.google.crypto.tink.proto.HashType;
 import com.google.crypto.tink.proto.HmacKey;
@@ -34,51 +34,42 @@ import javax.crypto.spec.SecretKeySpec;
 /**
  * This key manager generates new {@code HmacKey} keys and produces new instances of {@code MacJce}.
  */
-class HmacKeyManager extends KeyManagerBase<Mac, HmacKey, HmacKeyFormat> {
+class HmacKeyManager extends KeyTypeManager<HmacKey> {
   public HmacKeyManager() {
-    super(Mac.class, HmacKey.class, HmacKeyFormat.class, TYPE_URL);
+    super(
+        HmacKey.class,
+        new PrimitiveFactory<Mac, HmacKey>(Mac.class) {
+          @Override
+          public Mac getPrimitive(HmacKey key) throws GeneralSecurityException {
+            HashType hash = key.getParams().getHash();
+            byte[] keyValue = key.getKeyValue().toByteArray();
+            SecretKeySpec keySpec = new SecretKeySpec(keyValue, "HMAC");
+            int tagSize = key.getParams().getTagSize();
+            switch (hash) {
+              case SHA1:
+                return new MacJce("HMACSHA1", keySpec, tagSize);
+              case SHA256:
+                return new MacJce("HMACSHA256", keySpec, tagSize);
+              case SHA512:
+                return new MacJce("HMACSHA512", keySpec, tagSize);
+              default:
+                throw new GeneralSecurityException("unknown hash");
+            }
+          }
+        });
   }
-  /** Type url that this manager does support. */
-  public static final String TYPE_URL = "type.googleapis.com/google.crypto.tink.HmacKey";
-  /** Current version of this key manager. Keys with version equal or smaller are supported. */
-  private static final int VERSION = 0;
 
+  private static final int VERSION = 0;
+  
   /** Minimum key size in bytes. */
   private static final int MIN_KEY_SIZE_IN_BYTES = 16;
 
   /** Minimum tag size in bytes. This provides minimum 80-bit security strength. */
   private static final int MIN_TAG_SIZE_IN_BYTES = 10;
 
-  /** @param serializedKey serialized {@code HmacKey} proto */
   @Override
-  public Mac getPrimitiveFromKey(HmacKey keyProto) throws GeneralSecurityException {
-    HashType hash = keyProto.getParams().getHash();
-    byte[] keyValue = keyProto.getKeyValue().toByteArray();
-    SecretKeySpec keySpec = new SecretKeySpec(keyValue, "HMAC");
-    int tagSize = keyProto.getParams().getTagSize();
-    switch (hash) {
-      case SHA1:
-        return new MacJce("HMACSHA1", keySpec, tagSize);
-      case SHA256:
-        return new MacJce("HMACSHA256", keySpec, tagSize);
-      case SHA512:
-        return new MacJce("HMACSHA512", keySpec, tagSize);
-      default:
-        throw new GeneralSecurityException("unknown hash");
-    }
-  }
-
-  /**
-   * @param serializedKeyFormat serialized {@code HmacKeyFormat} proto
-   * @return new {@code HmacKey} proto
-   */
-  @Override
-  public HmacKey newKeyFromFormat(HmacKeyFormat format) throws GeneralSecurityException {
-    return HmacKey.newBuilder()
-        .setVersion(VERSION)
-        .setParams(format.getParams())
-        .setKeyValue(ByteString.copyFrom(Random.randBytes(format.getKeySize())))
-        .build();
+  public String getKeyType() {
+    return "type.googleapis.com/google.crypto.tink.HmacKey";
   }
 
   @Override
@@ -87,40 +78,25 @@ class HmacKeyManager extends KeyManagerBase<Mac, HmacKey, HmacKeyFormat> {
   }
 
   @Override
-  protected KeyMaterialType keyMaterialType() {
+  public KeyMaterialType keyMaterialType() {
     return KeyMaterialType.SYMMETRIC;
   }
 
   @Override
-  protected HmacKey parseKeyProto(ByteString byteString)
-      throws InvalidProtocolBufferException {
-    return HmacKey.parseFrom(byteString);
-  }
-
-  @Override
-  protected HmacKeyFormat parseKeyFormatProto(ByteString byteString)
-      throws InvalidProtocolBufferException {
-    return HmacKeyFormat.parseFrom(byteString);
-  }
-
-  @Override
-  protected void validateKey(HmacKey key) throws GeneralSecurityException {
+  public void validateKey(HmacKey key) throws GeneralSecurityException {
     Validators.validateVersion(key.getVersion(), VERSION);
     if (key.getKeyValue().size() < MIN_KEY_SIZE_IN_BYTES) {
       throw new GeneralSecurityException("key too short");
     }
-    validate(key.getParams());
+    validateParams(key.getParams());
   }
 
   @Override
-  protected void validateKeyFormat(HmacKeyFormat format) throws GeneralSecurityException {
-    if (format.getKeySize() < MIN_KEY_SIZE_IN_BYTES) {
-      throw new GeneralSecurityException("key too short");
-    }
-    validate(format.getParams());
+  public HmacKey parseKey(ByteString byteString) throws InvalidProtocolBufferException {
+    return HmacKey.parseFrom(byteString);
   }
 
-  private void validate(HmacParams params) throws GeneralSecurityException {
+  private static void validateParams(HmacParams params) throws GeneralSecurityException {
     if (params.getTagSize() < MIN_TAG_SIZE_IN_BYTES) {
       throw new GeneralSecurityException("tag size too small");
     }
@@ -144,4 +120,32 @@ class HmacKeyManager extends KeyManagerBase<Mac, HmacKey, HmacKeyFormat> {
         throw new GeneralSecurityException("unknown hash type");
     }
   }
+
+  @Override
+  public KeyFactory<HmacKeyFormat, HmacKey> keyFactory() {
+    return new KeyFactory<HmacKeyFormat, HmacKey>(HmacKeyFormat.class) {
+      @Override
+      public void validateKeyFormat(HmacKeyFormat format) throws GeneralSecurityException {
+        if (format.getKeySize() < MIN_KEY_SIZE_IN_BYTES) {
+          throw new GeneralSecurityException("key too short");
+        }
+        validateParams(format.getParams());
+      }
+
+      @Override
+      public HmacKeyFormat parseKeyFormat(ByteString byteString)
+          throws InvalidProtocolBufferException {
+        return HmacKeyFormat.parseFrom(byteString);
+      }
+
+      @Override
+      public HmacKey createKey(HmacKeyFormat format) throws GeneralSecurityException {
+        return HmacKey.newBuilder()
+            .setVersion(VERSION)
+            .setParams(format.getParams())
+            .setKeyValue(ByteString.copyFrom(Random.randBytes(format.getKeySize())))
+            .build();
+      }
+    };
+  }
 }
diff --git a/java/src/main/java/com/google/crypto/tink/mac/MacConfig.java b/java/src/main/java/com/google/crypto/tink/mac/MacConfig.java
index e02de636a700fd41900e2f6691528e8da8c92b4f..29fd052eb1a795ef685f254fddc42c83f45d830a 100644
--- a/java/src/main/java/com/google/crypto/tink/mac/MacConfig.java
+++ b/java/src/main/java/com/google/crypto/tink/mac/MacConfig.java
@@ -35,7 +35,7 @@ import java.security.GeneralSecurityException;
  * @since 1.0.0
  */
 public final class MacConfig {
-  public static final String HMAC_TYPE_URL = HmacKeyManager.TYPE_URL;
+  public static final String HMAC_TYPE_URL = new HmacKeyManager().getKeyType();
 
   /** @deprecated */
   @Deprecated
@@ -83,7 +83,7 @@ public final class MacConfig {
    * @since 1.2.0
    */
   public static void register() throws GeneralSecurityException {
-    Registry.registerKeyManager(new HmacKeyManager());
+    Registry.registerKeyManager(new HmacKeyManager(), true);
     Registry.registerKeyManager(new AesCmacKeyManager(), true);
     Registry.registerPrimitiveWrapper(new MacWrapper());
   }
diff --git a/java/src/main/java/com/google/crypto/tink/mac/MacKeyTemplates.java b/java/src/main/java/com/google/crypto/tink/mac/MacKeyTemplates.java
index 8e28f5a1303b220d3fdcaaaa55e655b71aafc043..7bfeceafab25e024895378623497df1f7cb5d344 100644
--- a/java/src/main/java/com/google/crypto/tink/mac/MacKeyTemplates.java
+++ b/java/src/main/java/com/google/crypto/tink/mac/MacKeyTemplates.java
@@ -132,7 +132,7 @@ public final class MacKeyTemplates {
         .build();
     return KeyTemplate.newBuilder()
         .setValue(format.toByteString())
-        .setTypeUrl(HmacKeyManager.TYPE_URL)
+        .setTypeUrl(new HmacKeyManager().getKeyType())
         .setOutputPrefixType(OutputPrefixType.TINK)
         .build();
   }
diff --git a/java/src/test/java/com/google/crypto/tink/RegistryTest.java b/java/src/test/java/com/google/crypto/tink/RegistryTest.java
index 985cb66dc12ce002ab29f44f23753f7bc74ecd4f..0c16ab7dc69ad2c9fa7ec8ee6702e7327cdeee33 100644
--- a/java/src/test/java/com/google/crypto/tink/RegistryTest.java
+++ b/java/src/test/java/com/google/crypto/tink/RegistryTest.java
@@ -126,7 +126,7 @@ public class RegistryTest {
   public void testGetKeyManager_legacy_shouldWork() throws Exception {
     testGetKeyManager_shouldWork(AeadConfig.AES_CTR_HMAC_AEAD_TYPE_URL, "AesCtrHmacAeadKeyManager");
     testGetKeyManager_shouldWork(AeadConfig.AES_EAX_TYPE_URL, "AesEaxKeyManager");
-    testGetKeyManager_shouldWork(MacConfig.HMAC_TYPE_URL, "HmacKeyManager");
+    testGetKeyManager_shouldWork(MacConfig.HMAC_TYPE_URL, "KeyManagerImpl");
   }
 
   @Test
@@ -139,7 +139,7 @@ public class RegistryTest {
   @Test
   public void testGetKeyManager_shouldWorkHmac() throws Exception {
     assertThat(Registry.getKeyManager(MacConfig.HMAC_TYPE_URL, Mac.class).getClass().toString())
-        .contains("HmacKeyManager");
+        .contains("KeyManagerImpl");
   }
 
   @Test
@@ -196,7 +196,7 @@ public class RegistryTest {
   @Test
   public void testGetUntypedKeyManager_shouldWorkHmac() throws Exception {
     assertThat(Registry.getUntypedKeyManager(MacConfig.HMAC_TYPE_URL).getClass().toString())
-        .contains("HmacKeyManager");
+        .contains("KeyManagerImpl");
   }
 
   @Test
diff --git a/java/src/test/java/com/google/crypto/tink/mac/HmacKeyManagerTest.java b/java/src/test/java/com/google/crypto/tink/mac/HmacKeyManagerTest.java
index 69c78871b13b5b98e1b14a76598c6267366d8423..fb1d28205c9c92ed9ec3384d0d69e1d0cc735459 100644
--- a/java/src/test/java/com/google/crypto/tink/mac/HmacKeyManagerTest.java
+++ b/java/src/test/java/com/google/crypto/tink/mac/HmacKeyManagerTest.java
@@ -19,6 +19,9 @@ package com.google.crypto.tink.mac;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
+import com.google.crypto.tink.KeyManager;
+import com.google.crypto.tink.KeyManagerImpl;
+import com.google.crypto.tink.Mac;
 import com.google.crypto.tink.TestUtil;
 import com.google.crypto.tink.proto.HashType;
 import com.google.crypto.tink.proto.HmacKey;
@@ -38,16 +41,17 @@ import org.junit.runners.JUnit4;
 public class HmacKeyManagerTest {
   @Test
   public void testNewKeyMultipleTimes() throws Exception {
-    HmacKeyManager keyManager = new HmacKeyManager();
+    KeyManager<Mac> keyManager = new KeyManagerImpl<>(new HmacKeyManager(), Mac.class);
     HmacKeyFormat hmacKeyFormat = HmacKeyFormat.newBuilder()
         .setParams(HmacParams.newBuilder().setHash(HashType.SHA256).setTagSize(16).build())
         .setKeySize(32)
         .build();
     ByteString serialized = ByteString.copyFrom(hmacKeyFormat.toByteArray());
-    KeyTemplate keyTemplate = KeyTemplate.newBuilder()
-        .setTypeUrl(HmacKeyManager.TYPE_URL)
-        .setValue(serialized)
-        .build();
+    KeyTemplate keyTemplate =
+        KeyTemplate.newBuilder()
+            .setTypeUrl(new HmacKeyManager().getKeyType())
+            .setValue(serialized)
+            .build();
     // Calls newKey multiple times and make sure that we get different HmacKey each time.
     Set<String> keys = new TreeSet<String>();
     int numTests = 27;
@@ -69,12 +73,13 @@ public class HmacKeyManagerTest {
 
   @Test
   public void testNewKeyCorruptedFormat() throws Exception {
-    HmacKeyManager keyManager = new HmacKeyManager();
+    KeyManager<Mac> keyManager = new KeyManagerImpl<>(new HmacKeyManager(), Mac.class);
     ByteString serialized = ByteString.copyFrom(new byte[128]);
-    KeyTemplate keyTemplate = KeyTemplate.newBuilder()
-        .setTypeUrl(HmacKeyManager.TYPE_URL)
-        .setValue(serialized)
-        .build();
+    KeyTemplate keyTemplate =
+        KeyTemplate.newBuilder()
+            .setTypeUrl(new HmacKeyManager().getKeyType())
+            .setValue(serialized)
+            .build();
     try {
       keyManager.newKey(serialized);
       fail("Corrupted format, should have thrown exception");
diff --git a/java/src/test/java/com/google/crypto/tink/mac/MacKeyTemplatesTest.java b/java/src/test/java/com/google/crypto/tink/mac/MacKeyTemplatesTest.java
index 97c1d2647fb43fe94aaa8059dbe61c93e168cb38..be6976391b75c5cea8a99b539a32c99df0990804 100644
--- a/java/src/test/java/com/google/crypto/tink/mac/MacKeyTemplatesTest.java
+++ b/java/src/test/java/com/google/crypto/tink/mac/MacKeyTemplatesTest.java
@@ -32,7 +32,7 @@ public class MacKeyTemplatesTest {
   @Test
   public void testHMAC_SHA256_128BITTAG() throws Exception {
     KeyTemplate template = MacKeyTemplates.HMAC_SHA256_128BITTAG;
-    assertEquals(HmacKeyManager.TYPE_URL, template.getTypeUrl());
+    assertEquals(new HmacKeyManager().getKeyType(), template.getTypeUrl());
     assertEquals(OutputPrefixType.TINK, template.getOutputPrefixType());
     HmacKeyFormat format = HmacKeyFormat.parseFrom(template.getValue());
 
@@ -44,7 +44,7 @@ public class MacKeyTemplatesTest {
   @Test
   public void testHMAC_SHA256_256BITTAG() throws Exception {
     KeyTemplate template = MacKeyTemplates.HMAC_SHA256_256BITTAG;
-    assertEquals(HmacKeyManager.TYPE_URL, template.getTypeUrl());
+    assertEquals(new HmacKeyManager().getKeyType(), template.getTypeUrl());
     assertEquals(OutputPrefixType.TINK, template.getOutputPrefixType());
     HmacKeyFormat format = HmacKeyFormat.parseFrom(template.getValue());
 
@@ -56,7 +56,7 @@ public class MacKeyTemplatesTest {
   @Test
   public void testHMAC_SHA512_256BITTAG() throws Exception {
     KeyTemplate template = MacKeyTemplates.HMAC_SHA512_256BITTAG;
-    assertEquals(HmacKeyManager.TYPE_URL, template.getTypeUrl());
+    assertEquals(new HmacKeyManager().getKeyType(), template.getTypeUrl());
     assertEquals(OutputPrefixType.TINK, template.getOutputPrefixType());
     HmacKeyFormat format = HmacKeyFormat.parseFrom(template.getValue());
 
@@ -68,7 +68,7 @@ public class MacKeyTemplatesTest {
   @Test
   public void testHMAC_SHA512_512BITTAG() throws Exception {
     KeyTemplate template = MacKeyTemplates.HMAC_SHA512_512BITTAG;
-    assertEquals(HmacKeyManager.TYPE_URL, template.getTypeUrl());
+    assertEquals(new HmacKeyManager().getKeyType(), template.getTypeUrl());
     assertEquals(OutputPrefixType.TINK, template.getOutputPrefixType());
     HmacKeyFormat format = HmacKeyFormat.parseFrom(template.getValue());
 
@@ -85,7 +85,7 @@ public class MacKeyTemplatesTest {
     int tagSize = 24;
     HashType hashType = HashType.SHA512;
     KeyTemplate template = MacKeyTemplates.createHmacKeyTemplate(keySize, tagSize, hashType);
-    assertEquals(HmacKeyManager.TYPE_URL, template.getTypeUrl());
+    assertEquals(new HmacKeyManager().getKeyType(), template.getTypeUrl());
     assertEquals(OutputPrefixType.TINK, template.getOutputPrefixType());
 
     HmacKeyFormat format = HmacKeyFormat.parseFrom(template.getValue());