PAForUserEnc.java revision 12489:339e2b4a5241
1/*
2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.security.krb5.internal;
27
28import java.io.ByteArrayOutputStream;
29import java.io.IOException;
30import sun.security.krb5.*;
31import sun.security.krb5.internal.crypto.KeyUsage;
32import sun.security.krb5.internal.util.KerberosString;
33import sun.security.util.DerOutputStream;
34import sun.security.util.DerValue;
35
36/**
37 * Implements the ASN.1 PA-FOR-USER type.
38 *
39 * <pre>{@code
40 * padata-type  ::= PA-FOR-USER
41 *                  -- value 129
42 * padata-value ::= EncryptedData
43 *                  -- PA-FOR-USER-ENC
44 * PA-FOR-USER-ENC ::= SEQUENCE {
45 *     userName[0] PrincipalName,
46 *     userRealm[1] Realm,
47 *     cksum[2] Checksum,
48 *     auth-package[3] KerberosString
49 * }
50 * }</pre>
51 *
52 * <p>
53 * This definition reflects MS-SFU.
54 */
55
56public class PAForUserEnc {
57    final public PrincipalName name;
58    final private EncryptionKey key;
59    final public static String AUTH_PACKAGE = "Kerberos";
60
61    public PAForUserEnc(PrincipalName name, EncryptionKey key) {
62        this.name = name;
63        this.key = key;
64    }
65
66    /**
67     * Constructs a PA-FOR-USER object from a DER encoding.
68     * @param encoding the input object
69     * @param key the key to verify the checksum inside encoding
70     * @throws KrbException if the verification fails.
71     * Note: this method is now only used by test KDC, therefore
72     * the verification is ignored (at the moment).
73     */
74    public PAForUserEnc(DerValue encoding, EncryptionKey key)
75            throws Asn1Exception, KrbException, IOException {
76        DerValue der = null;
77        this.key = key;
78
79        if (encoding.getTag() != DerValue.tag_Sequence) {
80            throw new Asn1Exception(Krb5.ASN1_BAD_ID);
81        }
82
83        // Realm after name? Quite abnormal.
84        PrincipalName tmpName = null;
85        der = encoding.getData().getDerValue();
86        if ((der.getTag() & 0x1F) == 0x00) {
87            try {
88                tmpName = new PrincipalName(der.getData().getDerValue(),
89                    new Realm("PLACEHOLDER"));
90            } catch (RealmException re) {
91                // Impossible
92            }
93        } else {
94            throw new Asn1Exception(Krb5.ASN1_BAD_ID);
95        }
96
97        der = encoding.getData().getDerValue();
98        if ((der.getTag() & 0x1F) == 0x01) {
99            try {
100                Realm realm = new Realm(der.getData().getDerValue());
101                name = new PrincipalName(
102                        tmpName.getNameType(), tmpName.getNameStrings(), realm);
103            } catch (RealmException re) {
104                throw new IOException(re);
105            }
106        } else {
107            throw new Asn1Exception(Krb5.ASN1_BAD_ID);
108        }
109
110        der = encoding.getData().getDerValue();
111        if ((der.getTag() & 0x1F) == 0x02) {
112            // Deal with the checksum
113        } else {
114            throw new Asn1Exception(Krb5.ASN1_BAD_ID);
115        }
116
117        der = encoding.getData().getDerValue();
118        if ((der.getTag() & 0x1F) == 0x03) {
119            String authPackage = new KerberosString(der.getData().getDerValue()).toString();
120            if (!authPackage.equalsIgnoreCase(AUTH_PACKAGE)) {
121                throw new IOException("Incorrect auth-package");
122            }
123        } else {
124            throw new Asn1Exception(Krb5.ASN1_BAD_ID);
125        }
126        if (encoding.getData().available() > 0)
127            throw new Asn1Exception(Krb5.ASN1_BAD_ID);
128    }
129
130    public byte[] asn1Encode() throws Asn1Exception, IOException {
131        DerOutputStream bytes = new DerOutputStream();
132        bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), name.asn1Encode());
133        bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), name.getRealm().asn1Encode());
134
135        try {
136            Checksum cks = new Checksum(
137                    Checksum.CKSUMTYPE_HMAC_MD5_ARCFOUR,
138                    getS4UByteArray(),
139                    key,
140                    KeyUsage.KU_PA_FOR_USER_ENC_CKSUM);
141            bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x02), cks.asn1Encode());
142        } catch (KrbException ke) {
143            throw new IOException(ke);
144        }
145
146        DerOutputStream temp = new DerOutputStream();
147        temp.putDerValue(new KerberosString(AUTH_PACKAGE).toDerValue());
148        bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x03), temp);
149
150        temp = new DerOutputStream();
151        temp.write(DerValue.tag_Sequence, bytes);
152        return temp.toByteArray();
153    }
154
155    /**
156     * Returns S4UByteArray, the block to calculate checksum inside a
157     * PA-FOR-USER-ENC data structure. It includes:
158     * 1. userName.name-type encoded as a 4-byte integer in little endian
159     *    byte order
160     * 2. all string values in the sequence of strings contained in the
161     *    userName.name-string field
162     * 3. the string value of the userRealm field
163     * 4. the string value of auth-package field
164     */
165    public byte[] getS4UByteArray() {
166        try {
167            ByteArrayOutputStream ba = new ByteArrayOutputStream();
168            ba.write(new byte[4]);
169            for (String s: name.getNameStrings()) {
170                ba.write(s.getBytes("UTF-8"));
171            }
172            ba.write(name.getRealm().toString().getBytes("UTF-8"));
173            ba.write(AUTH_PACKAGE.getBytes("UTF-8"));
174            byte[] output = ba.toByteArray();
175            int pnType = name.getNameType();
176            output[0] = (byte)(pnType & 0xff);
177            output[1] = (byte)((pnType>>8) & 0xff);
178            output[2] = (byte)((pnType>>16) & 0xff);
179            output[3] = (byte)((pnType>>24) & 0xff);
180            return output;
181        } catch (IOException ioe) {
182            // not possible
183            throw new AssertionError("Cannot write ByteArrayOutputStream", ioe);
184        }
185    }
186
187    public String toString() {
188        return "PA-FOR-USER: " + name;
189    }
190}
191