diff --git a/java/src/main/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManager.java b/java/src/main/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManager.java
index 2a948b46424fa9311f74de139f63e7abd27a09ea..c9ccf6ca8efacd914e2b97bd5f76695a6fb33229 100644
--- a/java/src/main/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManager.java
+++ b/java/src/main/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManager.java
@@ -16,7 +16,7 @@
 
 package com.google.crypto.tink.streamingaead;
 
-import com.google.crypto.tink.KeyManagerBase;
+import com.google.crypto.tink.KeyTypeManager;
 import com.google.crypto.tink.StreamingAead;
 import com.google.crypto.tink.proto.AesGcmHkdfStreamingKey;
 import com.google.crypto.tink.proto.AesGcmHkdfStreamingKeyFormat;
@@ -34,98 +34,96 @@ import java.security.GeneralSecurityException;
  * This key manager generates new {@code AesGcmHkdfStreamingKey} keys and produces new instances of
  * {@code AesGcmHkdfStreaming}.
  */
-class AesGcmHkdfStreamingKeyManager
-    extends KeyManagerBase<StreamingAead, AesGcmHkdfStreamingKey, AesGcmHkdfStreamingKeyFormat> {
+class AesGcmHkdfStreamingKeyManager extends KeyTypeManager<AesGcmHkdfStreamingKey> {
   public AesGcmHkdfStreamingKeyManager() {
     super(
-        StreamingAead.class,
         AesGcmHkdfStreamingKey.class,
-        AesGcmHkdfStreamingKeyFormat.class,
-        TYPE_URL);
+        new PrimitiveFactory<StreamingAead, AesGcmHkdfStreamingKey>(StreamingAead.class) {
+          @Override
+          public StreamingAead getPrimitive(AesGcmHkdfStreamingKey key)
+              throws GeneralSecurityException {
+            return new AesGcmHkdfStreaming(
+                key.getKeyValue().toByteArray(),
+                StreamingAeadUtil.toHmacAlgo(key.getParams().getHkdfHashType()),
+                key.getParams().getDerivedKeySize(),
+                key.getParams().getCiphertextSegmentSize(),
+                /* firstSegmentOffset= */ 0);
+          }
+        });
   }
 
-  private static final int VERSION = 0;
-
-  public static final String TYPE_URL =
-      "type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey";
-  
   private static final int NONCE_PREFIX_IN_BYTES = 7;
   private static final int TAG_SIZE_IN_BYTES = 16;
 
-  /** @param serializedKey serialized {@code AesGcmHkdfStreamingKey} proto */
-  @Override
-  public StreamingAead getPrimitiveFromKey(AesGcmHkdfStreamingKey keyProto)
-      throws GeneralSecurityException {
-    return new AesGcmHkdfStreaming(
-        keyProto.getKeyValue().toByteArray(),
-        StreamingAeadUtil.toHmacAlgo(
-            keyProto.getParams().getHkdfHashType()),
-        keyProto.getParams().getDerivedKeySize(),
-        keyProto.getParams().getCiphertextSegmentSize(),
-        /* firstSegmentOffset= */ 0);
-  }
-
-  /**
-   * @param serializedKeyFormat serialized {@code AesGcmHkdfStreamingKeyFormat} proto
-   * @return new {@code AesGcmHkdfStreamingKey} proto
-   */
   @Override
-  public AesGcmHkdfStreamingKey newKeyFromFormat(AesGcmHkdfStreamingKeyFormat format)
-      throws GeneralSecurityException {
-    return AesGcmHkdfStreamingKey.newBuilder()
-        .setKeyValue(ByteString.copyFrom(Random.randBytes(format.getKeySize())))
-        .setParams(format.getParams())
-        .setVersion(VERSION)
-        .build();
+  public String getKeyType() {
+    return "type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey";
   }
 
   @Override
   public int getVersion() {
-    return VERSION;
+    return 0;
   }
 
   @Override
-  protected KeyMaterialType keyMaterialType() {
+  public KeyMaterialType keyMaterialType() {
     return KeyMaterialType.SYMMETRIC;
   }
 
   @Override
-  protected AesGcmHkdfStreamingKey parseKeyProto(ByteString byteString)
-      throws InvalidProtocolBufferException {
-    return AesGcmHkdfStreamingKey.parseFrom(byteString);
+  public void validateKey(AesGcmHkdfStreamingKey key) throws GeneralSecurityException {
+    Validators.validateVersion(key.getVersion(), getVersion());
+    validateParams(key.getParams());
   }
 
   @Override
-  protected AesGcmHkdfStreamingKeyFormat parseKeyFormatProto(ByteString byteString)
+  public AesGcmHkdfStreamingKey parseKey(ByteString byteString)
       throws InvalidProtocolBufferException {
-    return AesGcmHkdfStreamingKeyFormat.parseFrom(byteString);
+    return AesGcmHkdfStreamingKey.parseFrom(byteString);
   }
 
   @Override
-  protected void validateKey(AesGcmHkdfStreamingKey key) throws GeneralSecurityException {
-    Validators.validateVersion(key.getVersion(), VERSION);
-    validate(key.getParams());
-  }
+  public KeyFactory<AesGcmHkdfStreamingKeyFormat, AesGcmHkdfStreamingKey> keyFactory() {
+    return new KeyFactory<AesGcmHkdfStreamingKeyFormat, AesGcmHkdfStreamingKey>(
+        AesGcmHkdfStreamingKeyFormat.class) {
+      @Override
+      public void validateKeyFormat(AesGcmHkdfStreamingKeyFormat format)
+          throws GeneralSecurityException {
+        if (format.getKeySize() < 16) {
+          throw new GeneralSecurityException("key_size must be at least 16 bytes");
+        }
+        validateParams(format.getParams());
+      }
 
-  @Override
-  protected void validateKeyFormat(AesGcmHkdfStreamingKeyFormat format)
-      throws GeneralSecurityException {
-    if (format.getKeySize() < 16) {
-      throw new GeneralSecurityException("key_size must be at least 16 bytes");
-    }
-    validate(format.getParams());
+      @Override
+      public AesGcmHkdfStreamingKeyFormat parseKeyFormat(ByteString byteString)
+          throws InvalidProtocolBufferException {
+        return AesGcmHkdfStreamingKeyFormat.parseFrom(byteString);
+      }
+
+      @Override
+      public AesGcmHkdfStreamingKey createKey(AesGcmHkdfStreamingKeyFormat format)
+          throws GeneralSecurityException {
+        return AesGcmHkdfStreamingKey.newBuilder()
+            .setKeyValue(ByteString.copyFrom(Random.randBytes(format.getKeySize())))
+            .setParams(format.getParams())
+            .setVersion(getVersion())
+            .build();
+      }
+    };
   }
 
-  private void validate(AesGcmHkdfStreamingParams params) throws GeneralSecurityException {
+  private static void validateParams(AesGcmHkdfStreamingParams params)
+      throws GeneralSecurityException {
     Validators.validateAesKeySize(params.getDerivedKeySize());
     if (params.getHkdfHashType() == HashType.UNKNOWN_HASH) {
       throw new GeneralSecurityException("unknown HKDF hash type");
     }
-    if (params.getCiphertextSegmentSize() <= params.getDerivedKeySize() + 1 + NONCE_PREFIX_IN_BYTES
-        + TAG_SIZE_IN_BYTES) {
+    if (params.getCiphertextSegmentSize()
+        < params.getDerivedKeySize() + NONCE_PREFIX_IN_BYTES + TAG_SIZE_IN_BYTES + 2) {
       throw new GeneralSecurityException(
           "ciphertext_segment_size must be at least (derived_key_size + NONCE_PREFIX_IN_BYTES + "
-          + "TAG_SIZE_IN_BYTES + 2)");
+              + "TAG_SIZE_IN_BYTES + 2)");
     }
   }
 }
diff --git a/java/src/main/java/com/google/crypto/tink/streamingaead/StreamingAeadConfig.java b/java/src/main/java/com/google/crypto/tink/streamingaead/StreamingAeadConfig.java
index c2e82bea2dde820e6af6a9e46f600287a14fbda7..666c8d8b13b25990aa7478e48744a9f382c897d7 100644
--- a/java/src/main/java/com/google/crypto/tink/streamingaead/StreamingAeadConfig.java
+++ b/java/src/main/java/com/google/crypto/tink/streamingaead/StreamingAeadConfig.java
@@ -39,7 +39,7 @@ public final class StreamingAeadConfig {
   public static final String AES_CTR_HMAC_STREAMINGAEAD_TYPE_URL =
       AesCtrHmacStreamingKeyManager.TYPE_URL;
   public static final String AES_GCM_HKDF_STREAMINGAEAD_TYPE_URL =
-      AesGcmHkdfStreamingKeyManager.TYPE_URL;
+      new AesGcmHkdfStreamingKeyManager().getKeyType();
 
   /** @deprecated */
   @Deprecated
@@ -75,7 +75,7 @@ public final class StreamingAeadConfig {
    */
   public static void register() throws GeneralSecurityException {
     Registry.registerKeyManager(new AesCtrHmacStreamingKeyManager());
-    Registry.registerKeyManager(new AesGcmHkdfStreamingKeyManager());
+    Registry.registerKeyManager(new AesGcmHkdfStreamingKeyManager(), /* newKeyAllowed = */ true);
     Registry.registerPrimitiveWrapper(new StreamingAeadWrapper());
   }
 }
diff --git a/java/src/main/java/com/google/crypto/tink/streamingaead/StreamingAeadKeyTemplates.java b/java/src/main/java/com/google/crypto/tink/streamingaead/StreamingAeadKeyTemplates.java
index 2fd14cc2d328d790ddc01beb96032885aae8c32b..388c4b624d8173dc3ea55c3014600b2e7fdc4399 100644
--- a/java/src/main/java/com/google/crypto/tink/streamingaead/StreamingAeadKeyTemplates.java
+++ b/java/src/main/java/com/google/crypto/tink/streamingaead/StreamingAeadKeyTemplates.java
@@ -153,7 +153,7 @@ public final class StreamingAeadKeyTemplates {
             .build();
     return KeyTemplate.newBuilder()
         .setValue(format.toByteString())
-        .setTypeUrl(AesGcmHkdfStreamingKeyManager.TYPE_URL)
+        .setTypeUrl(new AesGcmHkdfStreamingKeyManager().getKeyType())
         .setOutputPrefixType(OutputPrefixType.RAW)
         .build();
   }
diff --git a/java/src/test/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManagerTest.java b/java/src/test/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManagerTest.java
index 915ff979c2285e0fcce0dea7bd8ed549acf66b70..cccb83c67e7b75720bd93f4ed19e4b169dad3efe 100644
--- a/java/src/test/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManagerTest.java
+++ b/java/src/test/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManagerTest.java
@@ -19,6 +19,8 @@ package com.google.crypto.tink.streamingaead;
 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.StreamingAead;
 import com.google.crypto.tink.StreamingTestUtil;
 import com.google.crypto.tink.TestUtil;
@@ -42,7 +44,7 @@ import org.junit.runners.JUnit4;
 public class AesGcmHkdfStreamingKeyManagerTest {
   private static final int AES_KEY_SIZE = 16;
   private AesGcmHkdfStreamingParams keyParams;
-  private AesGcmHkdfStreamingKeyManager keyManager;
+  private KeyManager<StreamingAead> keyManager;
 
   @Before
   public void setUp() throws GeneralSecurityException {
@@ -52,7 +54,7 @@ public class AesGcmHkdfStreamingKeyManagerTest {
             .setDerivedKeySize(AES_KEY_SIZE)
             .setHkdfHashType(HashType.SHA256)
             .build();
-    keyManager = new AesGcmHkdfStreamingKeyManager();
+    keyManager = new KeyManagerImpl<>(new AesGcmHkdfStreamingKeyManager(), StreamingAead.class);
   }
 
   @Test
diff --git a/java/src/test/java/com/google/crypto/tink/streamingaead/StreamingAeadKeyTemplatesTest.java b/java/src/test/java/com/google/crypto/tink/streamingaead/StreamingAeadKeyTemplatesTest.java
index 08344ee090350b659d9662ebf6a553f7fe2153db..0d727efac2ee0a2be13f01cbe862d4cda647b777 100644
--- a/java/src/test/java/com/google/crypto/tink/streamingaead/StreamingAeadKeyTemplatesTest.java
+++ b/java/src/test/java/com/google/crypto/tink/streamingaead/StreamingAeadKeyTemplatesTest.java
@@ -65,7 +65,7 @@ public class StreamingAeadKeyTemplatesTest {
   @Test
   public void testAES128_GCM_HKDF_4KB() throws Exception {
     KeyTemplate template = StreamingAeadKeyTemplates.AES128_GCM_HKDF_4KB;
-    assertEquals(AesGcmHkdfStreamingKeyManager.TYPE_URL, template.getTypeUrl());
+    assertEquals(new AesGcmHkdfStreamingKeyManager().getKeyType(), template.getTypeUrl());
     assertEquals(OutputPrefixType.RAW, template.getOutputPrefixType());
     AesGcmHkdfStreamingKeyFormat format = AesGcmHkdfStreamingKeyFormat.parseFrom(
         template.getValue());
@@ -79,7 +79,7 @@ public class StreamingAeadKeyTemplatesTest {
   @Test
   public void testAES256_GCM_HKDF_4KB() throws Exception {
     KeyTemplate template = StreamingAeadKeyTemplates.AES256_GCM_HKDF_4KB;
-    assertEquals(AesGcmHkdfStreamingKeyManager.TYPE_URL, template.getTypeUrl());
+    assertEquals(new AesGcmHkdfStreamingKeyManager().getKeyType(), template.getTypeUrl());
     assertEquals(OutputPrefixType.RAW, template.getOutputPrefixType());
     AesGcmHkdfStreamingKeyFormat format = AesGcmHkdfStreamingKeyFormat.parseFrom(
         template.getValue());
@@ -126,7 +126,7 @@ public class StreamingAeadKeyTemplatesTest {
     HashType hkdfHashType = HashType.SHA512;
     KeyTemplate template = StreamingAeadKeyTemplates.createAesGcmHkdfStreamingKeyTemplate(
         mainKeySize, hkdfHashType, derivedKeySize, ciphertextSegmentSize);
-    assertEquals(AesGcmHkdfStreamingKeyManager.TYPE_URL, template.getTypeUrl());
+    assertEquals(new AesGcmHkdfStreamingKeyManager().getKeyType(), template.getTypeUrl());
     assertEquals(OutputPrefixType.RAW, template.getOutputPrefixType());
     AesGcmHkdfStreamingKeyFormat format = AesGcmHkdfStreamingKeyFormat.parseFrom(
         template.getValue());